179 lines
5.0 KiB
Python
179 lines
5.0 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2020 Akumatic
|
|
|
|
class InvalidOpcode(Exception):
|
|
pass
|
|
|
|
class IndexError(Exception):
|
|
pass
|
|
|
|
class Data:
|
|
def __init__(self, vals: list):
|
|
self._data = {i:vals[i] for i in range(len(vals))}
|
|
|
|
def __getitem__(self, idx):
|
|
if idx < 0:
|
|
raise IndexError("Negative Index")
|
|
if idx in self._data:
|
|
return self._data[idx]
|
|
self._data[idx] = 0
|
|
return 0
|
|
|
|
def __setitem__(self, idx, val):
|
|
if idx < 0:
|
|
raise IndexError("Negative Index")
|
|
self._data[idx] = val
|
|
|
|
def toList(self):
|
|
return list(self._data.values())
|
|
|
|
def maxIdx(self):
|
|
return max(self._data.keys())
|
|
|
|
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)
|
|
|
|
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 #
|
|
# # # # # # # # # # #
|
|
|
|
# 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:
|
|
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 |