--- Day 24: Crossed Wires ---
You and The Historians arrive at the edge of a large grove somewhere in the jungle. After the last incident, the Elves installed a small device that monitors the fruit. While The Historians search the grove, one of them asks if you can take a look at the monitoring device; apparently, it's been malfunctioning recently.
The device seems to be trying to produce a number through some boolean logic gates. Each gate has two inputs and one output. The gates all operate on values that are either true (
1
) or false (0
).
AND
gates output1
if both inputs are1
; if either input is0
, these gates output0
.OR
gates output1
if one or both inputs is1
; if both inputs are0
, these gates output0
.XOR
gates output1
if the inputs are different; if the inputs are the same, these gates output0
.Gates wait until both inputs are received before producing output; wires can carry
0
,1
or no value at all. There are no loops; once a gate has determined its output, the output will not change until the whole system is reset. Each wire is connected to at most one gate output, but can be connected to many gate inputs.Rather than risk getting shocked while tinkering with the live system, you write down all of the gate connections and initial wire values (your puzzle input) so you can consider them in relative safety. For example:
x00: 1 x01: 1 x02: 1 y00: 0 y01: 1 y02: 0 x00 AND y00 -> z00 x01 XOR y01 -> z01 x02 OR y02 -> z02
Because gates wait for input, some wires need to start with a value (as inputs to the entire system). The first section specifies these values. For example,
x00: 1
means that the wire namedx00
starts with the value1
(as if a gate is already outputting that value onto that wire).The second section lists all of the gates and the wires connected to them. For example,
x00 AND y00 -> z00
describes an instance of anAND
gate which has wiresx00
andy00
connected to its inputs and which will write its output to wirez00
.In this example, simulating these gates eventually causes
0
to appear on wirez00
,0
to appear on wirez01
, and1
to appear on wirez02
.Ultimately, the system is trying to produce a number by combining the bits on all wires starting with
z
.z00
is the least significant bit, thenz01
, thenz02
, and so on.In this example, the three output bits form the binary number
100
which is equal to the decimal number4
.Here's a larger example:
x00: 1 x01: 0 x02: 1 x03: 1 x04: 0 y00: 1 y01: 1 y02: 1 y03: 1 y04: 1 ntg XOR fgs -> mjb y02 OR x01 -> tnw kwq OR kpj -> z05 x00 OR x03 -> fst tgd XOR rvg -> z01 vdt OR tnw -> bfw bfw AND frj -> z10 ffh OR nrd -> bqk y00 AND y03 -> djm y03 OR y00 -> psh bqk OR frj -> z08 tnw OR fst -> frj gnj AND tgd -> z11 bfw XOR mjb -> z00 x03 OR x00 -> vdt gnj AND wpb -> z02 x04 AND y00 -> kjc djm OR pbm -> qhw nrd AND vdt -> hwm kjc AND fst -> rvg y04 OR y02 -> fgs y01 AND x02 -> pbm ntg OR kjc -> kwq psh XOR fgs -> tgd qhw XOR tgd -> z09 pbm OR djm -> kpj x03 XOR y03 -> ffh x00 XOR y04 -> ntg bfw OR bqk -> z06 nrd XOR fgs -> wpb frj XOR qhw -> z04 bqk OR frj -> z07 y03 OR x01 -> nrd hwm AND bqk -> z03 tgd XOR rvg -> z12 tnw OR pbm -> gnj
After waiting for values on all wires starting with
z
, the wires in this system have the following values:bfw: 1 bqk: 1 djm: 1 ffh: 0 fgs: 1 frj: 1 fst: 1 gnj: 1 hwm: 1 kjc: 0 kpj: 1 kwq: 0 mjb: 1 nrd: 1 ntg: 0 pbm: 1 psh: 1 qhw: 1 rvg: 0 tgd: 0 tnw: 1 vdt: 1 wpb: 0 z00: 0 z01: 0 z02: 0 z03: 1 z04: 0 z05: 1 z06: 1 z07: 1 z08: 1 z09: 1 z10: 1 z11: 0 z12: 0
Combining the bits from all wires starting with
z
produces the binary number0011111101000
. Converting this number to decimal produces2024
.Simulate the system of gates and wires. What decimal number does it output on the wires starting with
z
?
First of all, we need to read the initial bits, and the connections.
show (b;c): "."vs'".."vs"."sv read0 `24.txt ("x00: 1";"x01: 0";"x02: 1";"x03: 1";"x04: 0";"y00: 1";"y01: 1";"y02: 1";"y03.. ("ntg XOR fgs -> mjb";"y02 OR x01 -> tnw";"kwq OR kpj -> z05";"x00 OR x03 -> ..
We cast the name to a symbol, and the value to a boolean.
show b:(!) . "SB"$flip ": "vs/:b x00| 1 x01| 0 x02| 1 x03| 1 x04| 0 y00| 1 y01| 1 y02| 1 y03| 1 y04| 1
For the connections, we do something similar.
show c:(!) . flip {(last x; x 1 0 2)} each `$" "vs'c mjb| XOR ntg fgs tnw| OR y02 x01 z05| OR kwq kpj fst| OR x00 x03 z01| XOR tgd rvg bfw| OR vdt tnw z10| AND bfw frj bqk| OR ffh nrd djm| AND y00 y03 psh| OR y03 y00 z08| OR bqk frj frj| OR tnw fst z11| AND gnj tgd z00| XOR bfw mjb vdt| OR x03 x00 z02| AND gnj wpb kjc| AND x04 y00 qhw| OR djm pbm hwm| AND nrd vdt rvg| AND kjc fst fgs| OR y04 y02 pbm| AND y01 x02
Now, we want to evaluate all of the z values. We can find them like so:
show zs: asc {x where "z"=first each string x} key c
We first define 3 functions for the different operations.
AND:&; OR:|; XOR:<>
Now, to evaluate a value, we first check if it is in the b dictionary. If so, return its value. Otherwise, we get its definition from the connections, and recursively evaluate.
Applying this to each of the z values gives the following:
{$[x in key b; b x; [(f;X;Y):c x; f[.z.s X; .z.s Y]]]} each zs 0001011111100b
This is in reverse order, so we reverse it, and then base 2 decode.
2 sv reverse {$[x in key b; b x; [(f;X;Y):c x; f[.z.s X; .z.s Y]]]} each zs 0001011111100b
--- Part Two ---
After inspecting the monitoring device more closely, you determine that the system you're simulating is trying to add two binary numbers.
Specifically, it is treating the bits on wires starting with
x
as one binary number, treating the bits on wires starting withy
as a second binary number, and then attempting to add those two numbers together. The output of this operation is produced as a binary number on the wires starting withz
. (In all three cases, wire00
is the least significant bit, then01
, then02
, and so on.)The initial values for the wires in your puzzle input represent just one instance of a pair of numbers that sum to the wrong value. Ultimately, any two binary numbers provided as input should be handled correctly. That is, for any combination of bits on wires starting with
x
and wires starting withy
, the sum of the two numbers those bits represent should be produced as a binary number on the wires starting withz
.For example, if you have an addition system with four
x
wires, foury
wires, and fivez
wires, you should be able to supply any four-bit number on thex
wires, any four-bit number on they
numbers, and eventually find the sum of those two numbers as a five-bit number on thez
wires. One of the many ways you could provide numbers to such a system would be to pass11
on thex
wires (1011
in binary) and13
on they
wires (1101
in binary):x00: 1 x01: 1 x02: 0 x03: 1 y00: 1 y01: 0 y02: 1 y03: 1
If the system were working correctly, then after all gates are finished processing, you should find
24
(11+13
) on thez
wires as the five-bit binary number11000
:z00: 0 z01: 0 z02: 0 z03: 1 z04: 1
Unfortunately, your actual system needs to add numbers with many more bits and therefore has many more wires.
Based on forensic analysis of scuff marks and scratches on the device, you can tell that there are exactly four pairs of gates whose output wires have been swapped. (A gate can only be in at most one such pair; no gate's output was swapped multiple times.)
For example, the system below is supposed to find the bitwise
AND
of the six-bit number onx00
throughx05
and the six-bit number ony00
throughy05
and then write the result as a six-bit number onz00
throughz05
:x00: 0 x01: 1 x02: 0 x03: 1 x04: 0 x05: 1 y00: 0 y01: 0 y02: 1 y03: 1 y04: 0 y05: 1 x00 AND y00 -> z05 x01 AND y01 -> z02 x02 AND y02 -> z01 x03 AND y03 -> z03 x04 AND y04 -> z04 x05 AND y05 -> z00
However, in this example, two pairs of gates have had their output wires swapped, causing the system to produce wrong answers. The first pair of gates with swapped outputs is
x00 AND y00 -> z05
andx05 AND y05 -> z00
; the second pair of gates isx01 AND y01 -> z02
andx02 AND y02 -> z01
. Correcting these two swaps results in this system that works as intended for any set of initial values on wires that start withx
ory
:x00 AND y00 -> z00 x01 AND y01 -> z01 x02 AND y02 -> z02 x03 AND y03 -> z03 x04 AND y04 -> z04 x05 AND y05 -> z05
In this example, two pairs of gates have outputs that are involved in a swap. By sorting their output wires' names and joining them with commas, the list of wires involved in swaps is
z00,z01,z02,z05
.Of course, your actual system is much more complex than this, and the gates that need their outputs swapped could be anywhere, not just attached to a wire starting with
z
. If you were to determine that you need to swap output wiresaaa
witheee
,ooo
withz99
,bbb
withccc
, andaoc
withz24
, your answer would beaaa,aoc,bbb,ccc,eee,ooo,z24,z99
.Your system of gates and wires has four pairs of gates which need their output wires swapped - eight wires in total. Determine which four pairs of gates need their outputs swapped so that your system correctly performs addition; what do you get if you sort the names of the eight wires involved in a swap and then join those names with commas?
Note: this section works best if you paste a real input. I've preloaded mine for convenience.
There are many ways to approach this, but possibly the easiest is "manually", which I'll do here.
Either by looking at the input, or guessing, we can assume the connections should be of the form of a ripple carry adder.
This allows us to "predict" the structure, and find mismatches. First of all, we want to sort each connection definition, to make searching easier. We also define a function that searches for a given configuration.
c: asc each c; S:{c?x,asc y,z}.
This allows us to look for the output of a specific pattern. For example, we know the initial "carry" should be the AND of x00 and y00.
show carry: S `AND`x00`y00 `skt
Now, we can write a function that takes the x and y values, as well as the relevant carry, and works out the relevant outputs for this "step".
The function will output all of the symbols it finds, as well as returning the carry.
ADD:{xor:S(`XOR;x;y); nd:S(`AND;x;y); out:S(`XOR;xor;z); car:S(`OR;nd;S(`AND;xor;z)); last 0N!(xor;nd;out;car)}
Now, we find the x values and y values.
show (xs;ys): "xy" {y where x=first each string y}\: key b x00 x01 x02 x03 x04 x05 x06 x07 x08 x09 x10 x11 x12 x13 x14 x15 x16 x17 x18 x.. y00 y01 y02 y03 y04 y05 y06 y07 y08 y09 y10 y11 y12 y13 y14 y15 y16 y17 y18 y..
We "run" our adder like so:
run:{carry{(ADD . y)x}/1_flip (xs;ys)}; run[] `kjn`hth`z01`pgc `fvj`nss`z02`wdm `psv`nbc`z03`rkm `dvq`ptd`z04`rdj `ffn`bkk`z05`wvr `jgw`bpp`fkp`z06 `pkh`fts`` `hfd`bpb`` `sfp`prs`` `dtb`qnf`` `jpp`wmq`` `ctw`ptj`` `vdk`tdh`` `qvt`bph`` `dmh`mjb`` `kmb`swn`` `rtv`bvg`` `mkt`qsw`` `csh`wpt`` `tpw`qtq`` `ngm`wnm`` `rqt`mhd`` `tqf`bwk`` `gfg`vjh`` `pmf`mdt`` `tns`jvf`` `rhc`mhv`` `rnh`wpw`` `cnd`gdn`` `qgd`bbk`` `mgq`z31`` `nmr`vdm`` `bbd`vtd`` `dhd`kjj`` `mhn`tnc`` `mvv`rpc`` `dgg`kvd`` `krj`bpt`` `nht`wqc`` `mkc`qgm`` `nhs`vmr`` `vfb`krw`` `dkw`nhd`` `gcd`gvk`` `
This allows us to see where things are going wrong.
We define a function that swaps two outputs.
SWAP:{c[reverse x]:c x}
This allows us to swap the incorrect values. After this, run the adder again to see where it breaks next.
SWAP `fkp`z06; run[] `kjn`hth`z01`pgc `fvj`nss`z02`wdm `psv`nbc`z03`rkm `dvq`ptd`z04`rdj `ffn`bkk`z05`wvr `jgw`bpp`z06`fkp `pkh`fts`z07`mpp `hfd`bpb`z08`sbq `sfp`prs`z09`rvw `dtb`qnf`z10`stv `jpp`wmq`ngr` `ctw`ptj`` `vdk`tdh`` `qvt`bph`` `dmh`mjb`` `kmb`swn`` `rtv`bvg`` `mkt`qsw`` `csh`wpt`` `tpw`qtq`` `ngm`wnm`` `rqt`mhd`` `tqf`bwk`` `gfg`vjh`` `pmf`mdt`` `tns`jvf`` `rhc`mhv`` `rnh`wpw`` `cnd`gdn`` `qgd`bbk`` `mgq`z31`` `nmr`vdm`` `bbd`vtd`` `dhd`kjj`` `mhn`tnc`` `mvv`rpc`` `dgg`kvd`` `krj`bpt`` `nht`wqc`` `mkc`qgm`` `nhs`vmr`` `vfb`krw`` `dkw`nhd`` `gcd`gvk`` `
SWAP `ngr`z11; run[] `kjn`hth`z01`pgc `fvj`nss`z02`wdm `psv`nbc`z03`rkm `dvq`ptd`z04`rdj `ffn`bkk`z05`wvr `jgw`bpp`z06`fkp `pkh`fts`z07`mpp `hfd`bpb`z08`sbq `sfp`prs`z09`rvw `dtb`qnf`z10`stv `jpp`wmq`z11`knj `ctw`ptj`z12`vjn `vdk`tdh`z13`qqv `qvt`bph`z14`vfj `dmh`mjb`z15`gfj `kmb`swn`z16`kfm `rtv`bvg`z17`qpf `mkt`qsw`z18`mst `csh`wpt`z19`bvc `tpw`qtq`z20`nwk `ngm`wnm`z21`snv `rqt`mhd`z22`gcn `tqf`bwk`z23`pqf `gfg`vjh`z24`sfh `pmf`mdt`z25`chq `tns`jvf`z26`jjj `rhc`mhv`z27`rcs `rnh`wpw`z28`jqv `cnd`gdn`z29`wrv `qgd`bbk`z30`tpf `mgq`z31`mfm` `nmr`vdm`` `bbd`vtd`` `dhd`kjj`` `mhn`tnc`` `mvv`rpc`` `dgg`kvd`` `krj`bpt`` `nht`wqc`` `mkc`qgm`` `nhs`vmr`` `vfb`krw`` `dkw`nhd`` `gcd`gvk`` `
SWAP `mfm`z31; run[] `kjn`hth`z01`pgc `fvj`nss`z02`wdm `psv`nbc`z03`rkm `dvq`ptd`z04`rdj `ffn`bkk`z05`wvr `jgw`bpp`z06`fkp `pkh`fts`z07`mpp `hfd`bpb`z08`sbq `sfp`prs`z09`rvw `dtb`qnf`z10`stv `jpp`wmq`z11`knj `ctw`ptj`z12`vjn `vdk`tdh`z13`qqv `qvt`bph`z14`vfj `dmh`mjb`z15`gfj `kmb`swn`z16`kfm `rtv`bvg`z17`qpf `mkt`qsw`z18`mst `csh`wpt`z19`bvc `tpw`qtq`z20`nwk `ngm`wnm`z21`snv `rqt`mhd`z22`gcn `tqf`bwk`z23`pqf `gfg`vjh`z24`sfh `pmf`mdt`z25`chq `tns`jvf`z26`jjj `rhc`mhv`z27`rcs `rnh`wpw`z28`jqv `cnd`gdn`z29`wrv `qgd`bbk`z30`tpf `mgq`mfm`z31`brs `nmr`vdm`z32`bdc `bbd`vtd`z33`mvb `dhd`kjj`z34`mdg `mhn`tnc`z35`sgr `mvv`rpc`z36`hsn `dgg`kvd`z37`ntr `krj`bpt`` `nht`wqc`` `mkc`qgm`` `nhs`vmr`` `vfb`krw`` `dkw`nhd`` `gcd`gvk`` `
SWAP `krj`bpt; run[] `kjn`hth`z01`pgc `fvj`nss`z02`wdm `psv`nbc`z03`rkm `dvq`ptd`z04`rdj `ffn`bkk`z05`wvr `jgw`bpp`z06`fkp `pkh`fts`z07`mpp `hfd`bpb`z08`sbq `sfp`prs`z09`rvw `dtb`qnf`z10`stv `jpp`wmq`z11`knj `ctw`ptj`z12`vjn `vdk`tdh`z13`qqv `qvt`bph`z14`vfj `dmh`mjb`z15`gfj `kmb`swn`z16`kfm `rtv`bvg`z17`qpf `mkt`qsw`z18`mst `csh`wpt`z19`bvc `tpw`qtq`z20`nwk `ngm`wnm`z21`snv `rqt`mhd`z22`gcn `tqf`bwk`z23`pqf `gfg`vjh`z24`sfh `pmf`mdt`z25`chq `tns`jvf`z26`jjj `rhc`mhv`z27`rcs `rnh`wpw`z28`jqv `cnd`gdn`z29`wrv `qgd`bbk`z30`tpf `mgq`mfm`z31`brs `nmr`vdm`z32`bdc `bbd`vtd`z33`mvb `dhd`kjj`z34`mdg `mhn`tnc`z35`sgr `mvv`rpc`z36`hsn `dgg`kvd`z37`ntr `bpt`krj`z38`hsp `nht`wqc`z39`rgb `mkc`qgm`z40`cqq `nhs`vmr`z41`kpq `vfb`krw`z42`krg `dkw`nhd`z43`mdn `gcd`gvk`z44`z45 `z45
The result is then the ascending symbols that were swapped.
","sv string asc `fkp`z06`ngr`z11`mfm`z31`krj`bpt "bpt,fkp,krj,mfm,ngr,z06,z11,z31"
(b;c): "."vs'".."vs"."sv read0 `:i/24.txt
b:(!) . "SB"$flip ": "vs/:b
c:(!) . flip {(last x; x 1 0 2)} each `$" "vs'c
zs: asc {x where "z"=first each string x} key c
AND:&; OR:|; XOR:<>
2 sv reverse {$[x in key b; b x; [(f;X;Y):c x; f[.z.s X; .z.s Y]]]} each zs
/ part 2:
c: asc each c; S:{c?x,asc y,z}.
carry:S `AND`x00`y00
ADD:{xor:S(`XOR;x;y); nd:S(`AND;x;y); out:S(`XOR;xor;z); car:S(`OR;nd;S(`AND;xor;z)); last 0N!(xor;nd;out;car)}
(xs;ys): "xy" {y where x=first each string y}\: key b
run:{carry{(ADD . y)x}/1_flip (xs;ys)}
SWAP:{c[reverse x]:c x}
/ find values to swap using run[]
SWAP `fkp`z06
SWAP `ngr`z11
SWAP `mfm`z31
SWAP `krj`bpt
","sv string asc `fkp`z06`ngr`z11`mfm`z31`krj`bpt