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:
203
2019/intcode.py
203
2019/intcode.py
@ -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
|
Reference in New Issue
Block a user