Origial Animation in CodePen.io

Running with Bunnies

Created: Oct 28, 2026

Last modified: Oct 28, 2026

* AI-generated from the code I used in Google Foobar

TL, DR.

Running with Bunnies
====================

You and your rescued bunny prisoners need to get out of this collapsing death trap of a space station - and fast! Unfortunately, some of the bunnies have been weakened by their long imprisonment and can't run very fast. Their friends are trying to help them, but this escape would go a lot faster if you also pitched in. The defensive bulkhead doors have begun to close, and if you don't make it through in time, you'll be trapped! You need to grab as many bunnies as you can and get through the bulkheads before they close.

The time it takes to move from your starting point to all of the bunnies and to the bulkhead will be given to you in a square matrix of integers. Each row will tell you the time it takes to get to the start, first bunny, second bunny, ..., last bunny, and the bulkhead in that order. The order of the rows follows the same pattern (start, each bunny, bulkhead). The bunnies can jump into your arms, so picking them up is instantaneous, and arriving at the bulkhead at the same time as it seals still allows for a successful, if dramatic, escape. (Don't worry, any bunnies you don't pick up will be able to escape with you since they no longer have to carry the ones you did pick up.) You can revisit different spots if you wish, and moving to the bulkhead doesn't mean you have to immediately leave - you can move to and from the bulkhead to pick up additional bunnies if time permits.

In addition to spending time traveling between bunnies, some paths interact with the space station's security checkpoints and add time back to the clock. Adding time to the clock will delay the closing of the bulkhead doors, and if the time goes back up to 0 or a positive number after the doors have already closed, it triggers the bulkhead to reopen. Therefore, it might be possible to walk in a circle and keep gaining time: that is, each time a path is traversed, the same amount of time is used or added.

Write a function of the form answer(times, time_limit) to calculate the most bunnies you can pick up and which bunnies they are, while still escaping through the bulkhead before the doors close for good. If there are multiple sets of bunnies of the same size, return the set of bunnies with the lowest prisoner IDs (as indexes) in sorted order. The bunnies are represented as a sorted list by prisoner ID, with the first bunny being 0. There are at most 5 bunnies, and time_limit is a non-negative integer that is at most 999.

For instance, in the case of
[
  [0, 2, 2, 2, -1],  # 0 = Start
  [9, 0, 2, 2, -1],  # 1 = Bunny 0
  [9, 3, 0, 2, -1],  # 2 = Bunny 1
  [9, 3, 2, 0, -1],  # 3 = Bunny 2
  [9, 3, 2, 2,  0],  # 4 = Bulkhead
]
and a time limit of 1, the five inner array rows designate the starting point, bunny 0, bunny 1, bunny 2, and the bulkhead door exit respectively. You could take the path:

Start End Delta Time Status
    -   0     -    1 Bulkhead initially open
    0   4    -1    2
    4   2     2    0
    2   4    -1    1
    4   3     2   -1 Bulkhead closes
    3   4    -1    0 Bulkhead reopens; you and the bunnies exit

With this solution, you would pick up bunnies 1 and 2. This is the best combination for this space station hallway, so the answer is [1, 2].

Languages
=========

To provide a Python solution, edit solution.py
To provide a Java solution, edit solution.java

Test cases
==========

Inputs:
    (int) times = [
                    [0, 1, 1, 1, 1],
                    [1, 0, 1, 1, 1],
                    [1, 1, 0, 1, 1],
                    [1, 1, 1, 0, 1],
                    [1, 1, 1, 1, 0]
                  ]
    (int) time_limit = 3
Output:
    (int list) [0, 1]

Inputs:
    (int) times = [
                    [0, 2, 2, 2, -1],
                    [9, 0, 2, 2, -1],
                    [9, 3, 0, 2, -1],
                    [9, 3, 2, 0, -1],
                    [9, 3, 2, 2, 0]
                  ]
    (int) time_limit = 1
Output:
    (int list) [1, 2]

The station is falling apart and the bulkhead is sliding shut. You've got a clock showing time_limit seconds, a matrix telling you how long it takes to get from every spot to every other spot, and a handful of bunnies scattered along the way. Reach the bulkhead before it seals, carrying as many bunnies as you can. So far that's an ordinary routing problem, except for one twist: some corridors run through security checkpoints that add time back to the clock.

Full disclosure up front: I didn't solve this one on my own. At level 4, the only one I cracked by myself was Bringing a Gun to a Trainer Fight; I got through this one with outside help. What follows explains how the solution I submitted works.

# The rules in a nutshell

times[a][b] is how long it takes to go from a to b. Row and column 0 are the start, then one spot per bunny, then the bulkhead. A negative number means that corridor gives you time back.

You can visit any spot as many times as you like, the bulkhead included. Reaching it doesn't mean you have to leave.

If the clock drops below 0 the bulkhead closes, but if it climbs back to 0 or more, it opens again. All that matters is being at the bulkhead with the clock at 0 or above.

There are at most 5 bunnies. If two groups of the same size both make it, the one with the lowest IDs wins.

# Take it for a spin

Before any algorithms, go for a run yourself. Click a spot to move there. The arrows leave from where you're standing and show what each corridor does to the clock, and the green ones give time back. Bunnies jump into your arms as you pass by, and the table below logs your route just like the one in the statement. On the second example, try escaping with bunnies 1 and 2 when you only start with 1 second. Then, on "A cycle that gains time", see what happens when you go back and forth between bunny 0 and the bulkhead.

Example
โฑ 1 ยท bulkhead open In your arms: —
−2−2−2+1Start SBunny 0 0๐ŸฐBunny 1 1๐ŸฐBunny 2 2๐ŸฐBulkhead 🚪๐Ÿƒ

You're at the start with 1 second. Click another spot to move.

From To Delta Time Status
- S - 1 Bulkhead initially open
# Two separate questions

If you played with the second example, you probably noticed that the direct corridor is rarely the best one. Going from the start to bunny 1 takes 2, but swinging by the bulkhead first takes โˆ’1 + 2 = 1. That's a hint to split the problem in two:

What's the fastest way from one spot to another? Any detour counts, including the corridors that give time back.

Which bunnies do I pick up, and in what order? With the fastest times in hand, that's just adding up start โ†’ bunnies โ†’ bulkhead.

# Bellman-Ford: shortest paths with negative numbers

The first question is a shortest-path problem, but the usual go-to, Dijkstra, doesn't work here. Dijkstra treats a spot as settled the moment it leaves the queue, and that only holds when no corridor takes time off. With negative corridors, a path that looked expensive can end up being the cheapest one.

Bellman-Ford is more patient. Everything starts at infinity except the origin, and then it goes over every corridor again and again, keeping any improvement it finds:

$$ \text{dist}[v] = \min\big(\text{dist}[v],\ \text{dist}[u] + \text{times}[u][v]\big) $$

After the first pass, every shortest path made of one corridor is right. After the second, every one made of two, and so on. A shortest path that doesn't repeat spots has at most V โˆ’ 1 corridors, so after V โˆ’ 1 passes nothing can improve anymore... unless there's a catch. My relax does V passes, one extra that doesn't hurt. Since I need the times between every pair of spots, I run it once from each origin and build a distance matrix. With 7 spots at most, that's 7 passes over 49 corridors per origin, which is nothing.

# The catch: a negative cycle

The catch is a loop that adds up to a negative number. If going from bunny 0 to the bulkhead takes โˆ’1 and coming back takes 0, every lap hands you a free second. There's no such thing as "the shortest path" anymore, because one more lap always makes it shorter. For this problem that's great news: you have all the time in the world, so you take every bunny.

How do you spot one? Without negative cycles, shortest distances can't be improved: going from the start to u and then from u to v never beats the best path from the start to v. That's what isNegativeCycle checks. If dist[0][u] + dist[u][v] < dist[0][v] holds for some pair, something can still go down, and that only happens with a negative cycle around. Every spot connects to every other one, so any cycle can be reached from the start.

The second question doesn't need anything clever. With 5 bunnies there are 31 possible groups, and even counting every order you could visit them in, that's 325 routes in total. So try them all, in the order the statement asks for: bigger groups first and, among groups of the same size, lower IDs first. The first group with some order that fits in time_limit is the answer.

Each route uses the Bellman-Ford matrix: start โ†’ first bunny โ†’ โ€ฆ โ†’ last bunny โ†’ bulkhead. The neat part is that every leg already includes any detour worth taking, like stopping by the bulkhead to grab a second, so you never have to think about them. And if a leg happens to pass another bunny's spot, that bunny jumps in too. That doesn't break anything, since the bigger group was already tried earlier.

# Both halves together

On top is the time matrix. Flip the switch to compare the statement's times with Bellman-Ford's: green cells are shortcuts, and clicking one shows the route it takes and how its corridors add up. On "A cycle that gains time", the diagonal turns red. That's the time to get back to where you started, and a negative one is exactly the negative cycle. Below the matrix is the search, with every group it tried and the best order to visit it.

Example
ToFromS012🚪
S02
1
1
-1
0
8
0
1
1
-1
1
8
2
0
1
-1
2
8
2
1
0-1
🚪93220

Legend: Faster than the direct corridor (click it to see the route) Back where you started with more time than you left with: a negative cycle

Bunnies Best order Time
[0, 1, 2] S → 0 → 1 → 2 → 🚪 3 > 1 ✗
[0, 1] S → 0 → 1 → 🚪 2 > 1 ✗
[0, 2] S → 0 → 2 → 🚪 2 > 1 ✗
[1, 2] S → 1 → 2 → 🚪 1 ≤ 1 ✓

Bellman-Ford found 11 shortcuts. The search tried 4 groups, and the first one that fits in 1 is [1, 2].

# The tie-break gotcha

The version I submitted didn't loop over groups. It went straight through the orders with itertools.permutations(bunniesIndex, k) and returned the first one that fit, sorted. It works on the statement's examples, but there's a hole in it. Permutations come out in lexicographic order of the sequence, not of the group. Look at the last example in the demo: group {0, 1} only fits if you grab bunny 1 before bunny 0, and the sequence (1, 0) comes after (0, 2), which fits too. That version answered [0, 2] when the right answer is [0, 1].

The fix is to keep the two things apart. itertools.combinations hands out the groups in the right order, and each group then tries all of its permutations. That way the first group that fits is guaranteed to have the lowest IDs.

# The code

Here's the code I submitted, with the tie-break fix applied, written for Python 2.7, which is what Foobar ran:

BellmanFord.relax works out the shortest times from one origin, and generateBellmanFordMatrix does that for every spot to build the dist matrix.

isNegativeCycle looks for anything that can still improve from the start. If it finds something, the answer is every bunny.

solution goes through the sizes from biggest to smallest, the groups with combinations and their orders with permutations. Bunnies start at index 1 in the matrix, which is why 1 gets subtracted at the end.

import itertools


class BellmanFord:
    def __init__(self, edges):
        self.edges = edges
        self.distances = [float('inf') for vertex in edges]

    def relax(self, src):
        self.distances = [float('inf') for vertex in self.edges]
        self.distances[src] = 0 #self.edges[src][src]
        for _ in range(len(self.edges)):
            for u, v, w in self.enumerateEdges(self.edges):
                if self.distances[u] != float('inf') and self.distances[u] + w < self.distances[v]:
                    self.distances[v] = self.distances[u] + w
        return self.distances

    def enumerateEdges(self, edges):
        for u, row in enumerate(edges):
            for v, weight in enumerate(row):
                yield u, v, weight

    def isNegativeCycle(self, bellmanFordMatrix):
        distance = bellmanFordMatrix[0]
        for u, v, w in self.enumerateEdges(bellmanFordMatrix):
            if distance[u] != float('inf') and distance[u] + w < distance[v]:
                return True
        return False


def generateBellmanFordMatrix(edges):
    distance = []
    bell = BellmanFord(edges)
    for vertex in range(len(edges)):
        dist = bell.relax(vertex)
        distance.append(dist)
    return distance


def solution(times, time_limit):
    bunnies = len(times) - 2 if len(times) - 2 >= 0 else 0
    bunniesIndex = [i + 1 for i in range(bunnies)]
    bellmanFord = BellmanFord(times)
    dist = generateBellmanFordMatrix(times)
    if bellmanFord.isNegativeCycle(dist):
        return list(range(bunnies))
    for numbunnies in range(bunnies, 0, -1):
        # sets in lexicographic order, so the first one that fits has the lowest IDs
        for combination in itertools.combinations(bunniesIndex, numbunnies):
            for permutation in itertools.permutations(combination):

                cost = 0
                for i in range(len(permutation) - 1):
                    cost += dist[permutation[i]][permutation[i+1]]
                # from start to first bunny
                cost += dist[0][permutation[0]]
                # from last bunny to end
                cost += dist[permutation[len(permutation) - 1]][len(dist) - 1]

                if cost <= time_limit:
                    return [i - 1 for i in combination]
    return []
# Wrapping up

This challenge mixes two ideas. When a graph has negative weights, Bellman-Ford is the tool for the job, and it also tells you when there's a negative cycle, which here means infinite time. And when the combinatorial part is tiny, like 5 bunnies, trying everything is perfectly fine. Just be careful about the order you try things in, because that's where the tie-break bites.

An unhandled error has occurred. Reload ๐Ÿ—™