Advent of Code 2022 day 10
My Python solution for Advent of Code 2022 day 10. Just the right amount of frustrating to keep it fun and interesting. I spent ages debugging part two because I kept confusing the “sprite” with the CRT’s current position - I was adding padding to the CRT’s position not the sprite’s position, so I kept getting answers that were always slightly off by weird amounts.
Anyway, here we go. (I’ve chopped out the “large example” input and the right-hand side of the part two equality test from this code block.)
import math
from unittest import TestCase
from utils import get_raw_input
class Process:
def __init__(self, x_register=1):
self.x_register = x_register
self.current_instruction = None
self.current_step = 0
self.inputs = None
def tick(self):
if self.current_instruction:
exec(f"self.{self.current_instruction}()")
def new_instruction(self, instruction, inputs=None):
if self.current_instruction != None:
raise Exception("Already got an instruction")
else:
self.current_instruction = instruction
self.current_step = 0
self.inputs = inputs
def noop(self):
self.current_instruction = None
def addx(self):
if self.current_step < 1:
self.current_step += 1
else:
self.x_register += int(self.inputs)
self.current_instruction = None
@property
def ready(self):
return not bool(self.current_instruction)
def run_program(process, instructions):
cycles = [process.x_register]
for i, instruction in enumerate(instructions):
try:
process.new_instruction(instruction.split()[0], instruction.split()[1])
except IndexError:
process.new_instruction(instruction)
while not process.ready:
process.tick()
cycles.append(process.x_register)
return cycles
def extract_signal_strength(cycles):
return [(i + 1, value) for i, value in enumerate(cycles) if ((i % 40) - 19) == 0]
def calculate_signal_strength(signal_strengths):
return sum([math.prod(strength) for strength in signal_strengths])
def build_screen(cycles):
crt = []
for y in range(6):
row = []
for x in range(40):
sprite_centre = cycles[x + (y * 40)]
if x in range(sprite_centre - 1, sprite_centre + 2):
row.append("#")
else:
row.append(".")
crt.append(row)
return "\n".join(["".join(row) for row in crt])
def part_one(input):
process = Process()
cycles = run_program(process, input)
return calculate_signal_strength(extract_signal_strength(cycles))
def part_two(input):
process = Process()
cycles = run_program(process, input)
return build_screen(cycles)
class TestPartOne(TestCase):
def setUp(self):
self.small_example = [
"noop",
"addx 3",
"addx -5",
]
self.large_example = [
REMOVED
]
self.process = Process()
def test_first_example_x_register(self):
run_program(self.process, self.small_example)
self.assertEqual(self.process.x_register, -1)
def test_second_example_registers(self):
cycles = run_program(self.process, self.large_example)
self.assertEqual(cycles[19], 21)
self.assertEqual(cycles[59], 19)
self.assertEqual(cycles[99], 18)
self.assertEqual(cycles[139], 21)
self.assertEqual(cycles[179], 16)
self.assertEqual(cycles[219], 18)
def test_part_one_final_example(self):
self.assertEqual(part_one(self.large_example), 13140)
def test_part_two_example(self):
self.assertEqual(
part_two(self.large_example),
REMOVED,
)
if __name__ == "__main__":
input = get_raw_input("day_10_input.txt")
print(f"Part one: {part_one(input)}")
print(f"Part two: \n{part_two(input)}")