const fs = require('fs'); const input = fs.readFileSync(__dirname + '/input.txt', 'utf8'); const [initStr, gatesStr] = input.split("\n\n"); const originalValMap: { [key: string]: number }= {}; let valMap: { [key: string]: number } = {}; const gateMap: { [key: string]: [string, string, string] } = {}; let xStr = ''; let yStr = ''; const outputWires: Array = []; initStr.split("\n").forEach((s: string) => { const [wire, val] = s.split(': '); originalValMap[wire] = parseInt(val, 2); if (wire.startsWith('x')) { xStr += val; } else { yStr += val; } }); valMap = {...originalValMap}; const carries: Array = []; const halfAdds: Array = []; const badOuts: Array = []; const badAnds: Array = []; const gates: Array<[string, string, string, string]> = gatesStr.split("\n").map((s: string) => { const [inp, out] = s.split(' -> '); const [val1, op, val2] = inp.split(' '); return [val1, op, val2, out]; }); gates.forEach(([val1, op, val2, out]) => { if (out[0] === 'z') { outputWires.push(out); } gateMap[out] = [val1, op, val2]; }); function getWireVal(wire: string, trace: Array = []): number { if (Object.hasOwn(valMap, wire)) { return valMap[wire]; } const [wire1, op, wire2] = gateMap[wire]; if (trace.includes(wire1) || trace.includes(wire2)) { throw Error('Cycle'); } const val = getVal(wire1, op, wire2, [...trace, wire1, wire2]); valMap[wire] = val; return val; } function getVal(wire1: string, op: string, wire2: string, trace: Array = []): number { const val1 = getWireVal(wire1, trace); const val2 = getWireVal(wire2, trace); return { AND: () => val1 & val2, OR: () => val1 | val2, XOR: () => val1 ^ val2, }[op]!(); } outputWires.sort(); const expected = (parseInt(xStr, 2) + parseInt(yStr, 2)).toString(2); function calculate() { let outputStr = ''; valMap = {...originalValMap}; outputWires.forEach((wire) => { outputStr += getWireVal(wire); }); return outputStr; } function swap(wire1: string, wire2: string): void { const wire1Gate = gateMap[wire1]; const wire2Gate = gateMap[wire2]; gateMap[wire1] = wire2Gate; gateMap[wire2] = wire1Gate; gates.forEach((gate) => { if (gate[3] === wire1) { gate[3] = wire2; } else if (gate[3] === wire2) { gate[3] = wire1; } }); } interface HalfAdder { x: string, y: string, out: string, carry: string, sumGate: [string, string, string, string], carryGate: [string, string, string, string], } interface FullAdder { x: string, y: string, carryIn: string, out: string, carry:string, halfSumGate: [string, string, string, string], fullSumGate: [string, string, string, string], sumCarryGate: [string, string, string, string], inputCarryGate: [string, string, string, string], carryGate: [string, string, string, string], } const swaps: Array = []; function getGate(a: string, b: string, operator: string): [string, string, string, string] { const gate = gates.find(([val1, op, val2]) => { return op === operator && (a === val1 || b === val2 || a === val2 || b === val1); }); if (!gate) { throw new Error(`Two bad inputs, could not find gate, ${a}, ${b}`); } if ((gate[0] === a && gate[2] === b) || (gate[0] === b && gate[2] === a)) { return gate; } console.log(gate, a, b, operator); let wrongInput: string; let shouldBe: string; if (gate[0] === a) { wrongInput = b; shouldBe = gate[2] } else if (gate[0] === b) { wrongInput = a; shouldBe = gate[2] } if (gate[2] === a) { wrongInput = b; shouldBe = gate[0] } else { wrongInput = a; shouldBe = gate[0] } swaps.push(wrongInput); swaps.push(shouldBe); swap(wrongInput, shouldBe); console.log(`Wrong input found, ${wrongInput} should be ${shouldBe}`) return gate; } const carryGate = getGate('x00', 'y00', 'AND'); const sumGate = getGate('x00', 'y00', 'XOR'); const halfAdder: HalfAdder = { x: 'x00', y: 'y00', out: 'z00', carry: carryGate[3], carryGate: carryGate, sumGate: sumGate, } console.log(halfAdder); function getAdder(x: string, y: string, carryIn: string, out: string): FullAdder { const halfSumGate = getGate(x, y, 'XOR'); const fullSumGate = getGate(halfSumGate[3], carryIn, 'XOR'); if (fullSumGate[3] !== out) { console.log(`Wrong input found, ${fullSumGate[3]} should be ${out}`) swaps.push(fullSumGate[3]); swaps.push(out); swap(fullSumGate[3], out); } const sumCarryGate = getGate(halfSumGate[3], carryIn, 'AND'); const inputCarryGate = getGate(x, y, 'AND')!; const carryGate = getGate(sumCarryGate[3], inputCarryGate[3], 'OR'); return { x, y, carryIn, out: fullSumGate[3], carry: carryGate[3], halfSumGate, fullSumGate, sumCarryGate, inputCarryGate, carryGate }; } let carry = halfAdder.carry; for (let out of outputWires) { if (out === 'z00' || out === 'z45') { continue; } const x = out.replace('z', 'x'); const y = out.replace('z', 'y'); const adder = getAdder(x, y, carry, out); console.log(adder, swaps); carry = adder.carry; } swaps.sort(); console.log(swaps.join(','));