Unrolling the Paper Rolls

312 words
2 min.
Post preview image
Advent of Code 2025: Day 4 – removing accessible paper rolls from a grid iteratively

Day 4 gives us a fun grid puzzle! 📜

We have a 2D grid filled with @ (paper rolls) and . (empty space).

@.@@.@@@..
@@..@.@.@.
.@.@@..@.@
@@.@.@@@..
.@@@..@.@@

A paper roll is accessible if it has fewer than 4 adjacent @ neighbours (using 4-directional adjacency – no diagonals).

Part 1

Simple enough – just count how many @ cells have fewer than 4 @ neighbours:

function getRollCoords(grid: Map<string, string>): Coord[] {
    let paperRolls: Coord[] = [];
    grid.forEach((value, coord) => {
        if (value === '@') {
            const adjacentCells = getNeighbors(Coord.deserialize(coord), grid, true);
            const rolls = adjacentCells.filter(cell => cell === '@').length;
            if (rolls < 4)
                paperRolls.push(Coord.deserialize(coord));
        }
    });
    return paperRolls;
}

function findAccessiblePaperRolls(grid: Grid) {
    return getRollCoords(grid).length;
}

The getNeighbors utility returns the values of all orthogonal neighbours. The true flag means we only get cells that exist in the grid (no out-of-bounds). Any @ with fewer than 4 @ neighbours is accessible – boundary cells are naturally accessible since they have fewer neighbours overall.


Part 2

Part 2 asks for the total number of rolls removed across all rounds of a peeling process: each round, we find all currently accessible rolls, remove them (replacing @ with .), and then repeat until no accessible rolls remain.

function findAndRemoveAccessiblePaperRolls(grid: Grid) {
    let totalAccessiblePaperRolls = 0;

    let paperRollCoords: Coord[] = getRollCoords(grid);
    while (paperRollCoords.length > 0) {
        totalAccessiblePaperRolls += paperRollCoords.length;

        paperRollCoords.forEach((p) => grid.set(p.serialize(), '.'));

        paperRollCoords = getRollCoords(grid);
    }

    return totalAccessiblePaperRolls;
}

This is a classic “onion peeling” algorithm – each iteration strips away the outermost accessible layer, making previously interior rolls accessible in the next round. The loop continues until there’s nothing left to remove (or only rolls with 4 @ neighbours remain).


Battery Banks and Joltage
Fresh Ingredients by the Range