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]) => { gateMap[out] = [val1, op, val2]; if (out.startsWith('z')) { outputWires.push(out); } if ((op === 'XOR' && !val1.startsWith('x') && !val1.startsWith('y')) || (op === 'XOR' && (val1 === 'x00' || val1 === 'y00'))) { if (!out.startsWith('z')) { badOuts.push(out); } } else if (out.startsWith('z')) { badOuts.push(out); } if (op === 'AND') { for (let [val1, op, val2] of gates) { if (op !== 'OR' && (val1 === out || val2 === out)) { badAnds.push(out); break; } } } }); 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().reverse(); function calculate() { let outputStr = ''; valMap = {...originalValMap}; outputWires.forEach((wire) => { outputStr += getWireVal(wire); }); return outputStr; } function getPermutations(arr: Array): Array> { if (arr.length === 1) { return [arr]; } return arr.flatMap((s, i) => { return getPermutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map((p) => [s, ...p]); }); } function swap(wire1: string, wire2: string): void { const wire1Gate = gateMap[wire1]; const wire2Gate = gateMap[wire2]; gateMap[wire1] = wire2Gate; gateMap[wire2] = wire1Gate; } const expected = (parseInt(xStr, 2) + parseInt(yStr, 2)).toString(2); const zOutputsToSwap = badOuts.filter((o) => o[0] === 'z'); const otherOutputsToSwap = badOuts.filter((o) => o[0] !== 'z'); for (let perm of getPermutations(zOutputsToSwap)) { swap(perm[0], otherOutputsToSwap[0]); swap(perm[1], otherOutputsToSwap[1]); swap(perm[2], otherOutputsToSwap[2]); for (let i = 0; i < gates.length - 1; i++) { for (let j = i + 1; j < gates.length; j++) { swap(gates[i][3], gates[j][3]); try { const output = calculate(); if (output === expected) { const swaps = [...perm, ...otherOutputsToSwap, gates[i][3], gates[j][3]]; swaps.sort(); console.log(swaps); break; } } catch (e) { // } finally { swap(gates[i][3], gates[j][3]); } } } swap(perm[0], otherOutputsToSwap[0]); swap(perm[1], otherOutputsToSwap[1]); swap(perm[2], otherOutputsToSwap[2]); } /* * 1234 * 4123 * 3412 * 2341 * 4321 * 1432 * 2143 * 3214 * 1324 * 4132 */