--- 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).
ANDgates output1if both inputs are1; if either input is0, these gates output0.ORgates output1if one or both inputs is1; if both inputs are0, these gates output0.XORgates output1if 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,1or 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 -> z02Because 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: 1means that the wire namedx00starts 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 -> z00describes an instance of anANDgate which has wiresx00andy00connected to its inputs and which will write its output to wirez00.In this example, simulating these gates eventually causes
0to appear on wirez00,0to appear on wirez01, and1to appear on wirez02.Ultimately, the system is trying to produce a number by combining the bits on all wires starting with
z.z00is the least significant bit, thenz01, thenz02, and so on.In this example, the three output bits form the binary number
100which 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 -> gnjAfter 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: 0Combining the bits from all wires starting with
zproduces 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
xas one binary number, treating the bits on wires starting withyas 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, wire00is 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
xand 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
xwires, fourywires, and fivezwires, you should be able to supply any four-bit number on thexwires, any four-bit number on theynumbers, and eventually find the sum of those two numbers as a five-bit number on thezwires. One of the many ways you could provide numbers to such a system would be to pass11on thexwires (1011in binary) and13on theywires (1101in binary):x00: 1 x01: 1 x02: 0 x03: 1 y00: 1 y01: 0 y02: 1 y03: 1If the system were working correctly, then after all gates are finished processing, you should find
24(11+13) on thezwires as the five-bit binary number11000:z00: 0 z01: 0 z02: 0 z03: 1 z04: 1Unfortunately, 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
ANDof the six-bit number onx00throughx05and the six-bit number ony00throughy05and then write the result as a six-bit number onz00throughz05: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 -> z00However, 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 -> z05andx05 AND y05 -> z00; the second pair of gates isx01 AND y01 -> z02andx02 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 withxory:x00 AND y00 -> z00 x01 AND y01 -> z01 x02 AND y02 -> z02 x03 AND y03 -> z03 x04 AND y04 -> z04 x05 AND y05 -> z05In 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 wiresaaawitheee,ooowithz99,bbbwithccc, andaocwithz24, 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