#!/usr/bin/env python3 from typing import TextIO import re schematic: list[str] = [] # line {int: {(a,b): num}, ...} gears: dict[int, dict[tuple[int, int], int]] = {} def get_adjacent(l: int, co: tuple[int, int]) -> bool: for i in range(co[0], co[1]): # check below if ( l + 1 < len(schematic) and not schematic[l + 1][i].isdigit() and schematic[l + 1][i] != "." ): return True # check above if l != 0 and not schematic[l - 1][i].isdigit() and schematic[l - 1][i] != ".": return True # if first num check left if ( i != 0 and i == co[0] and not schematic[l][i - 1].isdigit() and schematic[l][i - 1] != "." ): return True # if last num check right if ( i + 1 < len(schematic[l]) and i == co[1] - 1 and not schematic[l][i + 1].isdigit() and schematic[l][i + 1] != "." ): return True # all check diag # U L if ( l != 0 and i != 0 and not schematic[l - 1][i - 1].isdigit() and schematic[l - 1][i - 1] != "." ): return True # U R if ( l != 0 and i + 1 < len(schematic[l]) and not schematic[l - 1][i + 1].isdigit() and schematic[l - 1][i + 1] != "." ): return True # D L if ( l + 1 < len(schematic) and i != 0 and not schematic[l + 1][i - 1].isdigit() and schematic[l + 1][i - 1] != "." ): return True # D R if ( l + 1 < len(schematic) and i + 1 < len(schematic[l]) and not schematic[l + 1][i + 1].isdigit() and schematic[l + 1][i + 1] != "." ): return True return False def part1(file: TextIO) -> int: sum_: int = 0 for line in file: schematic.append(line.strip()) for l in range(len(schematic)): nums = re.finditer(r"\d+", schematic[l]) for n in nums: if l in gears: gears[l][n.span()] = int(n.group(0)) else: gears[l] = {n.span(): int(n.group(0))} if get_adjacent(l, n.span()): sum_ += int(n.group(0)) return sum_ def find_num(l, c) -> tuple[int, tuple[int, int, int]]: for coord in gears[l]: if c in range(coord[0], coord[1] + 1): return ( gears[l][coord], (l, coord[0], coord[1]), ) # line pos (same coordinates on 2 diff lines) raise ValueError def part2() -> int: ratios: int = 0 for l in range(len(schematic)): for c in range(len(schematic[l])): if schematic[l][c] == "*": nums: list[int] = [] used_c: list[tuple[int, int, int]] = [] # start parsing for digits # check below if l + 1 < len(schematic) and schematic[l + 1][c].isdigit(): a = find_num(l + 1, c) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # check above if l != 0 and schematic[l - 1][c].isdigit(): a = find_num(l - 1, c) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # check left if c != 0 and schematic[l][c - 1].isdigit(): a = find_num(l, c - 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # check right if c + 1 < len(schematic[l]) and schematic[l][c + 1].isdigit(): a = find_num(l, c + 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # all check diag # U L if l != 0 and c != 0 and schematic[l - 1][c - 1].isdigit(): a = find_num(l - 1, c - 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # U R if ( l != 0 and c + 1 < len(schematic[l]) and schematic[l - 1][c + 1].isdigit() ): a = find_num(l - 1, c + 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # D L if ( l + 1 < len(schematic) and c != 0 and schematic[l + 1][c - 1].isdigit() ): a = find_num(l + 1, c - 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) # D R if ( l + 1 < len(schematic) and c + 1 < len(schematic[l]) and schematic[l + 1][c + 1].isdigit() ): a = find_num(l + 1, c + 1) if a[1] not in used_c: nums.append(a[0]) used_c.append(a[1]) if len(nums) == 2: ratios += nums[0] * nums[1] if len(nums) > 2: raise ValueError return ratios if __name__ == "__main__": with open("day03.txt") as f: print(part1(f)) print(part2()) # not 75089990 too low