2019 Day 13 Part 2; intcode as class; intcode_test to assure functionality; changed old code to adapt intcode computer as object

This commit is contained in:
Akumatic
2020-12-03 23:45:28 +01:00
parent 592513f6b7
commit 0d748eae52
8 changed files with 317 additions and 212 deletions

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2019 Akumatic
# Copyright (c) 2020 Akumatic
class InvalidOpcode(Exception):
pass
@ -27,64 +27,153 @@ class Data:
def toList(self):
return list(self._data.values())
def parseCode(i: int) -> tuple:
return (i % 100, i // 100 % 10, i // 1000 % 10, i // 10000 % 10)
def maxIdx(self):
return max(self._data.keys())
def getOutput(vals: list, input=0, base=0, full_out: bool = False,
getVals: bool = False, output: bool=False) -> int:
vals = Data(vals)
i = 0
out = []
while True:
opcode = parseCode(vals[i])
# 0 Parameter
if opcode[0] == 99: # Termination
if output:
return out
if getVals:
return vals.toList()
if full_out:
return vals, out
return vals[0]
class Computer:
def __init__(self,
vals: list,
base: int = 0,
input: int = 0,
wait_for_input: bool = False
):
self._val_backup = vals.copy()
self.reset(base=base, input=input, wait_for_input=wait_for_input)
# 1 Parameter
elif opcode[0] in (3,4,9):
a = vals[i+1] if not opcode[1] else i+1 if opcode[1] == 1 else base+vals[i+1]
if opcode[0] == 3: # Input
vals[a] = input
elif opcode[0] == 4: # Output
vals[0] = vals[a]
out.append(vals[0])
elif opcode[0] == 9: # Adjust Base
base += vals[a]
i += 2
def reset(self, vals = None, base = 0, input = 0, wait_for_input = False):
if vals is not None:
self._val_backup = vals
self.data: Data = Data(self._val_backup)
self.base: int = base
self.input: int = None if wait_for_input else input
self.pointer: int = 0
self.base: int = base
self.input: int = input
self.output: list = list()
self.opcode: tuple = None
self.stop = False
self.wait = False
# # # # # # # # # # #
# opcode functions #
# # # # # # # # # # #
# 2 Parameter
elif opcode[0] in (5,6):
a = vals[i+1] if not opcode[1] else i+1 if opcode[1] == 1 else base+vals[i+1]
b = vals[i+2] if not opcode[2] else i+2 if opcode[2] == 1 else base+vals[i+2]
if opcode[0] == 5 and vals[a] != 0: # Jump-if-true
i = vals[b]
elif opcode[0] == 6 and vals[a] == 0: # Jump-if-false
i = vals[b]
else:
i += 3
# 3 Parameter
elif opcode[0] in (1,2,7,8):
a = vals[i+1] if not opcode[1] else i+1 if opcode[1] == 1 else base+vals[i+1]
b = vals[i+2] if not opcode[2] else i+2 if opcode[2] == 1 else base+vals[i+2]
c = vals[i+3] if not opcode[3] else i+3 if opcode[3] == 1 else base+vals[i+3]
if opcode[0] == 1: # Addition
vals[c] = vals[a] + vals[b]
elif opcode[0] == 2: # Multiplication
vals[c] = vals[a] * vals[b]
elif opcode[0] == 7: # Less Than
vals[c] = int(vals[a] < vals[b])
elif opcode[0] == 8: # Equals
vals[c] = int(vals[a] == vals[b])
i += 4
# opcode 99
def __terminate(self):
self.stop = True
# opcode 1
def __add(self, a: int, b: int, c: int):
self.data[c] = self.data[a] + self.data[b]
self.pointer += 4
# opcode 2
def __multiply(self, a: int, b: int, c: int): # 2
self.data[c] = self.data[a] * self.data[b]
self.pointer += 4
# opcode 3
def __input(self, a: int): # 3
self.data[a] = self.input
self.pointer += 2
# opcode 4
def __output(self, a: int): # 4
self.data[0] = self.data[a]
self.output.append(self.data[0])
self.pointer += 2
# opcode 5
def __jump_if_true(self, a: int, b: int): # 5
if self.data[a]:
self.pointer = self.data[b]
else:
raise InvalidOpcode()
self.pointer += 3
# opcode 6
def __jump_if_false(self, a: int, b: int): # 6
if not self.data[a]:
self.pointer = self.data[b]
else:
self.pointer += 3
# opcode 7
def __less_than(self, a: int, b: int, c: int): # 7
self.data[c] = int(self.data[a] < self.data[b])
self.pointer += 4
# opcode 8
def __equals(self, a: int, b: int, c: int): # 8
self.data[c] = int(self.data[a] == self.data[b])
self.pointer += 4
# opcode 9
def __adjust_base(self, a: int): # 9
self.base += self.data[a]
self.pointer += 2
# # # # # # # # # # # # #
# operational functions #
# # # # # # # # # # # # #
def __setOpcode(self):
self.opcode = (
self.data[self.pointer] % 100,
self.data[self.pointer] // 100 % 10,
self.data[self.pointer] // 1000 % 10,
self.data[self.pointer] // 10000 % 10
)
def __getParameter(self, n: int):
params = list()
for i in range(1, n + 1):
params.append(
self.data[self.pointer + i] if self.opcode[i] == 0 else \
self.pointer + i if self.opcode[i] == 1 else \
self.data[self.pointer + i] + self.base
)
return params
def run(self):
while not self.stop:
self.__setOpcode()
if self.opcode[0] == 99:
self.__terminate()
elif self.opcode[0] == 1: # add
self.__add(*self.__getParameter(3))
elif self.opcode[0] == 2: # multiply
self.__multiply(*self.__getParameter(3))
elif self.opcode[0] == 3: # input
if self.input is not None:
if self.wait:
self.wait = False
self.__input(*self.__getParameter(1))
self.input = None
else:
self.wait = True
return
elif self.opcode[0] == 4: # output
self.__output(*self.__getParameter(1))
elif self.opcode[0] == 5: # jump if true
self.__jump_if_true(*self.__getParameter(2))
elif self.opcode[0] == 6: # jump if false
self.__jump_if_false(*self.__getParameter(2))
elif self.opcode[0] == 7: # less than
self.__less_than(*self.__getParameter(3))
elif self.opcode[0] == 8: # equals
self.__equals(*self.__getParameter(3))
elif self.opcode[0] == 9: # adjust base
self.__adjust_base(*self.__getParameter(1))
else:
raise InvalidOpcode