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

@ -5,26 +5,28 @@
import sys, os import sys, os
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import intcode import intcode, intcode_test
def readFile() -> list: def readFile() -> list:
with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f:
return [int(num) for num in f.readline().split(",")] return [int(num) for num in f.readline().split(",")]
def part1(vals: list) -> int: def part1(pc: intcode.Computer) -> int:
memory = vals.copy() pc.data[1], pc.data[2] = 12, 2
memory[1], memory[2] = 12, 2 pc.run()
return intcode.getOutput(memory) return pc.data[0]
def part2(vals: list) -> int: def part2(pc: intcode.Computer) -> int:
for noun in range(100): for noun in range(100):
for verb in range(100): for verb in range(100):
memory = vals.copy() pc.reset()
memory[1], memory[2] = noun, verb pc.data[1], pc.data[2] = noun, verb
if intcode.getOutput(memory) == 19690720: pc.run()
if pc.data[0] == 19690720:
return 100 * noun + verb return 100 * noun + verb
if __name__ == "__main__": if __name__ == "__main__":
vals = readFile() intcode_test.test_02()
print(f"Part 1: {part1(vals)}") pc = intcode.Computer(readFile())
print(f"Part 2: {part2(vals)}") print(f"Part 1: {part1(pc)}")
print(f"Part 2: {part2(pc)}")

View File

@ -5,44 +5,24 @@
import sys, os import sys, os
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import intcode import intcode, intcode_test
def readFile() -> list: def readFile() -> list:
with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f:
return [int(num) for num in f.readline().split(",")] return [int(num) for num in f.readline().split(",")]
def part1(vals: list) -> int: def part1(pc: intcode.Computer) -> int:
return intcode.getOutput(vals.copy(), input=1) pc.reset(input=1)
pc.run()
return pc.data[0]
def part2(vals: list) -> int: def part2(pc: intcode.Computer) -> int:
return intcode.getOutput(vals.copy(), input=5) pc.reset(input=5)
pc.run()
def test(): return pc.data[0]
assert intcode.parseCode(1001) == (1, 0, 1, 0)
assert intcode.getOutput([1,0,0,0,99]) == 2
assert intcode.getOutput([1,1,1,4,99,5,6,0,99]) == 30
assert intcode.getOutput([3,0,4,0,99], input=42) == 42
assert intcode.getOutput([1101,100,-1,4,0], getVals=True) == [1101,100,-1,4,99]
assert intcode.getOutput([3,9,8,9,10,9,4,9,99,-1,8], input=0) == 0
assert intcode.getOutput([3,9,8,9,10,9,4,9,99,-1,8], input=8) == 1
assert intcode.getOutput([3,9,7,9,10,9,4,9,99,-1,8], input=0) == 1
assert intcode.getOutput([3,9,7,9,10,9,4,9,99,-1,8], input=8) == 0
assert intcode.getOutput([3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9], input=0) == 0
assert intcode.getOutput([3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9], input=1) == 1
assert intcode.getOutput([3,3,1105,-1,9,1101,0,0,12,4,12,99,1], input=0) == 0
assert intcode.getOutput([3,3,1105,-1,9,1101,0,0,12,4,12,99,1], input=1) == 1
assert intcode.getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,
1101,1000,1,20,4,20,1105,1,46,98,99], input=0) == 999
assert intcode.getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,
1101,1000,1,20,4,20,1105,1,46,98,99], input=8) == 1000
assert intcode.getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,
1101,1000,1,20,4,20,1105,1,46,98,99], input=9) == 1001
if __name__ == "__main__": if __name__ == "__main__":
test() intcode_test.test_05()
vals = readFile() pc = intcode.Computer(readFile())
print(f"Part 1: {part1(vals)}") print(f"Part 1: {part1(pc)}")
print(f"Part 2: {part2(vals)}") print(f"Part 2: {part2(pc)}")

View File

@ -5,24 +5,24 @@
import sys, os import sys, os
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import intcode import intcode, intcode_test
def readFile() -> list: def readFile() -> list:
with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f:
return [int(num) for num in f.readline().split(",")] return [int(num) for num in f.readline().split(",")]
def part1(vals: list) -> int: def part1(pc: intcode.Computer) -> int:
return intcode.getOutput(vals.copy(), input=1) pc.reset(input=1)
pc.run()
return pc.data[0]
def part2(vals: list) -> int: def part2(pc: intcode.Computer) -> int:
return intcode.getOutput(vals.copy(), input=2) pc.reset(input=2)
pc.run()
def test(): return pc.data[0]
assert intcode.getOutput([104,1125899906842624,99]) == 1125899906842624
assert len(str(intcode.getOutput([1102,34915192,34915192,7,4,7,99,0]))) == 16
if __name__ == "__main__": if __name__ == "__main__":
test() intcode_test.test_09()
vals = readFile() pc = intcode.Computer(readFile())
print(f"Part 1: {part1(vals)}") print(f"Part 1: {part1(pc)}")
print(f"Part 2: {part2(vals)}") print(f"Part 2: {part2(pc)}")

View File

@ -1,110 +1,84 @@
""" https://adventofcode.com/2019/day/13 """ # SPDX-License-Identifier: MIT
# Copyright (c) 2019 Akumatic
#
# https://adventofcode.com/2019/day/13
class InvalidOpcode(Exception): import sys, os
pass sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import intcode
class mylist(list):
def __getitem__(self, item):
try:
return super(mylist,self).__getitem__(item)
except IndexError as e:
if item < 0: raise IndexError()
super(mylist,self).extend([0]*(item + 1 - super(mylist,self).__len__()))
return super(mylist,self).__getitem__(item)
def __setitem__(self, idx, val):
try:
return super(mylist,self).__setitem__(idx,val)
except IndexError as e:
if idx < 0: raise IndexError()
super(mylist,self).extend([0]*(idx + 1 - super(mylist,self).__len__()))
return super(mylist,self).__setitem__(idx,val)
def readFile(): def readFile():
with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f:
return [int(num) for num in f.readline().split(",")] return [int(num) for num in f.readline().split(",")]
def getOutput(vals : list, input=0, base=0): def getEntities(vals: list):
vals = mylist(vals) assert len(vals) % 3 == 0
i = 0 entities = {"empty": [], "wall": [], "block": [],
out = [] "paddle": tuple(), "ball": tuple(), "score": 0}
while 1: keys = tuple(entities.keys())
opcode = (vals[i] % 100,
vals[i] // 100 % 10,
vals[i] // 1000 % 10,
vals[i] // 10000 % 10)
# 0 Parameter
if opcode[0] in [99]: # Termination
return vals, out
# 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
# 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
for i in range(0, len(vals), 3):
# updated score
if (vals[i] == -1 and vals[i+1] == 0):
entities["score"] = vals[i+2]
continue
# updated ball, paddle and other positions
pos = (vals[i], vals[i+1])
if vals[i+2] < 3:
entities[keys[vals[i+2]]].append(pos)
else: else:
raise InvalidOpcode() entities[keys[vals[i+2]]] = pos
def getEntities():
output = getOutput(vals.copy())[1]
output.reverse()
assert len(output) % 3 == 0
entities = [set() for _ in range(5)]
while output:
pos = (output.pop(), output.pop())
tile = output.pop()
entities[tile].add(pos)
return entities return entities
def part1(entities): def part1(pc: intcode.Computer) -> int:
print([len(x) for x in entities]) pc.run()
print(entities[3], entities[4]) entities = getEntities(pc.output)
return len(entities[2]) return len(entities["block"])
class Ball: def part2(pc: intcode.Computer) -> int:
def __init__(self, x, y): pc.reset(wait_for_input=True)
self.x, self.y = 0,0 pc.data[0] = 2
self.dx, self.dy = 0,0
def part2(entities): # initial setup
pass pc.run()
idx = pc.output.index(-1)
while pc.output[idx + 1] != 0:
idx = pc.output.index(-1, idx + 1)
entities = getEntities(pc.output[:idx])
pc.output = pc.output[idx:]
# loop
while not pc.stop:
pc.run()
changes = getEntities(pc.output)
if changes["score"]:
entities["score"] = changes["score"]
if changes["ball"]:
changes["empty"].remove(entities["ball"])
entities["ball"] = changes["ball"]
if changes["paddle"]:
changes["empty"].remove(entities["paddle"])
entities["paddle"] = changes["paddle"]
for e in changes["empty"]:
entities["block"].remove(e)
pc.output.clear()
pc.input = entities["ball"][0] - entities["paddle"][0]
return entities["score"]
def test():
test_input = [1, 2, 3, 6, 5, 4]
test_output = getEntities(test_input)
assert test_output["empty"] == test_output["wall"] == test_output["block"] == []
assert test_output["paddle"] == (1, 2) and test_output["ball"] == (6, 5)
if __name__ == "__main__": if __name__ == "__main__":
vals = readFile() test()
entities = getEntities() pc = intcode.Computer(readFile())
print(f"Part 1: {part1(entities)}") print(f"Part 1: {part1(pc)}")
#print(f"Part 2: {part2(entities)}") print(f"Part 2: {part2(pc)}")

View File

@ -1 +1,2 @@
Part 1: 200 Part 1: 200
Part 2: 9803

View File

@ -18,7 +18,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day
| 07 | :white_check_mark: | :white_check_mark: || 08 | :white_check_mark: | :white_check_mark: | | 07 | :white_check_mark: | :white_check_mark: || 08 | :white_check_mark: | :white_check_mark: |
| 09 | :white_check_mark: | :white_check_mark: || 10 | :white_check_mark: | :white_check_mark: | | 09 | :white_check_mark: | :white_check_mark: || 10 | :white_check_mark: | :white_check_mark: |
| 11 | | || 12 | :white_check_mark: | :white_check_mark: | | 11 | | || 12 | :white_check_mark: | :white_check_mark: |
| 13 | | || 14 | | | | 13 | :white_check_mark: | :white_check_mark: || 14 | | |
| 15 | | || 16 | | | | 15 | | || 16 | | |
| 17 | | || 18 | | | | 17 | | || 18 | | |
| 19 | | || 20 | | | | 19 | | || 20 | | |

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (c) 2019 Akumatic # Copyright (c) 2020 Akumatic
class InvalidOpcode(Exception): class InvalidOpcode(Exception):
pass pass
@ -27,64 +27,153 @@ class Data:
def toList(self): def toList(self):
return list(self._data.values()) return list(self._data.values())
def parseCode(i: int) -> tuple: def maxIdx(self):
return (i % 100, i // 100 % 10, i // 1000 % 10, i // 10000 % 10) return max(self._data.keys())
def getOutput(vals: list, input=0, base=0, full_out: bool = False, class Computer:
getVals: bool = False, output: bool=False) -> int: def __init__(self,
vals = Data(vals) vals: list,
i = 0 base: int = 0,
out = [] input: int = 0,
while True: wait_for_input: bool = False
opcode = parseCode(vals[i]) ):
self._val_backup = vals.copy()
# 0 Parameter self.reset(base=base, input=input, wait_for_input=wait_for_input)
if opcode[0] == 99: # Termination
if output:
return out
if getVals:
return vals.toList()
if full_out:
return vals, out
return vals[0]
# 1 Parameter def reset(self, vals = None, base = 0, input = 0, wait_for_input = False):
elif opcode[0] in (3,4,9): if vals is not None:
a = vals[i+1] if not opcode[1] else i+1 if opcode[1] == 1 else base+vals[i+1] self._val_backup = vals
if opcode[0] == 3: # Input self.data: Data = Data(self._val_backup)
vals[a] = input self.base: int = base
elif opcode[0] == 4: # Output self.input: int = None if wait_for_input else input
vals[0] = vals[a] self.pointer: int = 0
out.append(vals[0]) self.base: int = base
elif opcode[0] == 9: # Adjust Base self.input: int = input
base += vals[a] self.output: list = list()
i += 2 self.opcode: tuple = None
self.stop = False
self.wait = False
# # # # # # # # # # #
# opcode functions #
# # # # # # # # # # #
# 2 Parameter # opcode 99
elif opcode[0] in (5,6): def __terminate(self):
a = vals[i+1] if not opcode[1] else i+1 if opcode[1] == 1 else base+vals[i+1] self.stop = True
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 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: 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

59
2019/intcode_test.py Normal file
View File

@ -0,0 +1,59 @@
import intcode
def test_02():
pc = intcode.Computer([1,0,0,0,99])
pc.run()
assert pc.data.toList() == [2,0,0,0,99]
pc.reset(vals=[2,3,0,3,99]), pc.run()
assert pc.data.toList() == [2,3,0,6,99]
pc.reset(vals=[2,4,4,5,99,0]), pc.run()
assert pc.data.toList() == [2,4,4,5,99,9801]
pc.reset(vals=[1,1,1,4,99,5,6,0,99]), pc.run()
assert pc.data.toList() == [30,1,1,4,2,5,6,0,99]
def test_05():
pc = intcode.Computer([1,0,0,0,99])
pc.run()
assert pc.data[0] == 2
pc.reset(vals=[1,1,1,4,99,5,6,0,99]), pc.run()
assert pc.data[0] == 30
pc.reset(vals=[3,0,4,0,99], input=42), pc.run()
assert pc.data[0] == 42
pc.reset(vals=[1101,100,-1,4,0]), pc.run()
assert pc.data.toList() == [1101,100,-1,4,99]
pc.reset(vals=[3,9,8,9,10,9,4,9,99,-1,8]), pc.run()
assert pc.data[0] == 0
pc.reset(input=8), pc.run()
assert pc.data[0] == 1
pc.reset(vals=[3,9,7,9,10,9,4,9,99,-1,8], input=0), pc.run()
assert pc.data[0] == 1
pc.reset(input=8), pc.run()
assert pc.data[0] == 0
pc.reset(vals=[3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9]), pc.run()
assert pc.data[0] == 0
pc.reset(input=1), pc.run()
assert pc.data[0] == 1
pc.reset(vals=[3,3,1105,-1,9,1101,0,0,12,4,12,99,1]), pc.run()
assert pc.data[0] == 0
pc.reset(input=1), pc.run()
assert pc.data[0] == 1
pc.reset(vals=[3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,
1101,1000,1,20,4,20,1105,1,46,98,99]), pc.run()
assert pc.data[0] == 999
pc.reset(input=8), pc.run()
assert pc.data[0] == 1000
pc.reset(input=9), pc.run()
assert pc.data[0] == 1001
def test_09():
pc = intcode.Computer([104,1125899906842624,99])
pc.run()
assert pc.data[0] == 1125899906842624
pc.reset(vals=[1102,34915192,34915192,7,4,7,99,0]), pc.run()
assert len(str(pc.data[0])) == 16
if __name__ == "__main__":
test_02()
test_05()
test_09()