## DEV Community

Nicky Meuleman

Posted on • Originally published at nickymeuleman.netlify.app

# Advent of Code 2022 Day 10

## Day 10: Cathode-Ray Tube

TL;DR: my solution in Rust

After falling from the bridge yesterday, you want to use the communication device from a few days ago to get in touch with the elves.

The screen broke, but the CPU still works.

The screen and CPU are both driven by a clock circuit, each tick of the clock is called a cycle.

The CPU has a single register (a place that stores a number) named `X`, which starts at 1.

It supports 2 instructions:

1. `addx N` takes 2 cycles to complete. After two cycles, the number `N` gets added to the `X` register.
2. `noop` takes 1 cycle to complete. It does nothing.

Today's input is a list of instructions for the CPU.

An example input looks like this:

``````noop
``````
1. The first cycle:
• start: `noop` begins executing. `X` is 1 (the starting value).
• end: `noop` finishes executing, doing nothing (`X` is 1)
2. The second cycle:
• start: `addx 3` begins executing. `X` is 1
• end: nothing finishes executing, doing nothing (`X` is 1)
3. The third cycle:
• start: `addx 3` continues executing. `X` is still 1
• end: `addx 3` finishes executing, adding 3 to `X` (`X` is 4)
4. The fourth cycle:
• start: `addx -5` begins executing. `X` is 4
• end: nothing finishes executing, doing nothing (`X` is 4)
5. The fifth cycle:
• start: `addx -5` continues executing. `X` is still 4
• end: `addx -5` finishes executing, subtracting 5 from `X` (`X` is -1)

## Part 1

The signal strength is the cycle number multiplied by the value in `X` during a cycle. (not after!)

The question asks to find the sum of the signal strength during the 20th, 60th, 100th, 140th, 180th, and 220th cycles.

That's the 20th cycle and every 40 cycles after that.

That means we have to add the signal strength during a cycle to a total whenever `cycle % 40 == 20` (up to 220).

• `X` starts at 1
• `cycle` starts at 1 (I know, an index starting at 1? The humanity!)
• the `total` starts at 0
• whenever `cycle % 40 == 20`, add `cycle * x` to `total`

Every instruction completes before an other one starts.

So for `noop`, the `cycle` counter gets incremented once.
For `addx`, the cycle counter gets incremented twice.

In pseudocode, that would be:

``````let mut x = 1;
let mut cycle = 1;
let mut total = 0;

for instruction in all_instructions {
// check signal strength
// increment cycle

// check signal strength
// add to x and increment cycle
}
``````

This maps directly to the solution for part1!

### Final code

``````pub fn part_1() -> i32 {

let mut x = 1;
let mut cycle = 1;
let mut total = 0;

for line in input.lines() {
if cycle % 40 == 20 {
total += cycle * x;
}
cycle += 1;

if let Some(("addx", num)) = line.split_once(' ') {
if cycle % 40 == 20 {
total += cycle * x;
}
let num: i32 = num.parse().unwrap();
x += num;
cycle += 1;
}
}

total
}
``````

## Part 2

Part 2 is about figuring out what the broken screen would have displayed given the instructions in your input.

The `X` register controls the horizontal middle of a 3 block wide sprite.

There is no concept of a vertical position.

• The screen and the CPU are tied to the same clock cycles.
• The screen draws a pixel every cycle. Its position incrementing by one every cycle.
• If any of the 3 blocks of the sprite is at the position the screen is currently drawing, a lit pixel is drawn, else a dim one.

The screen is 40 pixels wide and 6 high.

Some constants that are mentioned in the question:

``````const COLS: usize = 40;
const ROWS: usize = 6;
const SPRITE_WIDTH: u32 = 3;
``````

It draws left to right, top to bottom.
Every time it begins drawing a new row, it starts at the left edge again.

The `cycle` starts at 1, and the index on the `screen` starts at 0!

We can write a small helper function to determine if a pixel should be lit or dim.

The function takes in the current value of `x`, and the current `cycle`.

If the 3 wide sprite is at the location the screen is currently drawing, the pixel is lit.

``````fn get_pixel(cycle: usize, x: i32) -> char {
let curr_col = (cycle - 1) % COLS;
if (curr_col as i32).abs_diff(x) <= SPRITE_WIDTH / 2 {
'█'
} else {
' '
}
}
``````

We represent the screen as a big array of length `ROWS * COLS`.
Each item in that array is a pixel.

Our initial setup for the variables we keep track of in part2:

``````let mut x = 1;
let mut cycle = 1;
let mut screen = [' '; COLS * ROWS];
``````

Instead of checking the signal strength during each cycle, we draw a pixel to the screen.

At the end, we have a filled `screen` array.

As a fun trick, you can end part 2 here, print that array as a string and resize your terminal until you can read it (so until it's 40 characters wide).
Line wrapping will do the trick.

A more universal solution involves splitting the array into chunks of `COLS` long,
converted each chunk to a string,
and joining those strings with a newline.

``````let image = screen
.chunks(COLS)
.map(|row| row.iter().collect())
.collect::<Vec<String>>()
.join("\n");
``````

Put everything together and presto, Advent of Code 2022 day10 part2 is complete!

### Final code

``````const COLS: usize = 40;
const ROWS: usize = 6;
const SPRITE_WIDTH: u32 = 3;

fn get_pixel(cycle: usize, x: i32) -> char {
let curr_col = (cycle - 1) % COLS;
if (curr_col as i32).abs_diff(x) <= SPRITE_WIDTH / 2 {
'█'
} else {
' '
}
}

pub fn part_2() -> String {
let mut x = 1;
let mut cycle = 1;
let mut screen = [' '; COLS * ROWS];

for line in input.lines() {
screen[cycle - 1] = get_pixel(cycle, x);
cycle += 1;

if let Some(("addx", num)) = line.split_once(' ') {
screen[cycle - 1] = get_pixel(cycle, x);
cycle += 1;
let num: i32 = num.parse().unwrap();
x += num;
}
}

let image = screen
.chunks(COLS)
.map(|row| row.iter().collect())
.collect::<Vec<String>>()
.join('\n');

image
}
``````