From 70141f8762956666ff44f11bf4d84c82089003bc Mon Sep 17 00:00:00 2001 From: Tim Kurvers Date: Wed, 18 Dec 2024 12:12:37 +0100 Subject: [PATCH] feat(js): add solutions for 2024 day 17 --- js/src/2024/17/index.js | 124 ++++++++++++++++++++++++++++++++++ puzzles/2024/17/examples.yaml | 17 +++++ 2 files changed, 141 insertions(+) create mode 100644 js/src/2024/17/index.js create mode 100644 puzzles/2024/17/examples.yaml diff --git a/js/src/2024/17/index.js b/js/src/2024/17/index.js new file mode 100644 index 0000000..99a87df --- /dev/null +++ b/js/src/2024/17/index.js @@ -0,0 +1,124 @@ +import { mod, solution } from '../../utils/index.js'; + +const NUMBER_MATCHER = /\d+/g; + +const parse = (input) => { + const parts = input.trim().split('\n\n'); + const registers = Array.from(parts[0].matchAll(NUMBER_MATCHER)).map(BigInt); + const program = parts[1].slice(8).split(',').map(BigInt); + return { program, registers }; +}; + +const run = (program, registers) => { + const [a, b, c] = [0, 1, 2]; + + const resolve = (operand) => { + if (operand >= 0n && operand <= 3n) return operand; + if (operand === 7n) throw new Error('reserved'); + return registers[operand - 4n]; + }; + + const output = []; + + let ip = 0n; + + const operations = { + // adv + 0: (operand) => { + registers[a] = (registers[a] / 2n ** resolve(operand)) | 0n; + }, + + // bxl + 1: (operand) => { + registers[b] ^= operand; + }, + + // bst + 2: (operand) => { + registers[b] = mod(resolve(operand), 8n); + }, + + // jnz + 3: (operand) => { + if (registers[a] !== 0n) { + ip = operand; + } + }, + + // bxc + 4: (_operand) => { + registers[b] ^= registers[c]; + }, + + // out + 5: (operand) => { + output.push(mod(resolve(operand), 8n)); + }, + + // bdv + 6: (operand) => { + registers[b] = (registers[a] / 2n ** resolve(operand)) | 0n; + }, + + // cdv + 7: (operand) => { + registers[c] = (registers[a] / 2n ** resolve(operand)) | 0n; + }, + }; + + while (ip < program.length) { + const opcode = program[ip++]; + const operand = program[ip++]; + + const operation = operations[opcode]; + if (!operation) { + throw new Error(`no operation for opcode: ${opcode}`); + } + operation(operand); + } + + return output; +}; + +export const partOne = solution((input) => { + const { program, registers } = parse(input); + return run(program, registers).join(','); +}); + +export const partTwo = solution((input) => { + const { program, registers } = parse(input); + + // Samples of `a` with the resulting outputs: + // + // a = 7n => [ 0n ] + // a = 15n => [ 1n, 0n ] + // a = 16n => [ 2n, 0n ] + // a = 87n => [ 2n, 1n, 0n ] + // a = 88n => [ 3n, 1n, 0n ] + // a = 120n => [ 7n, 1n, 0n ] (a = 15n << 3) + // a = 960n => [ 0n, 7n, 1n, 0n ] (a = 15n << 6) + // a = 7680n => [ 0n, 0n, 7n, 1n, 0n ] (a = 15n << 9) + // + // As seen in the last three examples, a position's output is directly linked to its triple (three-bit) + // position in the `a` value, but in reverse: most significant bytes affect least significant output slots. + + let candidates = [0n]; + + // For each output slot in the program, attempt to find candidate `a` values, until eventually we have + // candidates that match the full program output + for (const [index, _] of program.entries()) { + const next = []; + for (const shifta of candidates) { + for (let a = 0n; a < 8n; ++a) { + const candidate = (shifta << 3n) + a; + const output = run(program, [candidate, ...registers.slice(1)]); + if (output.at(-index - 1) === program.at(-index - 1)) { + next.push(candidate); + } + } + } + candidates = next; + } + + return Math.min(...candidates.map(Number)); +}); diff --git a/puzzles/2024/17/examples.yaml b/puzzles/2024/17/examples.yaml new file mode 100644 index 0000000..4c63a32 --- /dev/null +++ b/puzzles/2024/17/examples.yaml @@ -0,0 +1,17 @@ +part-one: + - input: | + Register A: 729 + Register B: 0 + Register C: 0 + + Program: 0,1,5,4,3,0 + answer: 4,6,3,5,6,3,5,2,1,0 + +part-two: + - input: | + Register A: 2024 + Register B: 0 + Register C: 0 + + Program: 0,3,5,4,3,0 + answer: 117440