Python
Simple brute-force is enough.
DIRECTIONS = [
(0, 1), (1, 0), (0, -1), (-1, 0), # cardinal
(1, 1), (1, -1), (-1, 1), (-1, -1) # diagonal
]
# yield all valid neighbor coordinates
def yield_neighbors(i, j, m, n):
for di, dj in DIRECTIONS:
ni, nj = i+di, j+dj
if 0 <= ni < m and 0 <= nj < n:
yield ni, nj
# build char grid from input data
def build_grid(data: str):
return [list(row) for row in data.splitlines()]
def part1(data: str):
grid = build_grid(data)
m, n = len(grid), len(grid[0])
# count accessible rolls
accessible = 0
for i in range(m):
for j in range(n):
if grid[i][j] != '@':
continue
neighbor_rolls = sum(grid[ni][nj] == '@' for ni, nj in yield_neighbors(i, j, m, n))
if neighbor_rolls < 4:
accessible += 1
return accessible
def part2(data: str):
grid = build_grid(data)
m, n = len(grid), len(grid[0])
total_accessible = 0 # total accessible rolls over all cycles
accessible = None # rolls accessible this cycle
# repeat until no more rolls can be accessed
while accessible != 0:
accessible = 0
for i in range(m):
for j in range(n):
if grid[i][j] != '@':
continue
neighbor_rolls = sum(grid[ni][nj] == '@' for ni, nj in yield_neighbors(i, j, m, n))
if neighbor_rolls < 4:
accessible += 1
# we can immediately remove this roll, no need to wait for next cycle
grid[i][j] = '.'
# accumulate accessible rolls this cycle
total_accessible += accessible
return total_accessible
sample = """<paste sample here>"""
assert part1(sample) == 13
assert part2(sample) == 43