diff --git a/aoc/aoc.py b/aoc/aoc.py index 39ea1ff..baf185f 100644 --- a/aoc/aoc.py +++ b/aoc/aoc.py @@ -4,6 +4,7 @@ from typing import Any, Generic, Protocol, TypeVar from urllib.request import Request, urlopen from html.parser import HTMLParser from importlib import import_module +from timeit import default_timer T1 = TypeVar("T1") T2 = TypeVar("T2") @@ -187,7 +188,10 @@ def run_latest() -> None: ] day = days[-1] print(f"Running {day.__name__}") + s_time = default_timer() day.main() + e_time = default_timer() + print(f"Time: {e_time-s_time}") def main() -> None: @@ -196,10 +200,16 @@ def main() -> None: for file in sorted(Path("aoc").iterdir()) if file.name.endswith(".py") and file.name.startswith("day") ] + b_time = default_timer() for day in days: print(f"Running {day.__name__}") + s_time = default_timer() day.main() + e_time = default_timer() + print(f"Time: {e_time-s_time}") print() + f_time = default_timer() + print(f"Overall time: {f_time - b_time}") if __name__ == "__main__": diff --git a/aoc/day08.py b/aoc/day08.py index b9caef3..0509020 100644 --- a/aoc/day08.py +++ b/aoc/day08.py @@ -44,21 +44,19 @@ class Day08(AocSameParser[Map], Aoc2[Map, Map]): def part2(self, inpt: Map) -> int: current = [node for node in inpt.rules.keys() if node.endswith("A")] - steps_to_z: list[list[int]] = [[] for _ in current] - loops: list[Optional[tuple[int, int]]] = [None for _ in current] - - for steps, direction in enumerate(itertools.cycle(inpt.directions)): + steps_to_z = [0] * len(current) + for direction in itertools.cycle(inpt.directions): + dirty = False for n, node in enumerate(current): if node.endswith("Z"): - steps_to_z[n].append(steps) - loops[n] = self.find_loop(steps_to_z[n]) - - if not any(loop is None for loop in loops): + continue + dirty = True + steps_to_z[n] += 1 + current[n] = inpt.rules[node][direction] + if not dirty: break - current = [inpt.rules[node][direction] for node in current] - # Turns out, each path directly loops :/ - return math.lcm(*(loop[0] for loop in loops if loop is not None)) + return math.lcm(*steps_to_z) def main() -> None: diff --git a/aoc/day11.py b/aoc/day11.py index 7f2571d..5eb3aae 100644 --- a/aoc/day11.py +++ b/aoc/day11.py @@ -1,8 +1,9 @@ from __future__ import annotations from array import array -from collections.abc import Iterable +from collections.abc import Callable, Iterable from dataclasses import dataclass +from itertools import accumulate, combinations from .aoc import Aoc2, AocSameParser @@ -64,43 +65,50 @@ class Matrix: return "\n".join((x.tounicode() for x in self.iter_rows())) def finditer(self, element: str) -> Iterable[Position]: - for y, line in enumerate(self.iter_rows()): - for x, char in enumerate(line): - if char == element: - yield Position(x, y) + return ( + Position(x, y) + for y, line in enumerate(self.iter_rows()) + for x, char in enumerate(line) + if char == element + ) + + def filter_row_ids(self, predicate: Callable[[array[str]], bool]) -> Iterable[int]: + return (y for y, row in enumerate(self.iter_rows()) if predicate(row)) + + def filter_col_ids(self, predicate: Callable[[array[str]], bool]) -> Iterable[int]: + return (x for x, col in enumerate(self.iter_cols()) if predicate(col)) @dataclass class Map: map: Matrix - galaxies: list[Position] - adjusted_galaxies: list[Position] - large_adjusted_galaxies: list[Position] + galaxies: Iterable[tuple[Position, Position]] @classmethod def from_input(cls, inpt: str) -> Map: matrix = Matrix(inpt) + missing_xs = array( + "I", + accumulate([1 if col.count("#") == 0 else 0 for col in matrix.iter_cols()]), + ) - galaxies = list(matrix.finditer("#")) - missing_ys = [ - y for y, row in enumerate(matrix.iter_rows()) if row.count("#") == 0 - ] - missing_xs = [ - x for x, col in enumerate(matrix.iter_cols()) if col.count("#") == 0 - ] + missing_ys = array( + "I", + accumulate([1 if row.count("#") == 0 else 0 for row in matrix.iter_rows()]), + ) - adjusted_galaxies = [] - large_adjusted_galaxies = [] - - for pos in galaxies: - offset = Position( - len([m for m in missing_xs if m < pos.x]), - len([m for m in missing_ys if m < pos.y]), + offset_galaxies = ( + ( + Position( + missing_xs[pos.x], + missing_ys[pos.y], + ), + pos, ) - adjusted_galaxies.append(pos + offset) - large_adjusted_galaxies.append(pos + 999_999 * offset) + for pos in matrix.finditer("#") + ) - return cls(matrix, galaxies, adjusted_galaxies, large_adjusted_galaxies) + return cls(matrix, offset_galaxies) class Day11(AocSameParser[Map], Aoc2[Map, Map]): @@ -109,16 +117,13 @@ class Day11(AocSameParser[Map], Aoc2[Map, Map]): def part1(self, inpt: Map) -> int: return sum( - g1 | g2 - for i, g1 in enumerate(inpt.adjusted_galaxies) - for g2 in inpt.adjusted_galaxies[i + 1 :] + g1 | g2 for g1, g2 in combinations(((o + g) for o, g in inpt.galaxies), 2) ) def part2(self, inpt: Map) -> int: return sum( g1 | g2 - for i, g1 in enumerate(inpt.large_adjusted_galaxies) - for g2 in inpt.large_adjusted_galaxies[i + 1 :] + for g1, g2 in combinations(((999_999 * o + g) for o, g in inpt.galaxies), 2) )