You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdventOfCode19/days/comp.py

119 lines
3.6 KiB

from typing import Dict, Tuple, Iterator, Iterable, Union
class OpcodeComputer():
"""OpcodeCOmputer for Advent of Code 2019
OpcodeComputer as specified by day 2 and 5 of the Advent of \
Code 2019 challenge.
.. _Advent of Code 2019 day 2
https://adventofcode.com/2019/day/2
.. _Advent of Code 2019 day 5
https://adventofcode.com/2019/day/5
"""
__last_result: int = 0
__initial_mem: Dict[int, int]
memory: Dict[int, int]
def __init__(self, file: str = None, mem: str = '99') -> None:
inp = mem
if file:
with open(file) as fp:
inp = fp.readline()
self.__initial_mem = dict(enumerate([int(k) for k in inp.split(',')]))
self.memory = self.__initial_mem
def parse_param(self, pc: int, mem: Dict[int, int], base: int) \
-> Tuple[int, int, int, int]:
"""Parse Intcode and return parameters as specified by parameter mode
Args:
pc (int): current instruction pointer
mem (List[int]): program code
Returns:
Tuple[int, int, int, int]: current opcode, parameter a, b and c
"""
i: str = "{0:05d}".format(mem[pc])
a: int = 0
b: int = 0
c: int = 0
try:
a = pc+1 if i[2] == '1'\
else mem[pc+1] + base if i[2] == '2'\
else mem[pc+1]
b = pc+2 if i[1] == '1'\
else mem[pc+2] + base if i[1] == '2'\
else mem[pc+2]
c = pc+3 if i[0] == '1'\
else mem[pc+3] + base if i[0] == '2'\
else mem[pc+3]
except KeyError:
pass
return (int(i[-2:]), a, b, c)
def process_op(self, inp: Union[Iterator[int], Iterable[int]] = [])\
-> Iterator[int]:
"""Run program code
Args:
mem (List[int]): Memory to run OpcodeComputer on
Returns:
int: Content in memory at position 0
"""
def read(position: int) -> int:
try:
return mem[position]
except KeyError:
return 0
base = 0
pc: int = 0
mem = self.memory
cmd: int = mem[pc]
input: Iterator[int] = iter(inp)
popcount = 0
while cmd != 99:
cmd, a, b, c = self.parse_param(pc, mem, base)
if cmd == 1:
mem[c] = read(a) + read(b)
pc += 4
elif cmd == 2:
mem[c] = read(a) * read(b)
pc += 4
elif cmd == 3:
mem[a] = int(next(input))
popcount += 1
pc += 2
elif cmd == 4:
yield read(a)
pc += 2
elif cmd == 5:
pc = read(b) if read(a) != 0 else pc + 3
elif cmd == 6:
pc = mem[b] if mem[a] == 0 else pc + 3
elif cmd == 7:
mem[c] = 1 if mem[a] < mem[b] else 0
pc += 4
elif cmd == 8:
mem[c] = 1 if mem[a] == mem[b] else 0
pc += 4
elif cmd == 9:
base += mem[a]
pc += 2
else:
raise Exception(f'Unknown Opcode encountered: {cmd}')
cmd = mem[pc]
self.__last_result = mem[0]
def process_all(self) -> int:
list(self.process_op())
return self.__last_result
def reset(self):
self.memory = self.__initial_mem.copy()