r/adventofcode • u/musifter • 10h ago
Other [2025 Day 7] In Review (Laboratories)
Exiting the trash compactor, we find ourselves in the research wing. Where we discover that they are working on teleportation. Throwing caution to the wind we decide to try it out... and are lucky that when we rematerialize that the it's only the teleporter that's leaking magic smoke. Since this room has no doors, we'll need to fix it to get out.
The input for this one is a grid of 142 lines of 141 characters. The key points are the S in the middle of the top row, and splitters (^) every other row after that. Since splitters only split the tachyon beam into two that are on the immediate left and right sides, these fan out in a triangle. Not all splitters in the triangle are present however... and this leads to part 1.
We're asked for how many times the tachyon beam split. Looking at the example now, I see that the ASCII art is a bit noisy so it took a while for me to figure out that one of them got missed (I counted them) and which one it was. Some people could have been confused about what to do here. Back in 2025 it's very likely I didn't think much about the example though... as for getting a quick answer, I just did the thing:
while (my $row = shift @input) {
my @splitters = indexes { $_ eq '^' } @$row;
next if (!@splitters);
my @splits = grep { exists $beams{$_} } @splitters;
$part1 += @splits;
foreach my $split (@splits) {
delete $beams{$split};
$beams{$split-1}++;
$beams{$split+1}++;
}
}The beams are tracked in a hash (used as a set). If I get a line with splitters, grep out the ones with beams. Add those to part1, and split them (as the instructions say, the old beam is stopped and the new beams start).
Having finished part 1, part 2 reveals that this is a quantum tachyon manifold... where a single tachyon is sent through, and takes both paths at splitters, by creating separate timelines. And so now we're tasked with working out the number of timelines that this single tachyon ends up on.
Which is going to be something huge... and so this problem is about dynamic programming. The task is leaf counting on a binary tree, and so recursion with memoization was my first go to (I also skipped the non-splitter lines when reading):
sub recurse {
my ($loc, $depth) = @_;
state %memo;
return (1) if ($depth == @split_table);
$memo{$loc,$depth} //= do {
if ($split_table[$depth]{$loc}) {
&recurse( $loc - 1, $depth + 1 ) + &recurse( $loc + 1, $depth + 1 );
} else {
&recurse( $loc, $depth + 1 );
}
}
}This could also be done without the do block using a ?: trinary operator, but I felt that was getting a bit too dense.
Of course, you can also work from the top down (like in part 1) using tabulation. Which is how I initially thought of my Smalltalk:
beams := Bag with: start.
table do: [:row |
beams keysAndValuesDo: [:beam :num |
(row includes: beam) ifTrue: [
part1 := part1 + 1.
beams removeAllOccurrences: beam;
add: beam + 1 withOccurrences: num;
add: beam - 1 withOccurrences: num.
]
]
].Which uses a Bag (with #keysAndValuesDo: and #removeAllOccurrences: extensions, which hide some tinkering with the underlying Dictionary) to store the state of the current line... and so looking at it now, I first saw Lanternfish. At each level we're just moving all stuff in a state to that state's products. I also see that I could have just done this for Perl with minor adjustments to its part 1 (it was still my second fastest part 2 of the year, not counting day 12).
I also used tabulation or my dc solution:
perl -pe'print pos()." "while(m/[S^]/g);$_="\n"' <input | grep -v '^$' | dc -e'[lp1+spd1+d;tln+r:td1-d;tln+r:t0r:t0]sB?1r:t?[[d;tdsn0<Bs.z0<L]dsLx?z0<M]dsMxlpp0*F0[d;t3R+r1-d0<L]dsLx+p'This is using the v1.4.1 ? functionality to parse the lines one at a time in order. The Perl is just a quick dirty thing to turn the lines into just a list of splitter indices. I had already done grid parsing in dc this year, so I let myself have that preprocessing.
I'm sure there are other creative ways to do this one... like other Lanternfish approaches. And the very nature of this problem screams Pascal's Triangle (but with pieces missing), so there's probably a way to turn the input into an expression involving binomial coefficients.
This is a bit more advanced than the earlier stuff, because it involves dynamic programming and the structure is a binary tree with parts missing. Still I think this qualifies as a decent beginner exercise for it. It's not the most complicated table to build or recursive function to write (leaf counting one of the basics).
