Advent of Code 2025 – Day 03
Advent of Code 2025 - Day 3: Lobby
I spent Day 3 hanging out in the lobby with a pile of battery banks and a stubborn escalator. Here is how I cracked both puzzles using a pair of lean JavaScript scripts (part1.js and part2.js).
Part 1 – Two batteries, one number
Each bank is a string of digits. I needed the largest possible two-digit value that preserves order. Rather than evaluate every pair, I sweep the string once while tracking the best “first” digit seen so far. Every new digit forms a candidate pair with that best digit; if the candidate beats the running maximum, I store it, and if the new digit is even better as a first digit, I promote it. This greedy pass extracts the answer for a bank in linear time.
Complexity: For n banks whose lengths sum to L, the runtime is O(L) and the extra memory stays at O(1).
Part 2 – Twelve digits to rule the escalator
The safety override demanded far more juice: exactly twelve digits per bank. This morphs into the classic “remove k digits to maximize the remaining sequence” problem. I used a monotonic stack:
k = bank.length - 12is how many digits must be discarded.- While the stack’s last digit is smaller than the current digit and I still have removals left, pop it.
- Push the current digit.
- After processing the whole bank, trim any extra digits from the end until only twelve remain.
Converting the resulting 12-digit string to BigInt kept the totals precise, and summing across all banks produced the final answer.
Complexity: The same total input length L drives this part. Each digit is pushed and popped at most once, so the runtime is O(L) with O(12) extra storage per bank (effectively O(1)).
Lessons learned
- Greedy strategies shine when the problem boils down to keeping digits in order but maximizing lexicographic value.
- The same parsing foundation (reading banks from
input.txt) can feed both parts with different solving strategies. BigIntis a great safety net when puzzle outputs can exceed 64-bit integers.
With both parts done, the escalator has all the joltage it needs—and I get to keep moving deeper into the North Pole base.
Solutions
const fs = require('fs');
const path = require('path');
const inputPath = process.argv[2] ?? path.join(__dirname, 'input.txt');
function readBanks(filePath) {
try {
return fs
.readFileSync(filePath, 'utf8')
.trim()
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0);
} catch (error) {
throw new Error(`Unable to read input file: ${filePath}`);
}
}
function maxTwoDigitValue(bank) {
if (bank.length < 2) {
return 0;
}
let bestPair = -1;
let bestFirst = Number(bank[0]);
for (let i = 1; i < bank.length; i += 1) {
const digit = Number(bank[i]);
const candidate = bestFirst * 10 + digit;
if (candidate > bestPair) {
bestPair = candidate;
}
if (digit > bestFirst) {
bestFirst = digit;
}
}
return bestPair;
}
function solve(banks) {
return banks.reduce((sum, bank) => sum + maxTwoDigitValue(bank), 0);
}
function main() {
if (!fs.existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exitCode = 1;
return;
}
const banks = readBanks(inputPath);
if (banks.length === 0) {
console.log('0');
return;
}
const result = solve(banks);
console.log(result.toString());
}
main();
const fs = require('fs');
const path = require('path');
const DIGITS_TO_SELECT = 12;
const inputPath = process.argv[2] ?? path.join(__dirname, 'input.txt');
function readBanks(filePath) {
try {
return fs
.readFileSync(filePath, 'utf8')
.trim()
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0);
} catch (error) {
throw new Error(`Unable to read input file: ${filePath}`);
}
}
function bestTwelveDigits(bank) {
if (bank.length <= DIGITS_TO_SELECT) {
return BigInt(bank);
}
const stack = [];
let remaining = bank.length - DIGITS_TO_SELECT;
for (const char of bank) {
const digit = char;
while (stack.length && remaining > 0 && stack[stack.length - 1] < digit) {
stack.pop();
remaining -= 1;
}
stack.push(digit);
}
while (stack.length > DIGITS_TO_SELECT) {
stack.pop();
}
return BigInt(stack.join(''));
}
function solve(banks) {
return banks.reduce((sum, bank) => sum + bestTwelveDigits(bank), 0n);
}
function main() {
if (!fs.existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exitCode = 1;
return;
}
const banks = readBanks(inputPath);
if (banks.length === 0) {
console.log('0');
return;
}
const result = solve(banks);
console.log(result.toString());
}
main();