Hung-Yi's LogoHung-Yi’s Journal

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:

1

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 😅