Advent of Code 2022: Day 10
A TypeScript solution for the Advent of Code 2022, Day 10 puzzle: emulating CPU instructions and rendering to a virtual screen.
Today’s puzzle asks us to read a set of CPU instructions that draw a signal to an imaginary CRT screen.
The example input looks like this.
noop addx 3 addx -5
What’s challenging about today’s puzzle conditions is that the instructions vary in the duration of CPU cycles (noop
takes one cycle and addx
takes two cycles), and because of timing issues, we have to track the cycle count carefully and separately from the instruction count.
We start, as always, by splitting the puzzle input into a more manageable data structure: a list of instructions with a cmd
and an optional integer arg
.
const instructions = puzzleInput.split('\n') .map(line => line.split(' ')) .map(([cmd, arg]) => ({cmd, arg: parseInt(arg)}));
Because I was trying to compete for time1 today, I went with a fully imperative approach in a giant block of code, similar in strategy to many of the top competitors. Check out the comments for more details 😃
These variables track the state of the emulator, and accumulate values for our part 1 and part 2 answers.
let part1 = 0 // signal strength sum let part2 = '\n'; // CRT screen output let x = 1; // X register value let cycles = 0; // clock/cycles/ticks
And the emulation is basically a couple of nested loops: one to iterate through each instruction, and one to handle the individual cycles.
for (const { cmd, arg } of instructions) { // Switch on the cmd to figure out how long // the instruction will run let duration = cmd === 'addx' ? 2 : 1; // The inner loop simulates the instruction // for its given number of cycles, and handles the // cycle updating logic + answer accumulation while (duration > 0) { // Construct the sprite by drawing lit pixels // at the X position and 1 pixel either side const sprite = Array(40).fill(0) .map((_, i) => [x-1,x,x+1].includes(i) ? '▓' : '░') // Render the correct part of the sprite // depending on the previous cycle part2 += sprite[cycles % 40]; // Increment the cycle count cycles++; // In the middle of each line of 40 cycles, // calculate signal strength and accumulate if ((cycles - 20) % 40 === 0 && cycles <= 220) part1 += x * cycles; // After each line of 40 cycles, // switch to a new line on the CRT output if (cycles % 40 === 0) part2 += '\n'; duration--; } // Updating the X value always occurs at the // end of the duration of the 'addx' instruction if (cmd === 'addx' && !!arg) x += arg; }
Final Solution
const instructions = puzzleInput.split('\n') .map(line => line.split(' ')) .map(([cmd, arg]) => ({cmd, arg: parseInt(arg)})); let part1 = 0 // signal strength sum let part2 = '\n'; // CRT screen output let x = 1; // X register value let cycles = 0; // clock/cycles/ticks for (const { cmd, arg } of instructions) { // Switch on the cmd to figure out how long // the instruction will run let duration = cmd === 'addx' ? 2 : 1; // The inner loop simulates the instruction // for its given number of cycles, and handles the // cycle updating logic + answer accumulation while (duration > 0) { // Construct the sprite by drawing lit pixels // at the X position and 1 pixel either side const sprite = Array(40).fill(0) .map((_, i) => [x-1,x,x+1].includes(i) ? '▓' : '░') // Render the correct part of the sprite // depending on the previous cycle part2 += sprite[cycles % 40]; // Increment the cycle count cycles++; // In the middle of each line of 40 cycles, // calculate signal strength and accumulate if ((cycles - 20) % 40 === 0 && cycles <= 220) part1 += x * cycles; // After each line of 40 cycles, // switch to a new line on the CRT output if (cycles % 40 === 0) part2 += '\n'; duration--; } // Updating the X value always occurs at the // end of the duration of the 'addx' instruction if (cmd === 'addx' && !!arg) x += arg; } console.log("Part 1:", part1); console.log("Part 2:", part2);
Part 1: 14420 Part 2: ▓▓▓░░░▓▓░░▓░░░░▓▓▓░░▓▓▓░░▓▓▓▓░░▓▓░░▓░░▓░ ▓░░▓░▓░░▓░▓░░░░▓░░▓░▓░░▓░░░░▓░▓░░▓░▓░░▓░ ▓░░▓░▓░░░░▓░░░░▓░░▓░▓▓▓░░░░▓░░▓░░▓░▓░░▓░ ▓▓▓░░▓░▓▓░▓░░░░▓▓▓░░▓░░▓░░▓░░░▓▓▓▓░▓░░▓░ ▓░▓░░▓░░▓░▓░░░░▓░▓░░▓░░▓░▓░░░░▓░░▓░▓░░▓░ ▓░░▓░░▓▓▓░▓▓▓▓░▓░░▓░▓▓▓░░▓▓▓▓░▓░░▓░░▓▓░░
Footnotes:
I got rank 2939 for part 1 in 00:16:29, and rank 1856 for part 2 in 00:27:43. Nowhere near the leaderboard 😅