Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
867f99b
Create module for `CPU` class
katzuv Dec 24, 2022
d3248ff
Add CPU class
katzuv Dec 24, 2022
52ababb
Create solution file for part 1
katzuv Dec 25, 2022
66b8a0d
Add function that yields instructions from the input
katzuv Dec 25, 2022
1b1bc5d
Add initial interesting cycle class constant
katzuv Nov 11, 2023
9047fdc
Add initial interesting cycle difference class constant
katzuv Nov 11, 2023
88c84f3
Add constructor to `CPU` class
katzuv Dec 25, 2022
bb92cd8
Add method that is run when the CPU finished executing an instruction
katzuv Nov 11, 2023
227306c
Add method that handles the `noop` instruction
katzuv Nov 11, 2023
ccd660d
Add method that handles the `addx` instruction
katzuv Nov 11, 2023
a379c02
Add mapping of opcode to handler
katzuv Nov 11, 2023
d621540
Add method that runs a given instruction and calculates signal strength
katzuv Nov 11, 2023
fbf539d
Add function that returns signal strengths sum during interesting cycles
katzuv Nov 11, 2023
6beb7a8
Create solution file for part 2
katzuv Nov 11, 2023
77fcca4
Add a property for the CPU cycle
katzuv Nov 11, 2023
feefbd7
Add a property for the CPU x register value
katzuv Nov 11, 2023
96c2da4
Create module for `HandheldDevice` class
katzuv Nov 11, 2023
93b49c0
Create `HandheldDevice` class
katzuv Nov 11, 2023
f1033f6
Add screen width class constant
katzuv Nov 11, 2023
b7c8a53
Add screen height class constant
katzuv Nov 11, 2023
b7b5407
Add dark pixel symbol class constant
katzuv Nov 11, 2023
15dc4b5
Add lit pixel symbol class constant
katzuv Nov 11, 2023
40601f6
Add constructor to `HandheldDevice` class
katzuv Nov 11, 2023
eeb3459
Add method to `HandheldDevice` that runs instruction in underlying `CPU`
katzuv Nov 11, 2023
1ed1098
Add `HandheldDevice` property of if `CPU` ready for next instruction
katzuv Nov 11, 2023
f453ba0
Add method that draws the current pixel if needed
katzuv Nov 11, 2023
2420144
Add method that returns a rendered version of the device's screen
katzuv Nov 11, 2023
59ef6b4
Add function that return image drawn onscreen after program is run
katzuv Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions puzzles/solutions/2022/d10/cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Sequence


class CPU:
"""The handheld device's CPU."""

_INITIAL_INTERESTING_CYCLE = 20
_INTERESTING_CYCLES_DIFFERENCE = 40

def __init__(self):
"""Instantiate a CPU."""
self._x_register = 1

self._cycle = 0
self._cycle_in_instruction = 0
self.is_ready_for_next = False

self._interesting_cycle = self._INITIAL_INTERESTING_CYCLE
self.interesting_signal_strengths_sum = 0

@property
def cycle(self) -> int:
"""
:return: the current CPU's cycle
"""
return self._cycle

@property
def x_register(self) -> int:
"""
:return: the current value of the CPU's x register
"""
return self._x_register

def run(self, opcode: str, parameters: Sequence[int]) -> None:
"""
Run the given instruction.

This method also calculates the signal strength at interesting cycles.
:param opcode: instruction opcode
:param parameters: instruction parameters
"""
self._cycle += 1
if self.cycle == self._interesting_cycle:
signal_strength = self.cycle * self.x_register
self.interesting_signal_strengths_sum += signal_strength
self._interesting_cycle += self._INTERESTING_CYCLES_DIFFERENCE

handler = self._INSTRUCTION_TO_HANDLER[opcode]
handler(self, parameters)

def _finish_instruction(self) -> None:
"""Set the cycle in instruction counter to zero and mark the CPU as ready for the next instruction."""
self._cycle_in_instruction = 0
self.is_ready_for_next = True

def _noop(self, parameters: Sequence[int]) -> None:
"""Handle `noop` instruction."""
self._finish_instruction()

def _addx(self, parameters: Sequence[int]) -> None:
"""Handle `addx` instruction."""
if self._cycle_in_instruction == 0:
self._cycle_in_instruction += 1
self.is_ready_for_next = False
return
self._x_register += parameters[0]
self._finish_instruction()

_INSTRUCTION_TO_HANDLER = {"noop": _noop, "addx": _addx}
52 changes: 52 additions & 0 deletions puzzles/solutions/2022/d10/handheld_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import Sequence

from cpu import CPU


class HandheldDevice:
"""The handheld device, which has a CPU and a CRT screen."""

_SCREEN_WIDTH = 40
_SCREEN_HEIGHT = 6

_DARK_PIXEL = "."
_LIT_PIXEL = "#"

def __init__(self):
"""Instantiate a handheld device."""
self._cpu = CPU()
self._screen = [
[self._DARK_PIXEL] * self._SCREEN_WIDTH for _ in range(self._SCREEN_HEIGHT)
]

def run(self, opcode: str, parameters: Sequence[int]) -> None:
"""
Run the given instruction.
:param opcode: instruction opcode
:param parameters: instruction parameters
"""
self._cpu.run(opcode, parameters)

@property
def is_ready_for_next(self) -> bool:
return self._cpu.is_ready_for_next

def draw_current_pixel_if_needed(self) -> None:
"""Draw the pixel under the sprite if the CRT is under it."""
current_pixel_row, current_pixel_column = divmod(
self._cpu.cycle, self._SCREEN_WIDTH
)
current_x_register = self._cpu.x_register
sprite_boundaries = (
current_x_register - 1,
current_x_register,
current_x_register + 1,
)
if current_pixel_column in sprite_boundaries:
self._screen[current_pixel_row][current_pixel_column] = self._LIT_PIXEL

def render_screen(self) -> str:
"""
:return: rendered and prettified version of the screen
"""
return "\n".join("".join(row) for row in self._screen)
38 changes: 38 additions & 0 deletions puzzles/solutions/2022/d10/p1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import sys
from typing import Iterator

from cpu import CPU


def get_instructions(input_text: str) -> Iterator[tuple[str, tuple[int, ...]]]:
"""
:param input_text: puzzle input
:return: generator of instructions and their parameters
"""
for line in input_text.splitlines():
instruction, *parameters = line.split()
yield instruction, tuple(map(int, parameters))


def get_answer(input_text: str):
"""Return the sum of signal strengths during the 20th, 60th, 100th, 140th, 180th, and 220th cycles of the CPU."""
cpu = CPU()
instructions = get_instructions(input_text)
instruction = next(instructions)
while True:
instruction_opcode, parameters = instruction
cpu.run(instruction_opcode, parameters)
if cpu.is_ready_for_next:
try:
instruction = next(instructions)
except StopIteration:
break

return cpu.interesting_signal_strengths_sum


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.
29 changes: 29 additions & 0 deletions puzzles/solutions/2022/d10/p2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sys

from handheld_device import HandheldDevice
import p1


def get_answer(input_text: str):
"""Return the image drawn on the device's screen after the program is run."""
device = HandheldDevice()
instructions = p1.get_instructions(input_text)
instruction = next(instructions)
while True:
device.draw_current_pixel_if_needed()
instruction_opcode, parameters = instruction
device.run(instruction_opcode, parameters)
if device.is_ready_for_next:
try:
instruction = next(instructions)
except StopIteration:
break

return device.render_screen()


if __name__ == "__main__":
try:
print(get_answer(sys.argv[1]))
except IndexError:
pass # Don't crash if no input was passed through command line arguments.