How to solve Advent of Code 2022 - Day 10 with Python

How to solve Advent of Code 2022 - Day 10 with Python

If you missed any previous days, click here for all my content about that: Advent of Code, if you want to know why you should participate and try to code the solution for yourself, click here: Advent Of Code 2022 – 7 Reasons why you should participate. If you're here to see the solution of Day 10, continue reading ;)

GitHub Repository

https://github.com/GalaxyInfernoCodes/Advent_Of_Code_2022

I will upload all of my solutions there - in the form of Python (or Scala alternatives) notebooks. You have to use your own input for the "input.txt" since everyone gets their own and everyone needs to provide a different solution based on their input.

Day 10 Puzzle

On day 10 of Advent of Code, we had to retrace the steps of a very simple CPU based on the commands it's given. There are two commands: addx and noop.

addx 13
addx 4
noop
addx -1
addx 5

As always, I read in all lines:

with open('input.txt', 'r') as f:
    lines = f.readlines()
    lines = [entry.strip() for entry in lines]

The CPU class

This following class models the CPU from the puzzle for both parts. The sum of registers is specifically needed for part 1, while the display is for part 2.

class SimpleCPU:
    def __init__(self) -> None:
        self.register_X = 1
        self.cycle = 1
        self.waiting_add = 0
        self.wait = -1
        self.sum_of_registers = 0
        self.display = ""

    def run_command(self, command):
        if 'addx' in command:
            add_nr = int(command.split(' ')[1])
            self.wait = 2
            self.waiting_add = add_nr

    def advance_cycle(self):
        if self.cycle in [20, 60, 100, 140, 180, 220]:
            self.sum_of_registers += self.cycle * self.register_X
        self.cycle += 1
        self.wait -= 1
        if self.wait == 0:
            self.register_X += self.waiting_add

    def print_cpu_state(self, part):
        if part == 1:
            if self.cycle in [20, 60, 100, 140, 180, 220]:
                print(f"Completed cycles: {self.cycle}, register: {self.register_X}")
                if self.wait > 0:
                    print(f"waiting add: {self.waiting_add} in {self.wait} cycles")
                print('-----')
        else:
            sprite = [self.register_X - 1, self.register_X, self.register_X + 1]
            if (self.cycle-1) % 40 in sprite:
                self.display += "O"
            else:
                self.display += "_"
            if len(self.display) == 40:
                print(self.display)
                self.display = "" 

The main loop

After reading in the lines, we initiate a CPU and then run through the lines. It is important to check if the CPU is still busy with a 2-cycle add command, before taking the next command from our input.

def solve(file, part=1):
    with open(file, 'r') as f:
        lines = f.readlines()
        lines = [entry.strip() for entry in lines]

    cpu = SimpleCPU()

    while len(lines) > 0 or cpu.wait > 0:
        if cpu.wait > 0:
            pass
        else:
            line = lines.pop(0)
            cpu.run_command(line)
        cpu.print_cpu_state(part=part)
        cpu.advance_cycle()
    if part == 1:
        print('sum of signal strenghts', cpu.sum_of_registers)

Part 1

In part 1 we specifically need the sum of registers which is built up in the following code (excerpt from above):

def advance_cycle(self):
        if self.cycle in [20, 60, 100, 140, 180, 220]:
            self.sum_of_registers += self.cycle * self.register_X
solve('example2.txt', part=1)

Part 2

In part 2, we had to look ourselves at the printed output generated, by checking if the current position (which annoyingly ended up being cycle-1 for me, because the position of the cursor starts at 0 while the cycle starts counting at 1) is within one distance to the current register_X value, and if so it prints a symbol.

sprite = [self.register_X - 1, self.register_X, self.register_X + 1]
if (self.cycle-1) % 40 in sprite:
    self.display += "O"
else:
    self.display += "_"
if len(self.display) == 40:
    print(self.display)
    self.display = "" 
# example output, for the personal input, you can see capital letters in here
OO__OO__OO__OO__OO__OO__OO__OO__OO__OO__
OOO___OOO___OOO___OOO___OOO___OOO___OOO_
OOOO____OOOO____OOOO____OOOO____OOOO____
OOOOO_____OOOOO_____OOOOO_____OOOOO_____
OOOOOO______OOOOOO______OOOOOO______OOOO
OOOOOOO_______OOOOOOO_______OOOOOOO_____

Conclusion

Off by one errors are the worst. The CPU simulation was a nice touch, but debugging was frustrating especially the visual aspect since we did not have a real example of what the "hidden" capital letters would look like. I struggled to realize that I had an error in my code - the letter wasn't just wonky, it was indeed missing pixels...