JonathanGupton

Advent of Code 2025 - Day 04 - Python

Dec 4th, 2025
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.35 KB | None | 0 0
  1. from copy import deepcopy
  2. from dataclasses import dataclass
  3. from typing import Iterable
  4.  
  5.  
  6. @dataclass(slots=True, frozen=True)
  7. class Floor:
  8.     paper_locations: frozenset[complex]
  9.  
  10.  
  11. def parse_data(fp: str) -> Floor:
  12.     paper_locations = set()
  13.     with open(fp, "r") as f:
  14.         for y, line in enumerate(map(str.strip, f.readlines())):
  15.             for x, v in enumerate(line):
  16.                 x_max = x
  17.                 y_max = y
  18.                 if v == "@":
  19.                     paper_locations.add(complex(x, y))
  20.     return Floor(paper_locations=frozenset(paper_locations))
  21.  
  22.  
  23. DIRECTIONS = (
  24.     complex(-1, 1),
  25.     complex(0, 1),
  26.     complex(1, 1),
  27.     complex(-1, 0),
  28.     complex(1, 0),
  29.     complex(-1, -1),
  30.     complex(0, -1),
  31.     complex(1, -1),
  32. )
  33.  
  34.  
  35. def count_adjacent(
  36.     position: complex, floor: Floor, directions: Iterable[complex] = DIRECTIONS
  37. ) -> int:
  38.     return sum(
  39.         direction + position in floor.paper_locations for direction in directions
  40.     )
  41.  
  42.  
  43. def iter_find_accessible_paper_locations(floor: Floor) -> Iterable[complex]:
  44.     for position in floor.paper_locations:
  45.         if count_adjacent(position, floor) < 4:
  46.             yield position
  47.  
  48.  
  49. def find_removable_roll_count(floor: Floor) -> int:
  50.     initial_floor = deepcopy(floor)
  51.     current_floor = deepcopy(floor)
  52.     while True:
  53.         removable = set(iter_find_accessible_paper_locations(current_floor))
  54.         next_floor = Floor(frozenset(current_floor.paper_locations - removable))
  55.         if current_floor == next_floor:
  56.             return len(initial_floor.paper_locations - current_floor.paper_locations)
  57.         current_floor = next_floor
  58.  
  59.  
  60. def part_a() -> None:
  61.     """
  62.    >>> fp = "day04a.txt"
  63.    >>> floor = parse_data(fp)
  64.    >>> assert len([*iter_find_accessible_paper_locations(floor)]) == 13
  65.    """
  66.     fp = "day04.txt"
  67.     floor = parse_data(fp)
  68.     accessible_paper_locations_count = len(
  69.         [*iter_find_accessible_paper_locations(floor)]
  70.     )
  71.     print(accessible_paper_locations_count)
  72.  
  73.  
  74. def part_b():
  75.     """
  76.    >>> fp = "day04a.txt"
  77.    >>> floor = parse_data(fp)
  78.    >>> assert find_removable_roll_count(floor) == 43
  79.    """
  80.     fp = "day04.txt"
  81.     floor = parse_data(fp)
  82.     removable_count = find_removable_roll_count(floor)
  83.     print(removable_count)
  84.  
  85.  
  86. if __name__ == "__main__":
  87.     part_a()
  88.     part_b()
  89.  
Add Comment
Please, Sign In to add comment