aoc2023/aoc/day03.py

99 lines
3.1 KiB
Python

from collections import defaultdict
from dataclasses import dataclass
from functools import reduce
from typing import Any, Optional
from .aoc import Aoc2, AocSameParser
@dataclass
class Number:
value: int
symbol: Optional[tuple[int, int, str]]
startx: int
endx: int
linenr: int
class Day03(AocSameParser[list[Number]], Aoc2[list[Number], 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 not in (endx, startx - 1):
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()