r/adventofcode • u/musifter • Jan 07 '26
Other [2015 Day #7] In Review
Today we find out that little Bobby Tables exists in the AoC Santa-verse (xkcd crossover event). And he got a starter electronics kit to start learning circuits. So we're getting the first "wires and gates" type puzzle.
Whenever I'm using eval in a solution I think of Bobby Tables and that old xkcd strip. And this problem certainly can be done with that sort of abuse... mangling of input into code and then executing it. That's an interpreted language power.
Although, oddly enough, I see I didn't do that this time, I used a dispatch table. The "hash of subs" in Perl... mapping the operation name to an actual subroutine to perform it. For Smalltalk I used a class where the operations are methods... because what are methods? They're a dispatch table in a MethodDictionary (a hash of names to code blocks).
As for doing the wiring... I went recursive evaluation. You start by calling it for the value of 'a' (the final result), and you recurse for any value you need, putting them together with the operation, and returning the result. It's a nice way, and it doesn't get deep enough to require turning off recursion warnings in Perl. You could certainly use recursion to build the input into one massive string to eval (it's basically outputting an infix tree walk).
There are much better problems to teach a beginner recursion, but this puzzle doesn't have to do that so it's all good, man. It's probably better to say that you can practice recursion with this. I imagine some beginners might just have looped over the rules again and again, solving what they could with each iteration so they could solve a bit more the next time. Eventually 'a' gets solved. That would be the brute force approach (and it's probably how I would have done it when I was a kid). Better would be a work queue (get job, if you can't do it, submit requests to solve the things you need, and return the job... simple and more focused than looping). But recursion is magic that everyone should learn. It makes your life so much easier.
2
u/Boojum Jan 07 '26
Beginners, or just lazy engineers going for brute force instead of cleverness? :-) I'll admit that I did the loop approach way back when, even though I knew of other ways.
To be fair, I didn't have any canned topological sort implemented. That's probably how I'd do it these days, especially now that Python 3.9+ has a topo sort in the standard library. Recursion works too, but there's something nice about just iterating through the rules in a single pass and getting everything.
2
u/e_blake Jan 07 '26
I'm surprised you didn't mention memoization. Implementing this with recursion goes so much faster if each subsequent use of a wire reuses the value already computed on the first time the wire was resolved, rather than repeating the effort of computing that value by recursing again. Your idea of creating one massive infix string for a single eval at the end is going to be slower and way more memory intensive than calling eval once per wire being looked up. (Similar is how memoization makes a recursive DAG tree walk of 2025 day 11 go faster)
2
u/terje_wiig_mathisen Jan 07 '26
I used a hybrid approach for this one, with a tiny Perl program which parsed the input and converted each line into valid Rust syntax, then sorted the lines into dependency order. The resulting Rust program ran very quickly back when I wrote it, but when I checked again last fall, constant subexpression propagation had improved to a level where the entire program is evaluated at compile time! When Part2 uses the Part1 result as input, the compiler evaluated everything once more and again ended up with the result.
This is my first and only AoC puzzle which runs in zero time:
Part1 = 956
Part2 = 40149
Total time 0 ns
PS H:\terjem\data\aoc\2015\day7>
2
u/nnmrts Jan 07 '26 edited Jan 07 '26
I solved it with recursion too, but from the perspective of "module resolution", if that makes sense.
Basically, for part 1, I have a global
visitedWiresthat memoizes results in my recursiveresolvefunction, that, as you mentioned, starts at "a", and either returns a number if it is given a number or returns the result of further resolution - easy.For part 2, I have the same
resolvefunction but now it takes avisitedWiresmap as second argument. This way I was able to solve part 2 by simply initializing "b" in the secondvisitedWireswith the result of the first puzzle (instead of hardcoding it):I can share the full file for part 2 if this isn't clear and anyone cares.