Skip to content

Commit d4bfc35

Browse files
committed
AoC 2025 Day 4 - cleanup + faster
1 parent 0b6479b commit d4bfc35

File tree

1 file changed

+37
-53
lines changed

1 file changed

+37
-53
lines changed

src/main/python/AoC2025_04.py

Lines changed: 37 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
#
55

66
import sys
7+
from collections import deque
78

89
from aoc.common import InputData
910
from aoc.common import SolutionBase
1011
from aoc.common import aoc_samples
11-
from aoc.geometry import Direction
1212
from aoc.grid import Cell
13-
14-
Input = InputData
15-
Output1 = int
16-
Output2 = int
17-
13+
from aoc.grid import CharGrid
1814

1915
TEST = """\
2016
..@@.@@@@.
@@ -29,56 +25,44 @@
2925
@.@.@@@.@.
3026
"""
3127

28+
Input = CharGrid
29+
Output1 = int
30+
Output2 = int
31+
ROLL, EMPTY = "@", "."
32+
3233

3334
class Solution(SolutionBase[Input, Output1, Output2]):
3435
def parse_input(self, input_data: InputData) -> Input:
35-
return input_data
36-
37-
def part_1(self, inputs: Input) -> Output1:
38-
grid = list(inputs)
39-
ans = 0
40-
for r, row in enumerate(grid):
41-
for c, ch in enumerate(row):
42-
if ch == "@":
43-
cnt = 0
44-
cell = Cell(r, c)
45-
for d in Direction.octants():
46-
n = cell.at(d)
47-
if (
48-
0 <= n[0] < len(grid)
49-
and 0 <= n[1] < len(row)
50-
and grid[n[0]][n[1]] == "@"
51-
):
52-
cnt += 1
53-
if cnt < 4:
54-
ans += 1
55-
return ans
56-
57-
def part_2(self, inputs: Input) -> Output2:
58-
grid = [list(row) for row in inputs]
59-
ans = 0
60-
while True:
61-
new_grid = [list(row) for row in grid]
62-
for r, row in enumerate(grid):
63-
for c, ch in enumerate(row):
64-
if ch == "@":
65-
cnt = 0
66-
cell = Cell(r, c)
67-
for d in Direction.octants():
68-
n = cell.at(d)
69-
if (
70-
0 <= n[0] < len(grid)
71-
and 0 <= n[1] < len(row)
72-
and grid[n[0]][n[1]] == "@"
73-
):
74-
cnt += 1
75-
if cnt < 4:
76-
new_grid[r][c] = "."
77-
ans += 1
78-
if new_grid == grid:
79-
break
80-
grid = new_grid
81-
return ans
36+
return CharGrid.from_strings(list(input_data))
37+
38+
def is_removable(self, grid: CharGrid, cell: Cell) -> bool:
39+
return (
40+
grid.get_value(cell) == ROLL
41+
and sum(
42+
grid.get_value(n) == ROLL
43+
for n in grid.get_all_neighbours(cell)
44+
)
45+
< 4
46+
)
47+
48+
def part_1(self, grid: Input) -> Output1:
49+
return sum(self.is_removable(grid, cell) for cell in grid.get_cells())
50+
51+
def part_2(self, grid: Input) -> Output2:
52+
q = deque(
53+
cell for cell in grid.get_cells() if self.is_removable(grid, cell)
54+
)
55+
seen = set[Cell]()
56+
while len(q) != 0:
57+
cell = q.popleft()
58+
if cell in seen:
59+
continue
60+
seen.add(cell)
61+
grid.set_value(cell, EMPTY)
62+
for n in grid.get_all_neighbours(cell):
63+
if self.is_removable(grid, n):
64+
q.append(n)
65+
return len(seen)
8266

8367
@aoc_samples(
8468
(

0 commit comments

Comments
 (0)