Skip to content

Advent of Code 2025 Day 11: Reactor

"""Advent of Code 2025: Day 11 - Reactor"""

from advent import nx, prod, cache

G = nx.read_adjlist(open("app/day11.txt", "rb"), create_using=nx.DiGraph)


@cache
def dfs(node, dst):
    return 1 if node == dst else sum(dfs(n, dst) for n in G.neighbors(node))


def part1():
    return dfs('you', 'out')


def part2():
    return sum(
        prod(dfs(src, dst) for src, dst in zip(seq, seq[1:]))
        for seq in (('svr', 'fft', 'dac', 'out'), ('svr', 'dac', 'fft', 'out'))
    )


assert part1() == 640
assert part2() == 367579641755680

Day 11

Solution

I'm really loving Python as my relatively new primary programming language!

Today's puzzle involves depth first search and graphs. A key element today is memoization, or caching function results. Without memoization, it's extremely slow!

I used the networkx library, and it can read graphs in adjacency list format, so getting the graph of our network is just:

G = nx.read_adjlist(open("app/day11.txt", "rb"), create_using=nx.DiGraph)

Our depth first search helper function is super simple, and Python makes the memoization trivial with a @cache decorator. We search from the node to the dst in depth first manner - if we've reached the dst, we return 1; otherwise, we sum the results of recursively searching each of the neighbors:

@cache
def dfs(node, dst):
    return 1 if node == dst else sum(dfs(n, dst) for n in G.neighbors(node))

With the preliminaries in place, part 1 just searches from you to out. For part 2, we search for 3 successive segments, and multiply each of their totals together. Our input only has dac following fft, but I also summed fft following dac (always zero in my case) for completeness, so it would work on any input:

def part2():
    return sum(
        prod(dfs(src, dst) for src, dst in zip(seq, seq[1:]))
        for seq in (('svr', 'fft', 'dac', 'out'), ('svr', 'dac', 'fft', 'out'))
    )

Alternate Solution

It's also possible to search from the source all the way to the destination in one shot. To do that for part 2 we need to keep track of whether we've visited dac and fft, because a successful path from svr to out must have visited both of them:

"""Advent of Code 2025: Day 11 - Reactor (alt version)"""

from advent import nx, cache

G = nx.read_adjlist(open("app/day11.txt", "rb"), create_using=nx.DiGraph)


@cache
def dfs(node, fft, dac):
    match node:
        case 'out':
            return 1 if fft and dac else 0
        case 'fft':
            fft = True
        case 'dac':
            dac = True

    return sum(dfs(n, fft, dac) for n in G.neighbors(node))


assert dfs('you', True, True) == 640
assert dfs('svr', False, False) == 367579641755680

End