Advent of Code 2025 - Day 4Printing Department

Rust | Problem statement | Source code | Tags: Cellular automata

Part 1

There are 8 neighbor positions to test, which can be iterated by enumerating the relative coordinates for row and column. For each position, check if it's in bounds and contains a @.

fn neighbors(data: &Vec<Vec<char>>, i: isize, j: isize, w: isize, h: isize) -> usize {
let mut count = 0;
for di in -1..=1 {
for dj in -1..=1 {
if di == 0 && dj == 0 {
continue;
}
let ni = i + di;
let nj = j + dj;
if ni >= 0 && ni < h && nj >= 0 && nj < w && data[ni as usize][nj as usize] == '@' {
count += 1;
}
}
}
count
}
rust

Part 1 just counts the cells that contain @ and have fewer than 4 @ neighbors.

Part 2

I guess, the only pitfall here is to make sure to update the grid simultaneously because earlier updates can affect tests for later cells. I save a list of cells to update, and then apply the updates after iterating through the whole grid. I stop when there are no more updates to apply.

loop {
let mut positions: Vec<(usize, usize)> = vec![];
for (i, line) in data.iter().enumerate() {
for (j, c) in line.iter().enumerate() {
if *c != '@' {
continue;
}
if neighbors(&data, i as isize, j as isize, w, h) < 4 {
positions.push((i, j));
}
}
}
if positions.is_empty() {
break;
}
for (i, j) in positions.iter() {
data[*i][*j] = '.';
}
total += positions.len();
}
rust