From 10c048ab41b510b5ab3f123a36078c230b82f060 Mon Sep 17 00:00:00 2001 From: Akumatic Date: Wed, 2 Dec 2020 21:34:21 +0100 Subject: [PATCH] Intcode as a single file, add SPDX-License-Identifier --- 2019/01/code.py | 11 ++-- 2019/02/code.py | 29 +++++------ 2019/03/code.py | 36 ++++--------- 2019/03/wire.py | 21 ++++++++ 2019/04/code.py | 19 ++++--- 2019/05/code.py | 120 +++++++++++--------------------------------- 2019/06/code.py | 17 ++++--- 2019/07/amp.py | 93 ++++++++++++++++++++++++++++++++++ 2019/07/code.py | 109 +++++----------------------------------- 2019/08/code.py | 20 +++++--- 2019/09/code.py | 95 ++++++----------------------------- 2019/10/asteroid.py | 14 ++++++ 2019/10/code.py | 31 +++++------- 2019/12/code.py | 41 ++++----------- 2019/12/moon.py | 25 +++++++++ 2019/intcode.py | 90 +++++++++++++++++++++++++++++++++ 2020/README.md | 2 +- 17 files changed, 386 insertions(+), 387 deletions(-) create mode 100644 2019/03/wire.py create mode 100644 2019/07/amp.py create mode 100644 2019/10/asteroid.py create mode 100644 2019/12/moon.py create mode 100644 2019/intcode.py diff --git a/2019/01/code.py b/2019/01/code.py index 185146e..bb2f053 100644 --- a/2019/01/code.py +++ b/2019/01/code.py @@ -1,13 +1,16 @@ -""" https://adventofcode.com/2019/day/1 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/1 -def readFile(): +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(line[:-1]) for line in f.readlines()] -def part1(vals : list): +def part1(vals: list) -> int: return sum([val // 3 - 2 for val in vals]) -def part2(vals : list): +def part2(vals: list) -> int: fuel = [val // 3 - 2 for val in vals] for f in fuel: temp = f // 3 - 2 diff --git a/2019/02/code.py b/2019/02/code.py index 1e3e547..c311b85 100644 --- a/2019/02/code.py +++ b/2019/02/code.py @@ -1,30 +1,27 @@ -""" https://adventofcode.com/2019/day/2 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/2 -def readFile(): +import sys, os +sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import intcode + +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 getOutput(vals : list): - for i in range(0, len(vals), 4): - if vals[i] == 99: - break - elif vals[i] == 1: - vals[vals[i+3]] = vals[vals[i+1]] + vals[vals[i+2]] - else: # vals[i] == 2 - vals[vals[i+3]] = vals[vals[i+1]] * vals[vals[i+2]] - return vals[0] - -def part1(vals : list): +def part1(vals: list) -> int: memory = vals.copy() memory[1], memory[2] = 12, 2 - return getOutput(memory) + return intcode.getOutput(memory) -def part2(vals : list): +def part2(vals: list) -> int: for noun in range(100): for verb in range(100): memory = vals.copy() memory[1], memory[2] = noun, verb - if getOutput(memory) == 19690720: + if intcode.getOutput(memory) == 19690720: return 100 * noun + verb if __name__ == "__main__": diff --git a/2019/03/code.py b/2019/03/code.py index b3224e5..7d8cd87 100644 --- a/2019/03/code.py +++ b/2019/03/code.py @@ -1,29 +1,15 @@ -""" https://adventofcode.com/2019/day/3 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/3 -def readFile(): +from wire import Wire + +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [Wire(val for val in line[:-1].split(",")) for line in f.readlines()] -class Wire: - def __init__(self, input): - self.input = input - self.fields = {(0, 0, 0)} - self.__initFields() - - def __initFields(self): - x, y, step = 0, 0, 0 - for i in self.input: - dir = i[0] - len = int(i[1:]) - dx = 1 if dir == "R" else -1 if dir == "L" else 0 - dy = 1 if dir == "U" else -1 if dir == "D" else 0 - for r in range(0, len): - x += dx - y += dy - step += 1 - self.fields.add((x, y, step)) - -def getIntersections(a , b): +def getIntersections(a, b) -> set: intersections = set() bFields = {(val[0], val[1]) for val in b.fields} for point in a.fields: @@ -31,12 +17,12 @@ def getIntersections(a , b): intersections.add(point) return intersections -def getIntersectionDistance(intersections, i): +def getIntersectionDistance(intersections, i) -> int: for intersection in intersections: if intersection[0] == i[0] and intersection[1] == i[1]: return intersection[2] -def part1(vals : list): +def part1(vals: list) -> int: intersections = getIntersections(vals[0], vals[1]) dists = [] for i in intersections: @@ -44,7 +30,7 @@ def part1(vals : list): dists.sort() return dists[1] -def part2(vals : list): +def part2(vals: list) -> int: intersections = getIntersections(vals[0], vals[1]) intersections2 = getIntersections(vals[1], vals[0]) steps = [] diff --git a/2019/03/wire.py b/2019/03/wire.py new file mode 100644 index 0000000..8516f54 --- /dev/null +++ b/2019/03/wire.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +class Wire: + def __init__(self, input): + self.input = input + self.fields = {(0, 0, 0)} + self.__initFields() + + def __initFields(self): + x, y, step = 0, 0, 0 + for i in self.input: + dir = i[0] + len = int(i[1:]) + dx = 1 if dir == "R" else -1 if dir == "L" else 0 + dy = 1 if dir == "U" else -1 if dir == "D" else 0 + for r in range(0, len): + x += dx + y += dy + step += 1 + self.fields.add((x, y, step)) \ No newline at end of file diff --git a/2019/04/code.py b/2019/04/code.py index 1452be2..f57a89b 100644 --- a/2019/04/code.py +++ b/2019/04/code.py @@ -1,10 +1,13 @@ -""" https://adventofcode.com/2019/day/4 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/4 -def readFile(): +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(vals) for vals in f.readline().split("-")] -def getNumbers(min, max): +def getNumbers(min: int, max: int) -> set: result = set() for i in range(min, max+1): nums = [i // 100000, (i // 10000) % 10, (i // 1000) % 10, @@ -13,28 +16,28 @@ def getNumbers(min, max): result.add(i) return result -def isNotDecreasing(nums): +def isNotDecreasing(nums: list) -> bool: for x in range(1, 6): if nums[x] < nums[x - 1]: return False return True -def hasDoubleAdjacentValue(nums): +def hasDoubleAdjacentValue(nums: list) -> bool: for x in range(1, 5): if nums[x] == nums[x - 1] or nums[x] == nums[x + 1]: return True return False -def isNotPartOfBiggerGroup(nums): +def isNotPartOfBiggerGroup(nums: list) -> bool: for x in range(1, 6): if nums[x] == nums[x - 1] and nums.count(nums[x]) == 2: return True return False -def part1(vals : list): +def part1(vals : list) -> int: return len(getNumbers(vals[0], vals[1])) -def part2(vals : list): +def part2(vals : list) -> int: result = set() numbers = getNumbers(vals[0], vals[1] + 1) for i in numbers: diff --git a/2019/05/code.py b/2019/05/code.py index 22d7076..2008a1b 100644 --- a/2019/05/code.py +++ b/2019/05/code.py @@ -1,105 +1,43 @@ -""" https://adventofcode.com/2019/day/5 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/5 -def readFile(): +import sys, os +sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import intcode + +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 parseCode(code): - DE = code % 100 - C = code // 100 % 10 - B = code // 1000 % 10 - A = code // 10000 % 10 - return (DE, C, B, A) +def part1(vals: list) -> int: + return intcode.getOutput(vals.copy(), input=1) -def getOutput(vals : list, input=0, getVals=False): - i = 0 - while 1: - opcode = parseCode(vals[i]) - # 99 : Termination - # 0 Parameter - if opcode[0] == 99: - if getVals: - return vals - return vals[0] - - # 1 : Addition - # 3 parameter - elif opcode[0] == 1: - load1 = vals[i+1] if opcode[1] == 0 else i+1 - load2 = vals[i+2] if opcode[2] == 0 else i+2 - vals[vals[i+3]] = vals[load1] + vals[load2] - i += 4 - - # 2 : Multiplication - # 3 parameter - elif opcode[0] == 2: - load1 = vals[i+1] if opcode[1] == 0 else i+1 - load2 = vals[i+2] if opcode[2] == 0 else i+2 - vals[vals[i+3]] = vals[load1] * vals[load2] - i += 4 - - # 3 : Input - # 1 parameter - elif opcode[0] == 3: - vals[vals[i+1]] = input - i += 2 - - # 4 : Output - # 1 parameter - elif opcode[0] == 4: - load = vals[i+1] if opcode[1] == 0 else i+1 - vals[0] = vals[load] - i += 2 - - # 5 : Jump-if-true - # 6 : Jump-if-false - # 2 parameter - elif opcode[0] in [5, 6]: - load1 = vals[i+1] if opcode[1] == 0 else i+1 - if opcode[0] == 5 and vals[load1] or \ - opcode[0] == 6 and not vals[load1]: - load2 = vals[i+2] if opcode[2] == 0 else i+2 - i = vals[load2] - else: - i += 3 - - # 7 : less-than - # 8 : equals - # 3 parameter - elif opcode[0] in [7, 8]: - load1 = vals[i+1] if opcode[1] == 0 else i+1 - load2 = vals[i+2] if opcode[2] == 0 else i+2 - vals[vals[i+3]] = 1 if opcode[0] == 7 and vals[load1] < vals[load2] or \ - opcode[0] == 8 and vals[load1] == vals[load2] else 0 - i += 4 - -def part1(vals : list): - return getOutput(vals.copy(), input=1) - -def part2(vals : list): - return getOutput(vals.copy(), input=5) +def part2(vals: list) -> int: + return intcode.getOutput(vals.copy(), input=5) def test(): - assert parseCode(1001) == (1, 0, 1, 0) - assert getOutput([1,0,0,0,99]) == 2 - assert getOutput([1,1,1,4,99,5,6,0,99]) == 30 - assert getOutput([3,0,4,0,99], input=42) == 42 - assert getOutput([1101,100,-1,4,0], getVals=True) == [1101,100,-1,4,99] - assert getOutput([3,9,8,9,10,9,4,9,99,-1,8], input=0) == 0 - assert getOutput([3,9,8,9,10,9,4,9,99,-1,8], input=8) == 1 - assert getOutput([3,9,7,9,10,9,4,9,99,-1,8], input=0) == 1 - assert getOutput([3,9,7,9,10,9,4,9,99,-1,8], input=8) == 0 - assert getOutput([3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9], input=0) == 0 - assert getOutput([3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9], input=1) == 1 - assert getOutput([3,3,1105,-1,9,1101,0,0,12,4,12,99,1], input=0) == 0 - assert getOutput([3,3,1105,-1,9,1101,0,0,12,4,12,99,1], input=1) == 1 - assert getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31, + 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 getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31, + 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 getOutput([3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31, + 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 diff --git a/2019/06/code.py b/2019/06/code.py index 69bcc90..a211a4d 100644 --- a/2019/06/code.py +++ b/2019/06/code.py @@ -1,16 +1,19 @@ -""" https://adventofcode.com/2019/day/6 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/6 -def readFile(): +def readFile() -> dict: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: lines = [data[:-1].split(")") for data in f.readlines()] - return {line[1] : line[0] for line in lines} + return {line[1]: line[0] for line in lines} -def countOrbits(data, cache, node): +def countOrbits(data: dict, cache: dict, node: str) -> int: if node in cache: return cache[node] cache[node] = 0 if node not in data else 1 + countOrbits(data,cache,data[node]) return cache[node] -def getIntersection(data, cache, node1, node2): +def getIntersection(data: dict, cache: dict, node1: str, node2: str) -> str: if cache[node1] > cache[node2]: node1, node2 = node2, node1 parents = set() # get elements of shorter path @@ -23,10 +26,10 @@ def getIntersection(data, cache, node1, node2): node2 = data[node2] return node2 -def part1(vals : dict, cache): +def part1(vals : dict, cache) -> int: return sum([countOrbits(vals,cache,val) for val in vals]) -def part2(vals : dict, cache): +def part2(vals : dict, cache) -> int: intersection = getIntersection(vals,cache,"YOU","SAN") return cache["YOU"] + cache["SAN"] - 2*cache[intersection] - 2 diff --git a/2019/07/amp.py b/2019/07/amp.py new file mode 100644 index 0000000..7a3ed62 --- /dev/null +++ b/2019/07/amp.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +import queue + +class InvalidOpcode(Exception): + pass + +class Amp: + def __init__(self, vals, phase): + self.vals = vals.copy() + self.phase = phase + self.queue = queue.Queue() + self.output, self.prev, self.next = None, None, None + self.phased, self.term = False, False + self.i = 0 + + def run(self): + while 1: + opcode = (self.vals[self.i] % 100, self.vals[self.i] // 100 % 10, + self.vals[self.i] // 1000 % 10, self.vals[self.i] // 10000 % 10) + + # 99 : Termination + # 0 Parameter + if opcode[0] == 99: + self.output = self.vals[0] + self.term = True + break + + # 1 : Addition + # 3 parameter + elif opcode[0] == 1: + a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 + b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 + self.vals[self.vals[self.i+3]] = self.vals[a] + self.vals[b] + self.i += 4 + + # 2 : Multiplication + # 3 parameter + elif opcode[0] == 2: + a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 + b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 + self.vals[self.vals[self.i+3]] = self.vals[a] * self.vals[b] + self.i += 4 + + # 3 : Input + # 1 parameter + elif opcode[0] == 3: + if self.phased: + try: + if self.prev and self.prev.output: + self.vals[self.vals[self.i+1]] = self.prev.output + else: + self.vals[self.vals[self.i+1]] = self.queue.get(block=False) + except queue.Empty: + break + else: + self.vals[self.vals[self.i+1]], self.phased = self.phase, True + self.i += 2 + + # 4 : Output + # 1 parameter + elif opcode[0] == 4: + a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 + self.vals[0] = self.vals[a] + if self.next: + self.next.queue.put(self.vals[0]) + self.i += 2 + + # 5 : Jump-if-true + # 6 : Jump-if-false + # 2 parameter + elif opcode[0] in [5, 6]: + a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 + if opcode[0] == 5 and self.vals[a] or \ + opcode[0] == 6 and not self.vals[a]: + b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 + self.i = self.vals[b] + else: + self.i += 3 + + # 7 : less-than + # 8 : equals + # 3 parameter + elif opcode[0] in [7, 8]: + a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 + b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 + self.vals[self.vals[self.i+3]] = 1 if opcode[0] == 7 and self.vals[a] < \ + self.vals[b] or opcode[0] == 8 and self.vals[a] == self.vals[b] else 0 + self.i += 4 + + else: + raise InvalidOpcode \ No newline at end of file diff --git a/2019/07/code.py b/2019/07/code.py index 8188cec..7e13f19 100644 --- a/2019/07/code.py +++ b/2019/07/code.py @@ -1,101 +1,16 @@ -""" https://adventofcode.com/2019/day/7 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/7 -import queue, itertools +from itertools import permutations +from amp import Amp -class InvalidOpcode(Exception): - pass - -def readFile(): +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [int(num) for num in f.readline().split(",")] -class Amp: - def __init__(self, vals, phase): - self.vals = vals.copy() - self.phase = phase - self.queue = queue.Queue() - self.output, self.prev, self.next = None, None, None - self.phased, self.term = False, False - self.i = 0 - - def run(self): - while 1: - opcode = (self.vals[self.i] % 100, self.vals[self.i] // 100 % 10, - self.vals[self.i] // 1000 % 10, self.vals[self.i] // 10000 % 10) - - # 99 : Termination - # 0 Parameter - if opcode[0] == 99: - self.output = self.vals[0] - self.term = True - break - - # 1 : Addition - # 3 parameter - elif opcode[0] == 1: - a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 - b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 - self.vals[self.vals[self.i+3]] = self.vals[a] + self.vals[b] - self.i += 4 - - # 2 : Multiplication - # 3 parameter - elif opcode[0] == 2: - a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 - b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 - self.vals[self.vals[self.i+3]] = self.vals[a] * self.vals[b] - self.i += 4 - - # 3 : Input - # 1 parameter - elif opcode[0] == 3: - if self.phased: - try: - if self.prev and self.prev.output: - self.vals[self.vals[self.i+1]] = self.prev.output - else: - self.vals[self.vals[self.i+1]] = self.queue.get(block=False) - except queue.Empty: - break - else: - self.vals[self.vals[self.i+1]], self.phased = self.phase, True - self.i += 2 - - # 4 : Output - # 1 parameter - elif opcode[0] == 4: - a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 - self.vals[0] = self.vals[a] - if self.next: - self.next.queue.put(self.vals[0]) - self.i += 2 - - # 5 : Jump-if-true - # 6 : Jump-if-false - # 2 parameter - elif opcode[0] in [5, 6]: - a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 - if opcode[0] == 5 and self.vals[a] or \ - opcode[0] == 6 and not self.vals[a]: - b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 - self.i = self.vals[b] - else: - self.i += 3 - - # 7 : less-than - # 8 : equals - # 3 parameter - elif opcode[0] in [7, 8]: - a = self.vals[self.i+1] if opcode[1] == 0 else self.i+1 - b = self.vals[self.i+2] if opcode[2] == 0 else self.i+2 - self.vals[self.vals[self.i+3]] = 1 if opcode[0] == 7 and self.vals[a] < \ - self.vals[b] or opcode[0] == 8 and self.vals[a] == self.vals[b] else 0 - self.i += 4 - - else: - raise InvalidOpcode - -def amplify(vals : list, phaseSettings : tuple): +def amplify(vals: list, phaseSettings: tuple) -> int: amps = [Amp(vals, phaseSettings[i]) for i in range(5)] amps[0].queue.put(0) @@ -117,11 +32,11 @@ def amplify(vals : list, phaseSettings : tuple): return amps[4].output -def part1(vals : list): - return max([amplify(vals, (a, b, c, d, e)) for a,b,c,d,e in itertools.permutations([x for x in range(5)])]) +def part1(vals: list) -> int: + return max([amplify(vals, (a, b, c, d, e)) for a,b,c,d,e in permutations([x for x in range(5)])]) -def part2(vals : list): - return max([amplify(vals, (a, b, c, d, e)) for a,b,c,d,e in itertools.permutations([x for x in range(5,10)])]) +def part2(vals: list) -> int: + return max([amplify(vals, (a, b, c, d, e)) for a,b,c,d,e in permutations([x for x in range(5,10)])]) def test(): vals = [3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0] diff --git a/2019/08/code.py b/2019/08/code.py index fdbbec7..0db77b8 100644 --- a/2019/08/code.py +++ b/2019/08/code.py @@ -1,25 +1,29 @@ -""" https://adventofcode.com/2019/day/8 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/8 -def readFile(): +def readFile() -> str: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return f.read()[:-1] -def getLayers(input, width, height): +def getLayers(input: str, width: int, height: int) -> list: layers = [] for i in range(0, len(input), width*height): layers.append([input[i+width*x:i+width*(x+1)] for x in range(height)]) return layers -def getPicture(layers): +def getPicture(layers: list) -> str: width, height = len(layers[0][0]), len(layers[0]) return "\n".join(["".join([getColor(layers, w, h) for w in range(width)]) for h in range(height)]) -def getColor(layers, w, h): +def getColor(layers: list, w: int, h: int) -> str: for layer in layers: - if layer[h][w] != "2": return layer[h][w] + if layer[h][w] != "2": + return layer[h][w] return "2" -def part1(layers): +def part1(layers: list) -> int: min, minLayer = None, None for layer in layers: cnt = sum([l.count("0") for l in layer]) @@ -27,7 +31,7 @@ def part1(layers): min, minLayer = cnt, layer return sum([l.count("1") for l in minLayer]) * sum([l.count("2") for l in minLayer]) -def part2(layers): +def part2(layers: list) -> str: picture = getPicture(layers) return f"\n{picture.replace('0', ' ').replace('1', 'X')}" diff --git a/2019/09/code.py b/2019/09/code.py index 210d44c..724cbd0 100644 --- a/2019/09/code.py +++ b/2019/09/code.py @@ -1,92 +1,25 @@ -""" https://adventofcode.com/2019/day/9 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/9 -class InvalidOpcode(Exception): - pass +import sys, os +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() -> list: 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 - while 1: - opcode = (vals[i] % 100, - vals[i] // 100 % 10, - vals[i] // 1000 % 10, - vals[i] // 10000 % 10) +def part1(vals: list) -> int: + return intcode.getOutput(vals.copy(), input=1) - # 0 Parameter - if opcode[0] in [99]: # Termination - return vals - - # 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] - 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 - - else: - raise InvalidOpcode() - -def part1(vals : list): - return getOutput(vals.copy(), input=1)[0] - -def part2(vals : list): - return getOutput(vals.copy(), input=2)[0] +def part2(vals: list) -> int: + return intcode.getOutput(vals.copy(), input=2) def test(): - assert getOutput([104,1125899906842624,99])[0] == 1125899906842624 - assert len(getOutput([109,19,204,-34,99], base=2000)) == 1986 - assert len(str(getOutput([1102,34915192,34915192,7,4,7,99,0])[0])) == 16 + assert intcode.getOutput([104,1125899906842624,99]) == 1125899906842624 + assert len(str(intcode.getOutput([1102,34915192,34915192,7,4,7,99,0]))) == 16 if __name__ == "__main__": test() diff --git a/2019/10/asteroid.py b/2019/10/asteroid.py new file mode 100644 index 0000000..0df2cd1 --- /dev/null +++ b/2019/10/asteroid.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +from math import atan2, sqrt + +class Asteroid: + def __init__(self, x: int, y: int, ox: int, oy: int): + self.x = x + self.y = y + self.dist = sqrt((x - ox)**2 + (y - oy)**2) + self.angle = atan2(y - oy, x - ox) + + def __str__(self): + return str((self.x, self.y)) \ No newline at end of file diff --git a/2019/10/code.py b/2019/10/code.py index dea86f4..ad356ac 100644 --- a/2019/10/code.py +++ b/2019/10/code.py @@ -1,31 +1,25 @@ -""" https://adventofcode.com/2019/day/10 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/10 -from math import atan2, sqrt, pi +from asteroid import Asteroid +from math import pi -def readFile(): +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [line[:-1] for line in f.readlines()] -class Asteroid: - def __init__(self, x: int, y: int, ox: int, oy: int): - self.x = x - self.y = y - self.dist = sqrt((x - ox)**2 + (y - oy)**2) - self.angle = atan2(y - oy, x - ox) - - def __str__(self): - return str((self.x, self.y)) - -def getAsteroids(field: list, x: int, y: int): +def getAsteroids(field: list, x: int, y: int) -> list: return [Asteroid(i,j,x,y) for j in range(len(field)) for i in range(len(field[j])) if field[j][i] != "." and (i,j) != (x,y)] -def getVisibleAsteroids(field: list, x: int, y: int): +def getVisibleAsteroids(field: list, x: int, y: int) -> int: asteroids = getAsteroids(field, x, y) angles = set([asteroid.angle for asteroid in asteroids]) return len(angles) -def part1(vals: list): +def part1(vals: list) -> tuple: mx, my, max = 0, 0, 0 for y in range(len(vals)): for x in range(len(vals[0])): @@ -36,14 +30,13 @@ def part1(vals: list): mx, my, max = x, y, tmp return mx, my, max -def selectStart(indices, current): +def selectStart(indices: list, current: float) -> int: i = 0 while indices[i] < current: i += 1 return i + 1 - -def part2(vals: list, x: int, y: int, count): +def part2(vals: list, x: int, y: int, count: int) -> int: asteroids = getAsteroids(vals, x, y) d = dict() for asteroid in asteroids: diff --git a/2019/12/code.py b/2019/12/code.py index 0a01d21..370517e 100644 --- a/2019/12/code.py +++ b/2019/12/code.py @@ -1,36 +1,17 @@ -""" https://adventofcode.com/2019/day/12 """ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic +# +# https://adventofcode.com/2019/day/12 from itertools import combinations +from moon import Moon -def readFile(): +def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: lines = [line[1:-2].replace(" ", "").split(",") for line in f.readlines()] return [(int(line[0][2:]), int(line[1][2:]), int(line[2][2:])) for line in lines] -def cmp(a,b): - return 0 if a == b else -1 if a < b else 1 - -class Moon: - def __init__(self, pos): - self.pos = list(pos) - self.vel = [0] * 3 - - def changeGravity(self, other): - for i in range(3): - d = cmp(self.pos[i], other.pos[i]) - self.vel[i] -= d - other.vel[i] += d - - def move(self): - for i in range(3): - self.pos[i] += self.vel[i] - - def energy(self): - pot = sum([abs(p) for p in self.pos]) - kin = sum([abs(v) for v in self.vel]) - return pot, kin, pot * kin - -def simulate(moons : list, steps = 1): +def simulate(moons: list, steps: int = 1): for i in range(steps): for m, n in list(combinations(moons, 2)): m.changeGravity(n) @@ -38,24 +19,24 @@ def simulate(moons : list, steps = 1): for moon in moons: moon.move() -def lcm(x, y): +def lcm(x: int, y: int) -> int: tmp = x * y while y: x, y = y, x % y return tmp // x -def part1(vals: list): +def part1(vals: list) -> int: moons = [Moon(val) for val in vals] simulate(moons, 1000) return sum([moon.energy()[2] for moon in moons]) -def part2(vals: list): +def part2(vals: list) -> int: dims = [0, 0, 0] for i in range(3): moons = [Moon(val) for val in vals] oPos = [moon.pos[i] for moon in moons] - while 1: + while True: simulate(moons) dims[i] += 1 if [moon.pos[i] for moon in moons] == oPos and \ diff --git a/2019/12/moon.py b/2019/12/moon.py new file mode 100644 index 0000000..b7b23e6 --- /dev/null +++ b/2019/12/moon.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 Akumatic + +def cmp(a: int, b: int) -> int: + return 0 if a == b else -1 if a < b else 1 + +class Moon: + def __init__(self, pos): + self.pos = list(pos) + self.vel = [0] * 3 + + def changeGravity(self, other): + for i in range(3): + d = cmp(self.pos[i], other.pos[i]) + self.vel[i] -= d + other.vel[i] += d + + def move(self): + for i in range(3): + self.pos[i] += self.vel[i] + + def energy(self): + pot = sum([abs(p) for p in self.pos]) + kin = sum([abs(v) for v in self.vel]) + return pot, kin, pot * kin \ No newline at end of file diff --git a/2019/intcode.py b/2019/intcode.py new file mode 100644 index 0000000..fbc7b4a --- /dev/null +++ b/2019/intcode.py @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: MIT +# Copyright (c) 2019 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 parseCode(i: int) -> tuple: + return (i % 100, i // 100 % 10, i // 1000 % 10, i // 10000 % 10) + +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] + + # 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 + + else: + raise InvalidOpcode() \ No newline at end of file diff --git a/2020/README.md b/2020/README.md index b9259ef..cc0109d 100644 --- a/2020/README.md +++ b/2020/README.md @@ -15,7 +15,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day | Day | Part 1 | Part 2 ||Day | Part 1 | Part 2 | | --- | --- | --- |---| --- | --- | --- | -| 01 | :white_check_mark: | :white_check_mark: || 02 | | | +| 01 | :white_check_mark: | :white_check_mark: || 02 | :white_check_mark: | :white_check_mark: | | 03 | | || 04 | | | | 05 | | || 06 | | | | 07 | | || 08 | | |