mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Implement 2019 day 9
This commit is contained in:
25
2019/aoc2019/day09.py
Normal file
25
2019/aoc2019/day09.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import sys
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def run_machine(data: TextIO, initializer: int) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
computer = Computer(program)
|
||||||
|
computer.input.append(initializer)
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
if len(computer.output) > 1:
|
||||||
|
sys.exit(computer.output)
|
||||||
|
else:
|
||||||
|
return computer.output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return run_machine(data, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
return run_machine(data, 2)
|
||||||
@@ -11,12 +11,14 @@ def read_program(data: TextIO) -> List[int]:
|
|||||||
class Computer:
|
class Computer:
|
||||||
program: List[int]
|
program: List[int]
|
||||||
pointer: int
|
pointer: int
|
||||||
|
relative_base: int
|
||||||
input: collections.deque[int]
|
input: collections.deque[int]
|
||||||
output: collections.deque[int]
|
output: collections.deque[int]
|
||||||
|
|
||||||
def __init__(self, program: List[int], pointer: int = 0) -> None:
|
def __init__(self, program: List[int], pointer: int = 0) -> None:
|
||||||
self.program = program
|
self.program = program
|
||||||
self.pointer = pointer
|
self.pointer = pointer
|
||||||
|
self.relative_base = 0
|
||||||
self.input = collections.deque()
|
self.input = collections.deque()
|
||||||
self.output = collections.deque()
|
self.output = collections.deque()
|
||||||
|
|
||||||
@@ -30,29 +32,41 @@ class Computer:
|
|||||||
def __getitem__(self, item: Union[int, Tuple[int, int]]) -> int:
|
def __getitem__(self, item: Union[int, Tuple[int, int]]) -> int:
|
||||||
mode, key = self._mode_and_key(item)
|
mode, key = self._mode_and_key(item)
|
||||||
|
|
||||||
if mode == 0:
|
if mode == 1:
|
||||||
self._ensure_length(key + 1)
|
|
||||||
return self.program[key]
|
|
||||||
elif mode == 1:
|
|
||||||
return key
|
return key
|
||||||
|
elif mode == 0:
|
||||||
|
pass # Nothing to do here, handled below
|
||||||
|
elif mode == 2:
|
||||||
|
key += self.relative_base
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Unsupported mode "{mode}"')
|
raise ValueError(f'Unsupported mode "{mode}"')
|
||||||
|
|
||||||
|
self._ensure_length(key + 1)
|
||||||
|
return self.program[key]
|
||||||
|
|
||||||
def __setitem__(self, item: Union[int, Tuple[int, int]], value: int) -> None:
|
def __setitem__(self, item: Union[int, Tuple[int, int]], value: int) -> None:
|
||||||
mode, key = self._mode_and_key(item)
|
mode, key = self._mode_and_key(item)
|
||||||
|
|
||||||
if mode == 0:
|
if mode == 1:
|
||||||
self._ensure_length(key + 1)
|
|
||||||
self.program[key] = value
|
|
||||||
elif mode == 1:
|
|
||||||
raise ValueError('Cannot assign to an immediate')
|
raise ValueError('Cannot assign to an immediate')
|
||||||
|
elif mode == 0:
|
||||||
|
pass # Nothing to do here, handled below
|
||||||
|
elif mode == 2:
|
||||||
|
key += self.relative_base
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Unsupported mode "{mode}"')
|
raise ValueError(f'Unsupported mode "{mode}"')
|
||||||
|
|
||||||
|
self._ensure_length(key + 1)
|
||||||
|
self.program[key] = value
|
||||||
|
|
||||||
def _ensure_length(self, length: int) -> None:
|
def _ensure_length(self, length: int) -> None:
|
||||||
if len(self.program) < length:
|
if len(self.program) < length:
|
||||||
# Double current program size with 0s
|
if 2 * len(self.program) >= length:
|
||||||
self.program.extend(0 for _ in range(len(self.program)))
|
# Double current program size with 0s
|
||||||
|
self.program.extend(0 for _ in range(len(self.program)))
|
||||||
|
else:
|
||||||
|
# Resize until the desired length
|
||||||
|
self.program.extend(0 for _ in range(length - len(self.program)))
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
""" Run until failure """
|
""" Run until failure """
|
||||||
@@ -116,6 +130,10 @@ class Computer:
|
|||||||
else:
|
else:
|
||||||
self[mode[2], 3] = 0
|
self[mode[2], 3] = 0
|
||||||
self.pointer += 4
|
self.pointer += 4
|
||||||
|
elif opcode == 9:
|
||||||
|
# Adjust relative base
|
||||||
|
self.relative_base += self[mode[0], 1]
|
||||||
|
self.pointer += 2
|
||||||
elif opcode == 99:
|
elif opcode == 99:
|
||||||
# Halt
|
# Halt
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -64,3 +64,18 @@ def test_day5_example(inputs: int, expected: int):
|
|||||||
computer.run()
|
computer.run()
|
||||||
|
|
||||||
assert computer.output.pop() == expected
|
assert computer.output.pop() == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('program,output', [
|
||||||
|
([109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99],
|
||||||
|
[109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99]),
|
||||||
|
([1102, 34915192, 34915192, 7, 4, 7, 99, 0], [1219070632396864]),
|
||||||
|
([104, 1125899906842624, 99], [1125899906842624]),
|
||||||
|
])
|
||||||
|
def test_instructions_day9(program: List[int], output: List[int]) -> None:
|
||||||
|
computer = Computer(program)
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
result = list(computer.output)
|
||||||
|
|
||||||
|
assert result == output
|
||||||
|
|||||||
Reference in New Issue
Block a user