Advent of Code 2025 – Day 06
Day 6: Trash Compactor
Summary
Day 6 brings us into a garbage compactor where we meet a family of cephalopods. To pass the time while they open the door, we help the youngest one with her math homework. The puzzle involves parsing a 2D grid of characters representing math problems. The twist is in how the numbers and operations are arranged spatially.
Part 1
In Part 1, the problems are arranged horizontally but stacked vertically. Problems are separated by empty columns. Within a problem block, each line contains a number, and the bottom line contains the operator (+ or *).
Solution Approach
- Grid Parsing: Read the input file into a 2D grid (array of strings).
- Problem Extraction: Scan the grid column by column to identify “blank columns” (columns containing only spaces). These blank columns serve as delimiters between problems. We collect the start and end x-coordinates for each problem block.
- Parsing Individual Problems: For each block:
- Iterate through the rows.
- Parse the number on each row.
- Identify the operator at the bottom (either
+or*).
- Evaluation: Apply the operator to the list of numbers (sum or product).
- Aggregation: Sum the results of all problems to get the grand total.
Code Snippet (Problem Extraction)
function extractProblems(grid) {
const height = grid.length;
const width = Math.max(...grid.map((line) => line.length));
// ... helper to check if column is blank ...
const problems = [];
let x = 0;
while (x < width) {
// Skip blank columns
while (x < width && isBlankColumn(x)) x += 1;
if (x >= width) break;
const start = x;
// Find end of problem block
while (x < width && !isBlankColumn(x)) x += 1;
problems.push([start, x]);
}
return problems;
}
Part 2
Part 2 reveals that we were reading the “cephalopod math” incorrectly. The structure is actually column-based and read right-to-left.
- Each number is a vertical column (digits read top-to-bottom).
- Problems are still separated by blank columns.
- The operator is still at the bottom.
Solution Approach
The logic is similar to Part 1 but rotated.
- Right-to-Left Scanning: We scan the grid from
width - 1down to0to find problem boundaries. - Column-wise Number Parsing: Within a problem block, we iterate through columns (right-to-left).
- For each column, read digits from top to bottom (excluding the last row which might contain the operator).
- Join the digits to form a number.
- Operator Identification: Check the bottom row of the current column for the operator.
- Evaluation: Same as Part 1 (sum or product of the collected numbers).
Complexity Analysis
- Time Complexity: $O(W \times H)$, where $W$ is the width and $H$ is the height of the grid. In both parts, we visit every cell in the grid a constant number of times (once to check for blank columns, and once to parse the content).
- Space Complexity: $O(W \times H)$ to store the grid in memory. The storage for the parsed numbers is negligible compared to the grid itself.
Key Takeaways
- Grid Manipulation: This problem reinforces skills in navigating 2D grids, specifically handling arbitrary boundaries (blank columns) rather than fixed-size cells.
- Parsing Strategy: Separating the “identification of boundaries” from the “parsing of content” made the code cleaner and easier to adapt for Part 2.
- Directionality: Part 2 required a shift in perspective (vertical numbers, right-to-left reading), which was easily handled by adjusting the loop directions.
Solutions
const fs = require('fs');
const path = require('path');
const inputPath = process.argv[2] ?? path.join(__dirname, 'input.txt');
function readGrid(filePath) {
const raw = fs.readFileSync(filePath, 'utf8');
return raw
.replace(/\s+$/u, '')
.split(/\r?\n/)
.map((line) => line.replace(/\s+$/u, ''));
}
function extractProblems(grid) {
const height = grid.length;
const width = Math.max(...grid.map((line) => line.length));
const isBlankColumn = (x) => {
for (let y = 0; y < height; y += 1) {
const char = grid[y][x] ?? ' ';
if (char !== ' ') {
return false;
}
}
return true;
};
const problems = [];
let x = 0;
while (x < width) {
while (x < width && isBlankColumn(x)) {
x += 1;
}
if (x >= width) {
break;
}
const start = x;
while (x < width && !isBlankColumn(x)) {
x += 1;
}
problems.push([start, x]);
}
return problems;
}
function parseProblem(grid, bounds) {
const [startX, endX] = bounds;
const numbers = [];
let current = '';
for (let y = 0; y < grid.length; y += 1) {
let lineChunk = '';
for (let x = startX; x < endX; x += 1) {
lineChunk += grid[y][x] ?? ' ';
}
const trimmed = lineChunk.trim();
if (!trimmed) {
continue;
}
if (trimmed === '+' || trimmed === '*') {
return { numbers, op: trimmed };
}
current = trimmed;
numbers.push(Number(current));
}
throw new Error('Operation symbol not found for problem.');
}
function evaluateProblem(problem) {
const { numbers, op } = problem;
if (numbers.length === 0) {
return 0;
}
if (op === '+') {
return numbers.reduce((sum, value) => sum + value, 0);
}
if (op === '*') {
return numbers.reduce((product, value) => product * value, 1);
}
throw new Error(`Unknown operation: ${op}`);
}
function solve(grid) {
const problems = extractProblems(grid);
return problems.reduce((total, bounds) => {
const problem = parseProblem(grid, bounds);
return total + evaluateProblem(problem);
}, 0);
}
function main() {
if (!fs.existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exitCode = 1;
return;
}
const grid = readGrid(inputPath);
if (grid.length === 0) {
console.log('0');
return;
}
const result = solve(grid);
console.log(result.toString());
}
main();
const fs = require('fs');
const path = require('path');
const inputPath = process.argv[2] ?? path.join(__dirname, 'input.txt');
function readGrid(filePath) {
const raw = fs.readFileSync(filePath, 'utf8');
return raw
.replace(/\s+$/u, '')
.split(/\r?\n/)
.map((line) => line.replace(/\s+$/u, ''));
}
function columnIsBlank(grid, x) {
for (const row of grid) {
const char = row[x] ?? ' ';
if (char !== ' ') {
return false;
}
}
return true;
}
function extractProblems(grid) {
const height = grid.length;
const width = Math.max(...grid.map((line) => line.length));
const problems = [];
let x = width - 1;
while (x >= 0) {
while (x >= 0 && columnIsBlank(grid, x)) {
x -= 1;
}
if (x < 0) {
break;
}
const end = x + 1;
while (x >= 0 && !columnIsBlank(grid, x)) {
x -= 1;
}
problems.push([x + 1, end]);
}
return problems;
}
function parseProblem(grid, bounds) {
const [startX, endX] = bounds;
const height = grid.length;
const numbers = [];
let operator = null;
for (let x = endX - 1; x >= startX; x -= 1) {
const digits = [];
for (let y = 0; y < height - 1; y += 1) {
const char = grid[y][x] ?? ' ';
if (char === ' ') {
continue;
}
digits.push(char);
}
if (digits.length > 0) {
numbers.push(Number(digits.join('')));
}
const bottomChar = grid[height - 1][x] ?? ' ';
if (bottomChar === '+' || bottomChar === '*') {
operator = bottomChar;
}
}
if (operator === null) {
throw new Error('Operation symbol not found');
}
return { numbers, op: operator };
}
function evaluateProblem({ numbers, op }) {
if (op === '+') {
return numbers.reduce((sum, value) => sum + value, 0);
}
if (op === '*') {
return numbers.reduce((product, value) => product * value, 1);
}
throw new Error(`Unknown operator: ${op}`);
}
function solve(grid) {
const problems = extractProblems(grid);
return problems.reduce((total, bounds) => total + evaluateProblem(parseProblem(grid, bounds)), 0);
}
function main() {
if (!fs.existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exitCode = 1;
return;
}
const grid = readGrid(inputPath);
if (!grid.length) {
console.log('0');
return;
}
const result = solve(grid);
console.log(result.toString());
}
main();