diff --git a/README.md b/README.md index 478fb9a..76f390d 100644 --- a/README.md +++ b/README.md @@ -4,30 +4,30 @@ ## Progress -| Day | Name | Part 1 | Part 2 | -| :-: | :------------------ | :----: | :----: | -| 1 | | ⭐ | ⭐ | -| 2 | | ⭐ | ⭐ | -| 3 | | ⭐ | | -| 4 | | | | -| 5 | | | | -| 6 | | | | -| 7 | | | | -| 8 | | | | -| 9 | | | | -| 10 | | | | -| 11 | | | | -| 12 | | | | -| 13 | | | | -| 14 | | | | -| 15 | | | | -| 16 | | | | -| 17 | | | | -| 18 | | | | -| 19 | | | | -| 20 | | | | -| 21 | | | | -| 22 | | | | -| 23 | | | | -| 24 | | | | -| 25 | | | | +| Day | Name | Part 1 | Part 2 | +| :-: | :--- | :----: | :----: | +| 1 | | ⭐ | ⭐ | +| 2 | | ⭐ | ⭐ | +| 3 | | ⭐ | ⭐ | +| 4 | | | | +| 5 | | | | +| 6 | | | | +| 7 | | | | +| 8 | | | | +| 9 | | | | +| 10 | | | | +| 11 | | | | +| 12 | | | | +| 13 | | | | +| 14 | | | | +| 15 | | | | +| 16 | | | | +| 17 | | | | +| 18 | | | | +| 19 | | | | +| 20 | | | | +| 21 | | | | +| 22 | | | | +| 23 | | | | +| 24 | | | | +| 25 | | | | diff --git a/requirements.txt b/requirements.txt index 9af19a6..a96ddec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,6 @@ +iniconfig==2.3.0 +packaging==26.3 +pluggy==1.6.0 +Pygments==2.21.0 +pytest==9.1.1 ruff==0.16.4 diff --git a/src/modules/__tests__/test_day_3.py b/src/modules/__tests__/test_day_3.py index de53057..a6c17ab 100644 --- a/src/modules/__tests__/test_day_3.py +++ b/src/modules/__tests__/test_day_3.py @@ -4,3 +4,8 @@ from src.modules.day_3 import Engine def test_engine_parts(): engine = Engine(r"src/assets/day_3_test.txt") assert engine.sum_part_numbers() == 4361 + + +def test_gear_ratio(): + engine = Engine(r"src/assets/day_3_test.txt") + assert engine.get_gear_ratio() == 467835 diff --git a/src/modules/day_3.py b/src/modules/day_3.py index 2a3dbd2..b127338 100644 --- a/src/modules/day_3.py +++ b/src/modules/day_3.py @@ -1,22 +1,48 @@ +from functools import total_ordering + + def run(): engine = Engine(r"src/assets/day_3.txt") print(f"Part number sum: {engine.sum_part_numbers()}") + print(f"Gear ratio: {engine.get_gear_ratio()}") class Engine: engine: list[str] - neutral_char = "." + neutral_char: str = "." + gear_char: str = "*" def __init__(self, path: str): self.engine = self.read_engine(path) + def get_gear_ratio(self) -> int: + sum = 0 + for x in range(self.engine.__len__()): + for y in range(self.engine[x].__len__()): + gear = self.position_is_gear(x, y) + + if gear[0] == True: + n_1 = self.extract_number_from_position( + gear[1][0][0], gear[1][0][1] + ) + n_2 = self.extract_number_from_position( + gear[1][1][0], gear[1][1][1] + ) + sum += n_1[0] * n_2[0] + + return sum + def sum_part_numbers(self) -> int: sum = 0 for x in range(self.engine.__len__()): y = 0 while y < self.engine[x].__len__(): - if self.engine[x][y] != self.neutral_char and not self.position_is_symbol(x,y) and self.position_has_part(x,y): - number_pos = self.extract_number_from_position(x,y) + if ( + self.engine[x][y] != self.neutral_char + and not self.position_is_symbol(x, y) + and self.position_has_part(x, y) + ): + number_pos = self.extract_number_from_position(x, y) sum += number_pos[0] y = number_pos[1] - 1 y += 1 @@ -35,6 +61,53 @@ class Engine: return (int(n), y) + def position_is_gear(self, x: int, y: int) -> tuple[bool, list[tuple[int, int]]]: + if x >= self.engine.__len__() or x < 0 or y >= self.engine.__len__() or y < 0: + return (False, []) + if self.engine[x][y] != self.gear_char: + return (False, []) + + total_numbers = 0 + position_number: list[tuple[int, int]] = [] + b = self.position_is_number(x + 1, y) + t = self.position_is_number(x - 1, y) + r = self.position_is_number(x, y + 1) + l = self.position_is_number(x, y - 1) + bl = self.position_is_number(x + 1, y - 1) + br = self.position_is_number(x + 1, y + 1) + tl = self.position_is_number(x - 1, y - 1) + tr = self.position_is_number(x - 1, y + 1) + + if r is True: + position_number.append((x, y + 1)) + total_numbers += 1 + if l is True: + position_number.append((x, y - 1)) + total_numbers += 1 + + if tl is True: + position_number.append((x - 1, y - 1)) + total_numbers += 1 + elif t is True: + position_number.append((x - 1, y)) + total_numbers += 1 + + if tr is True and t is not True: + position_number.append((x - 1, y + 1)) + total_numbers += 1 + + if bl is True: + position_number.append((x + 1, y - 1)) + total_numbers += 1 + elif b is True: + position_number.append((x + 1, y)) + total_numbers += 1 + + if br is True and b is not True: + position_number.append((x + 1, y + 1)) + total_numbers += 1 + + return (total_numbers == 2, position_number) def position_has_part(self, x: int, y: int) -> bool: return ( @@ -58,6 +131,8 @@ class Engine: return self.engine[x][y] != self.neutral_char def position_is_number(self, x: int, y: int) -> bool: + if x >= self.engine.__len__() or x < 0 or y >= self.engine.__len__() or y < 0: + return False try: int(self.engine[x][y]) return True