diff --git a/2023/day03.py b/2023/day03.py new file mode 100644 index 0000000..afb710b --- /dev/null +++ b/2023/day03.py @@ -0,0 +1,190 @@ +#!/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 diff --git a/2023/day03_test.py b/2023/day03_test.py new file mode 100644 index 0000000..354166c --- /dev/null +++ b/2023/day03_test.py @@ -0,0 +1,10 @@ +from day03 import * + + +def test_part1() -> None: + with open("day03.txt") as f: + assert part1(f) == 546312 + + +def test_part2() -> None: + assert part2() == 87449461