2024-12-24 10:13:53 +00:00
|
|
|
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<string> = [];
|
|
|
|
|
|
|
|
|
|
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};
|
|
|
|
|
|
2024-12-25 13:55:42 +00:00
|
|
|
const carries: Array<string> = [];
|
|
|
|
|
const halfAdds: Array<string> = [];
|
|
|
|
|
const badOuts: Array<string> = [];
|
|
|
|
|
const badAnds: Array<string> = [];
|
|
|
|
|
|
|
|
|
|
const gates: Array<[string, string, string, string]> = gatesStr.split("\n").map((s: string) => {
|
2024-12-24 10:13:53 +00:00
|
|
|
const [inp, out] = s.split(' -> ');
|
|
|
|
|
const [val1, op, val2] = inp.split(' ');
|
2024-12-25 13:55:42 +00:00
|
|
|
return [val1, op, val2, out];
|
|
|
|
|
});
|
|
|
|
|
gates.forEach(([val1, op, val2, out]) => {
|
2024-12-24 10:13:53 +00:00
|
|
|
gateMap[out] = [val1, op, val2];
|
|
|
|
|
if (out.startsWith('z')) {
|
|
|
|
|
outputWires.push(out);
|
|
|
|
|
}
|
2024-12-25 13:55:42 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-24 10:13:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function getWireVal(wire: string, trace: Array<string> = []): 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<string> = []): 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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-25 13:55:42 +00:00
|
|
|
function getPermutations(arr: Array<string>): Array<Array<string>> {
|
|
|
|
|
if (arr.length === 1) {
|
|
|
|
|
return [arr];
|
2024-12-24 10:13:53 +00:00
|
|
|
}
|
2024-12-25 13:55:42 +00:00
|
|
|
return arr.flatMap((s, i) => {
|
|
|
|
|
return getPermutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map((p) => [s, ...p]);
|
|
|
|
|
});
|
2024-12-24 10:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-25 13:55:42 +00:00
|
|
|
function swap(wire1: string, wire2: string): void {
|
|
|
|
|
const wire1Gate = gateMap[wire1];
|
|
|
|
|
const wire2Gate = gateMap[wire2];
|
|
|
|
|
gateMap[wire1] = wire2Gate;
|
|
|
|
|
gateMap[wire2] = wire1Gate;
|
|
|
|
|
}
|
2024-12-24 10:13:53 +00:00
|
|
|
|
|
|
|
|
const expected = (parseInt(xStr, 2) + parseInt(yStr, 2)).toString(2);
|
|
|
|
|
|
2024-12-25 13:55:42 +00:00
|
|
|
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]);
|
|
|
|
|
}
|
2024-12-24 10:13:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-25 13:55:42 +00:00
|
|
|
|
|
|
|
|
swap(perm[0], otherOutputsToSwap[0]);
|
|
|
|
|
swap(perm[1], otherOutputsToSwap[1]);
|
|
|
|
|
swap(perm[2], otherOutputsToSwap[2]);
|
2024-12-24 10:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-25 13:55:42 +00:00
|
|
|
/*
|
|
|
|
|
* 1234
|
|
|
|
|
* 4123
|
|
|
|
|
* 3412
|
|
|
|
|
* 2341
|
|
|
|
|
* 4321
|
|
|
|
|
* 1432
|
|
|
|
|
* 2143
|
|
|
|
|
* 3214
|
|
|
|
|
* 1324
|
|
|
|
|
* 4132
|
|
|
|
|
*/
|