diff --git a/aoc.py b/aoc.py index 4f90fc9..20738e8 100644 --- a/aoc.py +++ b/aoc.py @@ -125,7 +125,7 @@ class Aoc(Generic[T1], Aoc2[T1, T1]): return self.part1(inpt) -def main(): +def main() -> None: days = [ import_module(file.stem) for file in Path(".").iterdir() diff --git a/day03.py b/day03.py new file mode 100644 index 0000000..39a2d6e --- /dev/null +++ b/day03.py @@ -0,0 +1,99 @@ +from collections import defaultdict +from dataclasses import dataclass +from functools import reduce +from typing import Any, Optional +from aoc import Aoc + + +@dataclass +class Number: + value: int + symbol: Optional[tuple[int, int, str]] + startx: int + endx: int + linenr: int + + +class Day03(Aoc[list[Number]]): + def __init__(self) -> None: + super().__init__(2023, 3) + + def get_symbol( + self, inpt: list[str], startx: int, endx: int, liney: int + ) -> Optional[tuple[int, int, str]]: + symbol = None + for x in range(max(0, startx - 1), min(len(inpt[0]), endx + 1)): + yrange = [liney - 1, liney, liney + 1] + if x != startx - 1 and x != endx: + yrange.remove(liney) + if liney == 0: + yrange.remove(liney - 1) + if liney == len(inpt) - 1: + yrange.remove(liney + 1) + for y in yrange: + try: + if inpt[y][x] != "." and not inpt[y][x].isnumeric(): + symbol = (x, y, inpt[y][x]) + except IndexError: + pass + return symbol + + def parseinput(self, inpt: str) -> list[Number]: + numbers = [] + lines = inpt.splitlines() + for posy, line in enumerate(lines): + num_accum = None + firstx = 0 + lastx = 0 + for posx, char in enumerate(line): + if char.isnumeric(): + if num_accum is None: + num_accum = char + firstx = posx + else: + num_accum += char + else: + if num_accum is not None: + lastx = posx + symbol = self.get_symbol(lines, firstx, lastx, posy) + numbers.append( + Number(int(num_accum), symbol, firstx, lastx, posy) + ) + + num_accum = None + firstx = 0 + lastx = 0 + + if num_accum is not None: + lastx = len(line) + symbol = self.get_symbol(lines, firstx, lastx, posy) + numbers.append(Number(int(num_accum), symbol, firstx, lastx, posy)) + + num_accum = None + firstx = 0 + lastx = 0 + + return numbers + + def part1(self, inpt: list[Number]) -> int: + return sum([number.value for number in inpt if number.symbol is not None]) + + def part2(self, inpt: list[Number]) -> Any: + gears_to_number = defaultdict(list) + for number in inpt: + if number.symbol is not None and number.symbol[2] == "*": + gears_to_number[number.symbol].append(number.value) + return sum( + reduce(lambda x, y: x * y, numbers) + for numbers in gears_to_number.values() + if len(numbers) == 2 + ) + + +def main() -> None: + day03 = Day03() + day03.run() + + +if __name__ == "__main__": + main()