diff --git a/2019/02/code.py b/2019/02/code.py index c311b85..10f1583 100644 --- a/2019/02/code.py +++ b/2019/02/code.py @@ -5,26 +5,28 @@ import sys, os sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import intcode +import intcode, intcode_test def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(num) for num in f.readline().split(",")] -def part1(vals: list) -> int: - memory = vals.copy() - memory[1], memory[2] = 12, 2 - return intcode.getOutput(memory) +def part1(pc: intcode.Computer) -> int: + pc.data[1], pc.data[2] = 12, 2 + pc.run() + return pc.data[0] -def part2(vals: list) -> int: +def part2(pc: intcode.Computer) -> int: for noun in range(100): for verb in range(100): - memory = vals.copy() - memory[1], memory[2] = noun, verb - if intcode.getOutput(memory) == 19690720: + pc.reset() + pc.data[1], pc.data[2] = noun, verb + pc.run() + if pc.data[0] == 19690720: return 100 * noun + verb if __name__ == "__main__": - vals = readFile() - print(f"Part 1: {part1(vals)}") - print(f"Part 2: {part2(vals)}") \ No newline at end of file + intcode_test.test_02() + pc = intcode.Computer(readFile()) + print(f"Part 1: {part1(pc)}") + print(f"Part 2: {part2(pc)}") \ No newline at end of file diff --git a/2019/05/code.py b/2019/05/code.py index 2008a1b..f7f28e4 100644 --- a/2019/05/code.py +++ b/2019/05/code.py @@ -5,44 +5,24 @@ import sys, os sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import intcode +import intcode, intcode_test def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(num) for num in f.readline().split(",")] -def part1(vals: list) -> int: - return intcode.getOutput(vals.copy(), input=1) +def part1(pc: intcode.Computer) -> int: + pc.reset(input=1) + pc.run() + return pc.data[0] -def part2(vals: list) -> int: - return intcode.getOutput(vals.copy(), input=5) - -def test(): - 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 +def part2(pc: intcode.Computer) -> int: + pc.reset(input=5) + pc.run() + return pc.data[0] if __name__ == "__main__": - test() - vals = readFile() - print(f"Part 1: {part1(vals)}") - print(f"Part 2: {part2(vals)}") \ No newline at end of file + intcode_test.test_05() + pc = intcode.Computer(readFile()) + print(f"Part 1: {part1(pc)}") + print(f"Part 2: {part2(pc)}") \ No newline at end of file diff --git a/2019/09/code.py b/2019/09/code.py index 724cbd0..b6ec996 100644 --- a/2019/09/code.py +++ b/2019/09/code.py @@ -5,24 +5,24 @@ import sys, os sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import intcode +import intcode, intcode_test def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(num) for num in f.readline().split(",")] -def part1(vals: list) -> int: - return intcode.getOutput(vals.copy(), input=1) +def part1(pc: intcode.Computer) -> int: + pc.reset(input=1) + pc.run() + return pc.data[0] -def part2(vals: list) -> int: - return intcode.getOutput(vals.copy(), input=2) - -def test(): - assert intcode.getOutput([104,1125899906842624,99]) == 1125899906842624 - assert len(str(intcode.getOutput([1102,34915192,34915192,7,4,7,99,0]))) == 16 +def part2(pc: intcode.Computer) -> int: + pc.reset(input=2) + pc.run() + return pc.data[0] if __name__ == "__main__": - test() - vals = readFile() - print(f"Part 1: {part1(vals)}") - print(f"Part 2: {part2(vals)}") \ No newline at end of file + intcode_test.test_09() + pc = intcode.Computer(readFile()) + print(f"Part 1: {part1(pc)}") + print(f"Part 2: {part2(pc)}") \ No newline at end of file diff --git a/2019/13/code.py b/2019/13/code.py index 10a0cae..5220eae 100644 --- a/2019/13/code.py +++ b/2019/13/code.py @@ -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): - pass - -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) +import sys, os +sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import intcode def readFile(): with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(num) for num in f.readline().split(",")] -def getOutput(vals : list, input=0, base=0): - vals = mylist(vals) - i = 0 - out = [] - while 1: - 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 +def getEntities(vals: list): + assert len(vals) % 3 == 0 + entities = {"empty": [], "wall": [], "block": [], + "paddle": tuple(), "ball": tuple(), "score": 0} + keys = tuple(entities.keys()) + 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: - 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 -def part1(entities): - print([len(x) for x in entities]) - print(entities[3], entities[4]) - return len(entities[2]) +def part1(pc: intcode.Computer) -> int: + pc.run() + entities = getEntities(pc.output) + return len(entities["block"]) -class Ball: - def __init__(self, x, y): - self.x, self.y = 0,0 - self.dx, self.dy = 0,0 +def part2(pc: intcode.Computer) -> int: + pc.reset(wait_for_input=True) + pc.data[0] = 2 -def part2(entities): - pass + # initial setup + 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__": - vals = readFile() - entities = getEntities() - print(f"Part 1: {part1(entities)}") - #print(f"Part 2: {part2(entities)}") \ No newline at end of file + test() + pc = intcode.Computer(readFile()) + print(f"Part 1: {part1(pc)}") + print(f"Part 2: {part2(pc)}") \ No newline at end of file diff --git a/2019/13/solution.txt b/2019/13/solution.txt index 5a25c12..bb51531 100644 --- a/2019/13/solution.txt +++ b/2019/13/solution.txt @@ -1 +1,2 @@ -Part 1: 200 \ No newline at end of file +Part 1: 200 +Part 2: 9803 \ No newline at end of file diff --git a/2019/README.md b/2019/README.md index 18136cc..f519a60 100644 --- a/2019/README.md +++ b/2019/README.md @@ -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: | | 09 | :white_check_mark: | :white_check_mark: || 10 | :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 | | | | 17 | | || 18 | | | | 19 | | || 20 | | | diff --git a/2019/intcode.py b/2019/intcode.py index fbc7b4a..000d48f 100644 --- a/2019/intcode.py +++ b/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() \ No newline at end of file + 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 \ No newline at end of file diff --git a/2019/intcode_test.py b/2019/intcode_test.py new file mode 100644 index 0000000..4544ac7 --- /dev/null +++ b/2019/intcode_test.py @@ -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() \ No newline at end of file