
AoC 2025, Day 4 - Printing Department
Sunday, December 7, 2025 at 12:00 PM
Now that we're done doing electrical maintenance, it's time to literally push paper in Day 4: Printing Department. Again, spoilers are below for this puzzle. Don't read it if you want to solve this yourself.
You enter the Printing Department, which is covered in large rolls of paper. You need to dive further into this North Pole labyrinth, and the elves tell you that there's a cafeteria in the back, if you can move all the paper rolls. They already have forklifts moving rolls around, but you can make this more efficient.
You have a grid where @ are rolls and . are empty spaces:
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
Your task is to help plan and move these paper rolls around to get you to the cafeteria. This will be dependent on the total rolls of paper adjacent to other rolls.
Part 1 is to determine how many rolls have less than 4 other rolls adjacent to it.
Part 2 uses Part 1's adjacent rules to remove valid rolls in cycles, then calculate total rolls removed when all is said and done.
Let's roll.
We turn our data into a JSON array:
data4 = [
"..@@.@@@@.",
"@@@.@.@.@@",
"@@@@@.@.@@",
"@.@@@@..@.",
"@@.@@@@.@@",
".@@@@@@@.@",
".@.@.@.@@@",
"@.@@@.@@@@",
".@@@@@@@@.",
"@.@.@@@.@.",
];
Because we don't want to have tons of if statements scattered around to determine range limits, we will pad our array rows with a . on each side, as well as create blank rows at the front/back of the array:
let grid = d.map((i) => `.${i}.`.split("")),
padRow = new Array(grid[0].length).fill(".");
grid.unshift(padRow);
grid.push(padRow);
That way, we can do adjacent calculations without worrying about OutOfRangeExceptions, simply by setting the starting row/column at 1 and the row/column limit at length - 1:
for (let y = 1; y < grid.length - 1; y++)
for (let x = 1; x < grid[y].length - 1; x++) {
Our grid is set up and ready to go.
When we find a @ paper roll in our grid, we create a string that contains all the adjacent symbols surrounding it, then run a matchAll to find all our @ symbols:
if (grid[y][x] == "@") {
let adjacent =
`${grid[y - 1][x]}${grid[y + 1][x]}
${grid[y][x - 1]}${grid[y - 1][x - 1]}${grid[y + 1][x - 1]}
${grid[y][x + 1]}${grid[y - 1][x + 1]}${grid[y + 1][x + 1]}`
.matchAll(/@/g)
.toArray();
If there's less than 4 @ symbols adjacent, mark it in the total:
if (adjacent.length < 4) {
total++;
}
Return the result, boom, done.
It's essentially doing Part 1 over and over again, removing valid rolls each cycle. We can modify our function to accommodate for this.
Add a boolean parameter, part2, to determine which puzzle it's solving:
var Day4A = (d, part2) => {
We also create a global boolean variable removeCycle that flips to a true value when a cycle has rolls that can be removed.
In our if statement for adjacent counts, we add logic to replace our @ symbol with x and mark removeCycle as true:
if (adjacent.length < 4) {
removeCycle = true;
grid[y][x] = "x";
total++;
}
Because our x symbols are still rolls in this cycle, we need to change our matchAll to include it in its search:
.matchAll(/[@]|[x]/g)
We surround our logic in a do/while loop, so that it always commits it at least once. The beginning of the loop resets removeCycle and replaces x symbols with .:
do {
removeCycle = false;
grid = grid.map((i) => i.map((j) => (j == "x" ? "." : j)));
And our while condition is to loop only if part2 and removeCycle are both true:
} while (part2 && removeCycle);
And there we go. Both parts done.
It's not Advent of Code without some grid puzzles. This one fell into place more easily than previous years. Maybe I'm just used to doing them.
My final code:
var data4 = require("./day04.json");
var Day4A = (d, part2) => {
let grid = d.map((i) => `.${i}.`.split("")),
padRow = new Array(grid[0].length).fill("."),
total = 0,
removeCycle = false;
grid.unshift(padRow);
grid.push(padRow);
do {
removeCycle = false;
grid = grid.map((i) => i.map((j) => (j == "x" ? "." : j)));
for (let y = 1; y < grid.length - 1; y++)
for (let x = 1; x < grid[y].length - 1; x++) {
if (grid[y][x] == "@") {
let adjacent =
`${grid[y - 1][x]}${grid[y + 1][x]}
${grid[y][x - 1]}${grid[y - 1][x - 1]}${grid[y + 1][x - 1]}
${grid[y][x + 1]}${grid[y - 1][x + 1]}${grid[y + 1][x + 1]}`
.matchAll(/[@]|[x]/g)
.toArray();
if (adjacent.length < 4) {
removeCycle = true;
grid[y][x] = "x";
total++;
}
}
}
} while (part2 && removeCycle);
return total;
};
console.log(`${Day4A(data4, false)} - ${Day4A(data4, true)}`);