Advent of Code 2025 - Day 4
I opened the puzzle for day 4 and almost got a heart attack. It's a grid problem! I still have nightmares from previous years where seemingly straightforward 2D grids turned into three-dimensional ones... and sometimes even four!
Luckily for me, today's 2D grid did not evolve into anything weird and remained as it was. Let's take a look.
Check out my full solution for day 4 at GitHub.
Part one
The first part provides us with a 2D grid, where each position in the grid is either an empty space (.) or a roll of paper (@):
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
Our job is to find all rolls of paper with fewer than four other rolls of paper as direct neighbours. A neighbour is defined as one of the eight positions around yourself (so up, down, left, right, and diagonal).
This puzzle can be solved by generating a two-dimensional array, iterating over each position and counting the rolls of paper around you. This is exactly what my count_adjacent() function does:
def count_adjacent(data: List[List[str]], x: int, y: int,) -> int:
directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1),
(0, 1), (1, -1), (1, 0), (1, 1)]
count = 0
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= ny < len(data) and 0 <= nx < len(data[ny]):
if data[ny][nx] == '@':
count += 1
return count
Instead of writing out all eight directions around a position, you could also use a for-loop like below and ignore the current position. I just like to have all directions written out. I find that more readable.
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
The result of count_adjacent() is the answer for part one.
Part two
Part two's throws a curveball that makes you remove all rolls of paper from the grid that adhere to the "less than four neighbouring rolls of paper"-rule and then checking the full grid again. Or, in other words: replace each roll of paper (@) with empty space (.) if the rule applies and iterate over the whole grid again until there is no roll of paper left to remove.
For iterating over the grid, we can still use the count_adjacent() function from part one, keep track of all paper rolls that we remove in one iteration, actually remove them and then iterate again, until we had an iteration where no paper rolls were removed:
while True:
(accessible, to_remove) = run(data)
if accessible == 0:
break
total_removed += accessible
for (y, x) in to_remove:
data[y][x] = '.'
After the last iteration, the variable total_removed contains the answer to part two.