From a2f386277f009ba18b8736748786213ea619bc8b Mon Sep 17 00:00:00 2001 From: Pascal Syma Date: Tue, 17 Dec 2024 18:42:35 +0100 Subject: [PATCH] Solve 2024-17 --- 2024/14/index.ts | 5 +- 2024/17/2024-17.run.xml | 7 +++ 2024/17/index.test.ts | 21 +++++++ 2024/17/index.ts | 126 ++++++++++++++++++++++++++++++++++++++++ 2024/17/run.ts | 12 ++++ utils/numbers.ts | 7 +++ 6 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 2024/17/2024-17.run.xml create mode 100644 2024/17/index.test.ts create mode 100644 2024/17/index.ts create mode 100644 2024/17/run.ts create mode 100644 utils/numbers.ts diff --git a/2024/14/index.ts b/2024/14/index.ts index 85769c8..9b24e8b 100644 --- a/2024/14/index.ts +++ b/2024/14/index.ts @@ -7,10 +7,7 @@ import { type CoordinateSides, isCoordinateEqual } from '../../utils/coordinates.ts'; - -function mod(x: number, m: number) { - return ((x % m) + m) % m; -} +import { mod } from '../../utils/numbers.ts'; export function solveFirst( input: string, diff --git a/2024/17/2024-17.run.xml b/2024/17/2024-17.run.xml new file mode 100644 index 0000000..278119f --- /dev/null +++ b/2024/17/2024-17.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2024/17/index.test.ts b/2024/17/index.test.ts new file mode 100644 index 0000000..72389b6 --- /dev/null +++ b/2024/17/index.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2024-17', () => { + it('first', () => { + const testInput = `Register A: 729 +Register B: 0 +Register C: 0 + +Program: 0,1,5,4,3,0`; + expect(solveFirst(testInput)).toBe('4,6,3,5,6,3,5,2,1,0'); + }); + it('second', () => { + const testInput = `Register A: 2024 +Register B: 0 +Register C: 0 + +Program: 0,3,5,4,3,0`; + expect(solveSecond(testInput)).toBe(117440); + }); +}); diff --git a/2024/17/index.ts b/2024/17/index.ts new file mode 100644 index 0000000..58e27c7 --- /dev/null +++ b/2024/17/index.ts @@ -0,0 +1,126 @@ +import { unsignedXOR } from '../../utils/numbers.ts'; + +const Instructions = { + adv: 0, + bxl: 1, + bst: 2, + jnz: 3, + bxc: 4, + out: 5, + bdv: 6, + cdv: 7 +} as const; + +type Instruction = (typeof Instructions)[keyof typeof Instructions]; + +function runCode( + instructions: Instruction[], + regA: number, + regB: number, + regC: number +) { + let instructionPointer = 0; + const output: number[] = []; + + while (instructionPointer < instructions.length) { + let instruction = instructions[instructionPointer]; + let operand = instructions[instructionPointer + 1]; + let comboOperand: number = operand; + if (operand === 4) { + comboOperand = regA; + } else if (operand === 5) { + comboOperand = regB; + } else if (operand === 6) { + comboOperand = regC; + } + + let jumped = false; + // console.log(regA, regB, regC, instruction, operand); + switch (instruction) { + case Instructions.adv: + regA = Math.floor(regA / Math.pow(2, comboOperand)); + break; + case Instructions.bdv: + regB = Math.floor(regA / Math.pow(2, comboOperand)); + break; + case Instructions.cdv: + regC = Math.floor(regA / Math.pow(2, comboOperand)); + break; + case Instructions.bxl: + regB = unsignedXOR(regB, operand); + break; + case Instructions.bst: + regB = comboOperand % 8; + break; + case Instructions.jnz: + if (regA !== 0) { + instructionPointer = operand; + jumped = true; + } + break; + case Instructions.bxc: + regB = unsignedXOR(regB, regC); + break; + case Instructions.out: + output.push(comboOperand % 8); + break; + } + if (!jumped) { + instructionPointer += 2; + } + } + + return output.join(','); +} + +export function solveFirst(input: string): string { + const match = input.match( + /^Register A: (\d*)\nRegister B: (\d*)\nRegister C: (\d*)\n\nProgram: ([\d,]*)$/m + ); + if (!match) { + return ''; + } + + const instructions = match[4].split(',').map(Number) as Instruction[]; + return runCode( + instructions, + Number(match[1]), + Number(match[2]), + Number(match[3]) + ); +} + +export function solveSecond(input: string): number { + const match = input.match( + /^Register A: (\d*)\nRegister B: (\d*)\nRegister C: (\d*)\n\nProgram: ([\d,]*)$/m + ); + if (!match) { + return 0; + } + + const instructions = match[4].split(',').map(Number) as Instruction[]; + + const regB = Number(match[2]); + const regC = Number(match[3]); + + const possible: { result: string; length: number }[] = []; + possible.push({ result: '', length: 0 }); + while (possible.length) { + const { result, length } = possible.shift()!; + if (length === instructions.length) { + return parseInt(result, 2); + } + const from = parseInt(`${result}000`, 2); + const to = parseInt(`${result}111`, 2); + + const expect = instructions.slice(-(length + 1)).join(','); + for (let regA = from; regA <= to; regA++) { + const r = runCode(instructions, regA, regB, regC); + if (r === expect) { + possible.push({ result: regA.toString(2), length: length + 1 }); + } + } + } + + return 0; +} diff --git a/2024/17/run.ts b/2024/17/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2024/17/run.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { solveFirst, solveSecond } from './index.ts'; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +const firstAnswer = solveFirst(input); + +console.log(firstAnswer); + +const secondAnswer = solveSecond(input); + +console.log(secondAnswer); diff --git a/utils/numbers.ts b/utils/numbers.ts new file mode 100644 index 0000000..cca940a --- /dev/null +++ b/utils/numbers.ts @@ -0,0 +1,7 @@ +export function mod(x: number, m: number) { + return ((x % m) + m) % m; +} + +export function unsignedXOR(a: number, b: number) { + return (a ^ b) >>> 0; +}