Implement 2019 day 9

This commit is contained in:
2021-01-24 10:16:14 +01:00
parent 4418292dc4
commit b65c805891
3 changed files with 68 additions and 10 deletions

25
2019/aoc2019/day09.py Normal file
View 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)

View File

@@ -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

View File

@@ -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