|
| 1 | +const fs = require('fs') |
| 2 | +const path = require('path') |
| 3 | + |
| 4 | +let input = fs.readFileSync(path.join(__dirname, 'input.txt'), { encoding: 'utf8' }) |
| 5 | + |
| 6 | +const operand = { |
| 7 | + 0: (cpu) => 0, |
| 8 | + 1: (cpu) => 1, |
| 9 | + 2: (cpu) => 2, |
| 10 | + 3: (cpu) => 3, |
| 11 | + 4: (cpu) => cpu.A, |
| 12 | + 5: (cpu) => cpu.B, |
| 13 | + 6: (cpu) => cpu.C, |
| 14 | + 7: (cpu) => { throw ("Invalid operand 7") } |
| 15 | +} |
| 16 | + |
| 17 | +const opcodes = { |
| 18 | + 0: (cpu, op) => { cpu.A = Math.floor(cpu.A / Math.pow(2, operand[op](cpu))); cpu.cp += 2 }, |
| 19 | + 1: (cpu, op) => { cpu.B = cpu.B ^ op; cpu.cp += 2 }, |
| 20 | + 2: (cpu, op) => { cpu.B = operand[op](cpu) % 8; cpu.cp += 2 }, |
| 21 | + 3: (cpu, op) => { if (cpu.A != 0) cpu.cp = op; else cpu.cp += 2 }, |
| 22 | + 4: (cpu, op) => { cpu.B = Number(BigInt(cpu.B) ^ BigInt(cpu.C)); cpu.cp += 2 }, |
| 23 | + 5: (cpu, op) => { cpu.out.push(operand[op](cpu) % 8); cpu.cp += 2 }, |
| 24 | + 6: (cpu, op) => { cpu.B = Math.floor(cpu.A / Math.pow(2, operand[op](cpu))); cpu.cp += 2 }, |
| 25 | + 7: (cpu, op) => { cpu.C = Math.floor(cpu.A / Math.pow(2, operand[op](cpu))); cpu.cp += 2 }, |
| 26 | +} |
| 27 | + |
| 28 | +// 2,4 - bst: Set B to A % 8 (combo operand 4 means get value from register A) |
| 29 | +// 1,1 - bxl: XOR B with 1 |
| 30 | +// 7,5 - cdv: Set C to floor(A / 2^B) (combo operand 5 means get power from register B) |
| 31 | +// 0,3 - adv: Set A to floor(A / 2^3) (divides A by 8) |
| 32 | +// 1,4 - bxl: XOR B with 4 |
| 33 | +// 4,4 - bxc: XOR B with C (operand ignored) |
| 34 | +// 5,5 - out: Output B % 8 (combo operand 5 means get value from register B) |
| 35 | +// 3,0 - jnz: If A ≠ 0, jump to position 0 |
| 36 | + |
| 37 | +function runJsCode(initialA) { |
| 38 | + let out = [] |
| 39 | + |
| 40 | + let a = initialA |
| 41 | + do { |
| 42 | + let b = a % 8 |
| 43 | + b = b ^ 1 |
| 44 | + let c = Math.floor(a / (2 ** b)) |
| 45 | + b = b ^ 4 |
| 46 | + b = Number(BigInt(b) ^ BigInt(c)) |
| 47 | + out.push(b % 8) |
| 48 | + a = Math.floor(a / 8) |
| 49 | + } while (a > 0) |
| 50 | + |
| 51 | + return out |
| 52 | +} |
| 53 | + |
| 54 | +function run(cpu, code) { |
| 55 | + cpu.cp = 0; |
| 56 | + cpu.out = []; |
| 57 | + do { |
| 58 | + opcodes[code[cpu.cp]](cpu, code[cpu.cp + 1]) |
| 59 | + } |
| 60 | + while (cpu.cp < code.length) |
| 61 | + return cpu |
| 62 | +} |
| 63 | + |
| 64 | +let [cpuT, codeT] = input.split('\n\n') |
| 65 | + |
| 66 | +let cpu = cpuT.split('\n').map(l => { |
| 67 | + let reg = l.substring(9).split(': ') |
| 68 | + return [reg[0], parseInt(reg[1])] |
| 69 | +}) |
| 70 | + |
| 71 | +let code = codeT.substring(8).split(',').map(Number) |
| 72 | + |
| 73 | +let computer = { |
| 74 | + A: cpu[0][1], |
| 75 | + B: cpu[1][1], |
| 76 | + C: cpu[2][1], |
| 77 | + cp: 0, |
| 78 | + out: [] |
| 79 | +} |
| 80 | + |
| 81 | +let newCpu = run(computer, code) |
| 82 | +console.log(newCpu.out.join(',')) |
| 83 | + |
| 84 | +function check(sofar, expected, allOptions = []) { |
| 85 | + |
| 86 | + if (sofar.length == expected.length) { |
| 87 | + allOptions.push(sofar) |
| 88 | + return |
| 89 | + } |
| 90 | + |
| 91 | + let options = [] |
| 92 | + |
| 93 | + for (let d = 0; d < 8; d++) { |
| 94 | + let octal = sofar + d |
| 95 | + let out = runJsCode(parseInt(octal, 8)) |
| 96 | + if (out.toReversed().every((v, i) => expected[expected.length - 1 - i] == v)) { |
| 97 | + options.push(octal) |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + for (const option of options) { |
| 102 | + check(option, expected, allOptions) |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +let expected = [2, 4, 1, 1, 7, 5, 0, 3, 1, 4, 4, 4, 5, 5, 3, 0] |
| 107 | + |
| 108 | +let options = [] |
| 109 | +check('',expected, options) |
| 110 | +console.log( parseInt(options.toSorted()[0],8)) |
0 commit comments