Advent of Code 2024 - Day 20: Race Condition

:: programming, python, puzzle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from advent import parse, grid_to_hash, nx

def gen_edges(grid):
    for pos, c in grid.items():
        if c in '.SE':
            if grid.get(pos + 1, None) in '.SE':
                yield (pos, pos + 1)
            if grid.get(pos + 1j, None)in '.SE':
                yield (pos, pos + 1j)

def solve():
    grid        = grid_to_hash(parse(20, list))
    start       = [ pos for pos, c in grid.items() if c == 'S' ][0]
    goal        = [ pos for pos, c in grid.items() if c == 'E' ][0]
    G           = nx.Graph(gen_edges(grid))
    path_d      = { pos : i for i, pos in enumerate(nx.shortest_path(G, start, goal)) }
    min_savings = 100
    part1       = 0
    part2       = 0

    for pos, i in path_d.items():
        for dx in range(-20, 20 + 1):
            for dy in range(-(20 - abs(dx)), (20 - abs(dx)) + 1):
                pos2 = pos + (dx + dy * 1j)
                if pos2 in path_d:
                    dist = abs(dx) + abs(dy)
                    if (path_d[pos2] - i) - dist >= min_savings:
                        part2 += 1
                        part1 += 1 if dist <= 2 else 0
    return part1, part2

# ---------------------------------------------------------------------------------------------

assert solve() == (1454, 997879) # 2.2 seconds

Solution

I spent a lot of time on this puzzle! I missed a very important aspect of the puzzle - that we know there is only one path from the start to the goal! Because of my error in reading comprehension, I was trying to solve a much harder, more general problem.

I finally gave in, and briefly read some comments in our discord, as well as reddit, and I quickly discovered that everyone was assuming this fact of having a single path i.e. a “race track”. That was the only hint I needed to get on the right track (pun intended).

Given that we have a single path from start to goal, all we have to do is compare pairs of nodes on the path to see if there is a more direct path between them within the time alotted (2 picoseconds for part one, and 20 picoseconds for part two).

I initially tried comparing various pairs of nodes from a starting index to through to the end, but that was slower than my final approach which was to simply look at nodes within the alotted distance from the path node, and see if they provide enough savings.

Both parts can be solved with a single loop. Running time is 2.2 seconds.

Conclusion

Properly reading and comprehending what the puzzle is asking for, and not over generalizing, is very important!