diff --git a/day01.py b/day01.py index 0ed6581..59549f1 100644 --- a/day01.py +++ b/day01.py @@ -1,6 +1,19 @@ from tools.aoc import AOCDay +def get_fuel(count: int, part2: bool = False): + if not part2: + return count // 3 - 2 + else: + fuel_sum = 0 + count = count // 3 - 2 + while count > 0: + fuel_sum += count + count = count // 3 - 2 + + return fuel_sum + + class Day(AOCDay): inputs = [ [ @@ -18,22 +31,21 @@ class Day(AOCDay): ] ] - def part1(self): - fuel_sum = 0 - for x in self.getInputListAsType(int): - fuel_sum += x // 3 - 2 + def get_fuel(self, part2: bool = False): + if len(self.input) == 1: + return get_fuel(self.getInput(int), part2) + else: + fuel_sum = 0 + for x in self.getInput(int): + fuel_sum += get_fuel(x, part2) - return fuel_sum + return fuel_sum + + def part1(self): + return self.get_fuel() def part2(self): - fuel_sum = 0 - for x in self.getInputListAsType(int): - fuel_add = x // 3 - 2 - while fuel_add > 0: - fuel_sum += fuel_add - fuel_add = fuel_add // 3 - 2 - - return fuel_sum + return self.get_fuel(True) if __name__ == '__main__': diff --git a/day07.py b/day07.py index 64206e0..9dd00ed 100644 --- a/day07.py +++ b/day07.py @@ -7,7 +7,7 @@ from typing import Any, List def get_comps(memory: List[int]) -> List[IntCode]: comps = [] for _ in range(5): - comps.append(IntCode(memory.copy())) + comps.append(IntCode(memory)) return comps @@ -41,7 +41,7 @@ class Day(AOCDay): comps[i].addInput(input1) comps[i].addInput(input2) - comps[i].run() + comps[i].start() signal = comps[4].getOutput() if signal > max_signal: @@ -54,7 +54,7 @@ class Day(AOCDay): max_signal = 0 phase_settings = [5, 6, 7, 8, 9] for s in itertools.permutations(phase_settings): - comps = get_comps(init_memory.copy()) + comps = get_comps(init_memory) for i, input1 in enumerate(s): if i == 0 and not comps[i].isRunning(): diff --git a/day11.py b/day11.py index ba5b584..00279ca 100644 --- a/day11.py +++ b/day11.py @@ -24,12 +24,12 @@ class Day(AOCDay): hull = Grid() face = 0 pos = Coordinate(0, 0) - painted = {} + painted = set() while not comp.isHalted(): - comp.addInput(hull.isSet(pos)) + comp.addInput(int(hull.isSet(pos))) hull.set(pos, comp.getOutput()) - painted[pos] = True + painted.add(pos) face += 1 if comp.getOutput() else -1 pos = pos + MOVEMENTS[face % 4] @@ -50,13 +50,7 @@ class Day(AOCDay): face += 1 if comp.getOutput() else -1 pos = pos + MOVEMENTS[face % 4] - hull.transform(GridTransformation.ROTATE_RIGHT) - hull.transform(GridTransformation.FLIP_HORIZONTALLY) - for x in range(hull.minX, hull.maxX + 1): - for y in range(hull.minY, hull.maxY + 1): - print("#" if hull.isSet(Coordinate(x, y)) else " ", end="") - - print() + hull.print(true_char='#', false_char=' ') return "see image above" diff --git a/day12.py b/day12.py index 17dcc9c..8a67b3b 100644 --- a/day12.py +++ b/day12.py @@ -20,10 +20,20 @@ class Moon: self.y = y self.z = z + def getKineticEnergy(self): + return abs(self.vel_x) + abs(self.vel_y) + abs(self.vel_z) + + def getPotentialEnergy(self): + return abs(self.x) + abs(self.y) + abs(self.z) + def getEnergy(self): - potential = abs(self.x) + abs(self.y) + abs(self.z) - kinetic = abs(self.vel_x) + abs(self.vel_y) + abs(self.vel_z) - return potential * kinetic + return self.getPotentialEnergy() * self.getKineticEnergy() + + def __str__(self): + return "x=%3d y=%3d z=%3d" % (self.x, self.y, self.z) + + def __repr__(self): + return str(self) def moveMoons(moons: List[Moon]): @@ -50,7 +60,7 @@ class Day(AOCDay): (12644, "input12") ], [ - # ??? (2772, "test_input12"), + (2772, "test_input12"), (4686774924, "test_input12_2"), (None, "input12") ] @@ -66,21 +76,20 @@ class Day(AOCDay): def part2(self) -> Any: moons = [Moon(*map(int, findall(scanline_regex, line)[0])) for line in self.getInput()] - - init_moons = moons.copy() - vel_0_pos = [0, 0, 0, 0] + init_moons = [Moon(*map(int, findall(scanline_regex, line)[0])) for line in self.getInput()] + vel_0_pos = [0] * len(moons) moveCounter = 0 - #while 0 in vel_0_pos: - for bla in range(3000): + while 0 in vel_0_pos: moveCounter += 1 moveMoons(moons) for i, m in enumerate(moons): - if m.x == init_moons[i].x and m.y == init_moons[i].y and m.z == init_moons[i].z and m.vel_x == 0 and m.vel_y == 0 and m.vel_z == 0: - print(i, moveCounter) - if (vel_0_pos[i] == 0 or vel_0_pos[i] > moveCounter - vel_0_pos[i]) and m.vel_x == 0 and m.vel_y == 0 and m.vel_z == 0: - vel_0_pos[i] = moveCounter - vel_0_pos[i] + if m.x == init_moons[i].x and m.y == init_moons[i].y and m.z == init_moons[i].z: + # if i == 1: + # print("init pos:", i, "after", moveCounter, "moves =>", m.getPotentialEnergy(), m.getKineticEnergy()) + if m.getKineticEnergy() == 0: + print("init state:", i, "after", moveCounter, "moves =>", m.getPotentialEnergy()) + vel_0_pos[i] = moveCounter - print(vel_0_pos) return lcm(*vel_0_pos) diff --git a/intcode.py b/intcode.py index 55185fc..4ff0efb 100644 --- a/intcode.py +++ b/intcode.py @@ -1,7 +1,8 @@ import threading +import time +from collections import deque, defaultdict from enum import Enum -from queue import Queue -from typing import List, Union +from typing import List class IntCodeState(Enum): @@ -10,26 +11,35 @@ class IntCodeState(Enum): HALTED = 2 +def _get_from_queue(queue: deque, count: int = 1, timeout: int = 10) -> int | List[int] | None: + started = time.perf_counter() + while len(queue) < count: + time.sleep(0.0001) + if time.perf_counter() - started > timeout: + raise TimeoutError() + + if count == 1: + return queue.popleft() + else: + return [queue.popleft() for _ in range(count)] + + class IntCode(threading.Thread): def __init__(self, memory: List[int]): super().__init__() self.instr_ptr = 0 - self.memory = memory - self.input_queue = Queue() - self.output_queue = Queue() + self.memory = defaultdict(int) + for i, v in enumerate(memory): + self.memory[i] = v + self.input_queue = deque() + self.output_queue = deque() self.state = IntCodeState.INITIALIZED self.relative_base = 0 def getMemoryValue(self, address: int) -> int: - if address > len(self.memory) - 1: - self.memory.extend([0] * (address - len(self.memory) + 1)) - return self.memory[address] def setMemoryValue(self, address: int, value: int): - if address > len(self.memory) - 1: - self.memory.extend([0] * (address - len(self.memory) + 1)) - self.memory[address] = value def getParameterValue(self, address: int, mode: int = 0) -> int: @@ -52,26 +62,20 @@ class IntCode(threading.Thread): def isHalted(self): return self.state == IntCodeState.HALTED - def sendOutputTo(self, target_queue: Queue): + def sendOutputTo(self, target_queue: deque): self.output_queue = target_queue def addInput(self, value: int): - self.input_queue.put(value) + self.input_queue.append(value) def hasOutput(self): - return self.output_queue.qsize() > 0 + return len(self.output_queue) > 0 - def getOutput(self, count: int = 1, block: bool = True, timeout: int = None) -> Union[int, List[int]]: - if count == 1: - res = self.output_queue.get(block=block, timeout=timeout) - self.output_queue.task_done() - else: - res = [] - for _ in range(count): - res.append(self.output_queue.get(block=block, timeout=timeout)) - self.output_queue.task_done() + def getOutput(self, count: int = 1, timeout: int = 10) -> int | List[int] | None: + return _get_from_queue(self.output_queue, count, timeout) - return res + def getInput(self, count: int = 1, timeout: int = 10) -> int | List[int] | None: + return _get_from_queue(self.input_queue, count, timeout) def run(self): self.state = IntCodeState.RUNNING @@ -96,11 +100,10 @@ class IntCode(threading.Thread): self.instr_ptr += 4 elif op_code == 3: # input target_addr = self.getTargetAddress(self.instr_ptr + 1, p1_mode) - self.setMemoryValue(target_addr, self.input_queue.get(timeout=30)) - self.input_queue.task_done() + self.setMemoryValue(target_addr, self.getInput()) self.instr_ptr += 2 elif op_code == 4: # output - self.output_queue.put(self.getParameterValue(self.instr_ptr + 1, p1_mode)) + self.output_queue.append(self.getParameterValue(self.instr_ptr + 1, p1_mode)) self.instr_ptr += 2 elif op_code == 5: # jump-if-true if self.getParameterValue(self.instr_ptr + 1, p1_mode) != 0: