Next place to search is the star fruit grove, where we find that they've installed a monitoring device since the incident the last time we were here. Unfortunately, it's malfunctioning and so we have to fix it. And so we get a logic wiring problem.
The input consists of two sections. The first is just a list of the 45 x and 45 y input lines with their values. The second section is the connections of the circuit which are rules involving two inputs (with three letter labels... no vowels, y is only allowed for the input yNN lines, but w is used this time), a logic gate (AND, OR, or XOR), and an output label.
Part 1 is to just simulate the circuit and get the output. And so I did the usual taunting of Bobby Tables, and turned the gates into code to eval:
foreach (@$gate_rules) {
my ($in1, $op, $in2, $out) = m#(\w+) ([A-Z]+) (\w+) -> (\w+)#;
my @in = sort( $in1, $in2 );
$gate{$in[0],$in[1],$out} = "\$wire{$out} = \$wire{$in1} $logic{$op} \$wire{$in2}";
}
Where %logic is just a lookup table from the words to the operator symbols.
Then I just ran the machine until everything was stable:
my $done = 0;
while (!$done) {
$done = 1;
foreach my $g (keys %gate) {
my ($in1, $in2, $out) = split( $;, $g );
if (defined( $wire{$in1} ) and defined( $wire{$in2} ) and !defined( $wire{$out} )) {
$done = 0;
my @in = sort( $in1, $in2 );
eval( $gate{$in[0],$in[1],$out} );
}
}
}
say "Part 1: ", oct("0b" . join('', map {$wire{$_}} reverse sort grep {$_ =~ m#^z#} keys %wire ));
Quick and dirty just to see what could possibly be up with part 2. Which reveals that this is supposed to be an adder (non-snake). We manage to deduce that only 4 pairs of output gate wires have been swapped (with no overlap). So we need to figure out what those are.
I started by writing a little exploration script, that bloated up, and ultimately I used to solve this problem by hand. One of the first things I added was a print_tree subroutine to do a recursive infix walk from a given node (ie all the zN nodes) and output it, with the output label in front and the gate expression in parens.
Then to simplify that output, I created a translation table (%trans) which allows for replacing node with a string. The two cases that I do automatically are the ones involving combining the input lines... xN AND yN becomes bothN and xN XOR yN becomes addN, because XOR is "add without carry". So the bulk of the circuit, as one might expect from an adder, is going to be handling those carries. And so the expression tree of an output zN node should look like:
zN
^
|
XOR (sumN)
/ \
/ \
addN OR (carryN)
/ \
/ \
bothN-1 AND (rolloverN-1)
/ \
/ \
(carryN-1) addN-1
That carryN subtree creates a mess, and so I started manually adding those to the translation table as I spotted them (look for a (carryN & addN) | bothN and call it carryN+1):
$trans{vms} = 'carry01';
$trans{kmq} = 'carry02';
$trans{vfb} = 'carry03';
$trans{ngk} = 'carry04';
The end goal being that (except for the start), all output zN should just be addN ^ carryN. When that's not there, there's a puzzle to solve. For example, after adding carry04 (for my input), I see this:
z03 (carry03 ^ add03)
z04 (carry04 ^ add04)
both05
z06 (trn (gnw (add05 & knc (hpm (carry04 & add04) | both04)) | gdd (knc (hpm (carry04 & add04) | both04) ^ add05)) ^ add06)
That missing z05 hash been replaced with both05, which should be in carry06. So I look at the z06 line and I immediately see that knc is carry05, but z05 wants that with a ^ add05, and that's gdd. And so one of the pairs in my input to swap is z05 and gdd. When I add that to the %soln table (which applies the mappings in it to the wires directly, and add the knc => carry05 relabel we found, things smooth out to this:
z04 (carry04 ^ add04)
z05 (carry05 ^ add05)
z06 (trn (gnw (add05 & carry05) | both05) ^ add06)
Which is good, as trn is clearly carry06 so that's the next line to add and z06 is good.
This continued until z37 where the last problem happened. The second and the fourth were the same problem (basically an & and ^ gate got swapped... a mixup of rollover and sum nodes in the diagram). The third one was a bit interesting because it was this:
z19 (carry19 ^ add19)
z20 (carry20 ^ both20)
z21 (add21 ^ jgb (cjn (carry20 & both20) | add20))
That z20 should have the add20 (from the next line) not both20. But I'd hidden the labels so I needed to grep out the original rules for x20/y20 from the input:
y20 AND x20 -> css
y20 XOR x20 -> jmv
And there's the labels I needed.
As I've said before, I do like doing puzzles in general, so although I spent a lot of work doing this by hand, I still enjoyed myself. I even had fun resolving these again now (although now I can just comment out translations I don't want so it's easier to look around). I've never actually gotten around to actually coding something to do this automatically.