/** * @typedef {object} Sequence * @property {string[]} inputs * @property {string} gate * @property {string} output */ /** * @param {Sequence[]} sequence * @returns {Sequence[]} */ const sort = (sequence) => { const sequenceObj = {}; for (const next of sequence) { sequenceObj[next.output] = next; } const seen = new Set(); const stack = []; /** @param {string} wire */ const topologicalSort = (wire) => { if (!seen.has(wire)) { seen.add(wire); const r1 = sequenceObj[wire].inputs[0]; const r2 = sequenceObj[wire].inputs[1]; if (r1 in sequenceObj) topologicalSort(r1); if (r2 in sequenceObj) topologicalSort(r2); stack.push(wire); } } for (const wire in sequenceObj) { topologicalSort(wire); } return stack.filter(wire => wire in sequenceObj).map(wire => sequenceObj[wire]); } /** * @param {string} input * @returns {System} */ const parseInput = (input) => { const [initialValues, sequenceLines] = input.split(/\r?\n\r?\n/g).map(v => v.split(/\r?\n/g).filter(v => v.length > 0)); const system = new System(); initialValues.forEach((v) => { const [register, value] = v.split(': '); system.registers[register] = parseInt(value); }); system.sequence = sort(sequenceLines.map(v => { const [r1, gate, r2, _, r3] = v.split(' '); return { inputs: [r1, r2], gate, output: r3, }; })); return system; } class System { /** @type {Record} */ registers = {}; /** @type {Sequence[]} */ sequence = []; static gates = { AND(i1, i2) { return i1 & i2; }, OR(i1, i2) { return i1 | i2; }, XOR(i1, i2) { return i1 ^ i2; } }; /** * @param {Record} registers * @param {string} designation * @returns {string} */ static readRegisterBinary(registers, designation) { return Object.keys(registers) .filter(k => k.startsWith(designation)) .sort((a, b) => parseInt(a.slice(1)) - parseInt(b.slice(1))) .reduce((a, k) => a = `${registers[k]}${a}`, '') } /** * Simulate the logic running and return the output. * @returns {number} */ simulate() { const sequence = this.sequence.map(r => ({ ...r })); const registers = { ...this.registers }; for (const next of sequence) { const { inputs, gate, output } = next; registers[output] = System.gates[gate](registers[inputs[0]], registers[inputs[1]]); } return parseInt(System.readRegisterBinary(registers, 'z'), 2); } } const input = require('fs').readFileSync(0, 'utf-8'); const system = parseInput(input); console.log('Part One:', system.simulate()); const swapRand = () => { const rSwaps = []; while (true) { if (rSwaps.length === 8) break; const rNext = Math.floor(Math.random() * (system.sequence.length)) if (!rSwaps.includes(rNext)) { rSwaps.push(rNext); } } const swapGroups = [rSwaps.slice(0, 2), rSwaps.slice(2, 4), rSwaps.slice(4, 6), rSwaps.slice(6, 8)]; const sequenceBackup = system.sequence.map(r => ({ ...r })); const swaps = []; for (const [r1, r2] of swapGroups) { const v1 = system.sequence[r1].output; const v2 = system.sequence[r2].output; system.sequence[r1].output = v2; system.sequence[r2].output = v1; swaps.push(v1, v2); } system.sequence = sort(system.sequence); const out = system.simulate(); system.sequence = sequenceBackup; return { out, swaps: [...swaps].sort(), orig: swaps }; } const x = System.readRegisterBinary(system.registers, 'x'); const y = System.readRegisterBinary(system.registers, 'y'); const expected = parseInt(x, 2) + parseInt(y, 2); const logger = new class { ops = 0; lOps = 0; lOpsTs = new Date().getTime(); incrOps() { const curTs = new Date().getTime(); if (curTs - this.lOpsTs >= 1000) { this.lOps = this.ops; this.ops = 0; this.lOpsTs = curTs; } this.ops += 1; } last = 0; write(lastOut, force) { const curTs = new Date().getTime(); if (force === true || curTs - this.last > 250) { process.stdout.clearLine(1); process.stdout.cursorTo(0); const parts = []; parts.push(`ops/s=${this.lOps}`); parts.push(`last_out=${lastOut}`); process.stdout.write(parts.join(' ')); this.last = curTs; } } } console.log(); while (true) { logger.incrOps() const { out, swaps, orig } = swapRand(); if (out === expected) { process.stdout.clearLine(1); process.stdout.cursorTo(0); process.stdout.write(`Match: ${swaps.join(',')} [${orig.join(',')}]\n`); process.stdout.write('\n'); } logger.write(swaps.join(',')); }