Advent of Code 2025 - Puzzle 3
Advent of Code is a yearly puzzle that runs through December where each day a new puzzle is released that gets progressively harder. The goal is to take the text input and create an algorithm in any language to solve the puzzle.
For this year I decided to make my solutions into interactive widgets on the web.
Spoilers
WARNING - Below is a spoiler. If you want to figure it out yourself have a look at the puzzle on Advent of Code and come back after you have solved it.
Spoilers below. Scroll down at your own risk.
.
.
.
The Puzzle
Our aim this time is to find the largest possible two digit number that can be created from a list of numbers. The only real constraint for this puzzle is that
the number ordering cannot be changed. So for instance, 123 can only result in 12, 13 or 23 whilst maintaining the order.
This must be done for multiple rows of numbers and the results added together to get the final answer.
Solution
Parsing the input
This time the idea is to first parse each line into an array of numbers, and then those numbers into items in a list. Using JavaScript’s split and map makes this a very quick and easy operation.
const lines = input.split("\n")
.map(line => line.split("").map(Number));
/*
* Input:
* 1234
* 5678
*
* Output:
* [[1, 2, 3, 4], [5, 6, 7, 8]]
*/
Processing the data
To find the largest possible number following the defined rules, we can define some core principles:
- The first digit will always be the largest number in the list except for the last number.
- The second digit will always be the largest number after the first digit.
With these two principles we can create a simple algorithm to find the largest two digit number for each line of input.
function findLargestTwoDigitNumber(numbers) {
if (numbers.length < 2) throw new Error('Puzzle input is invalid');
// First number will be the largest number except the last one
let firstNumber = Math.max(...numbers.slice(0, -1));
// Get the first index of the first number to find numbers after it
// first index must be used to give all possible second numbers
let firstIndex = numbers.indexOf(firstNumber);
let secondNumber = Math.max(...numbers.slice(firstIndex + 1));
return [firstNumber, secondNumber];
}
This is honestly not the most performant way of finding the answer, but it certainly was the simplest to implement and understand.
Showing the results
This solution was used to make the interactive widget below. You can change the inputs to anything you like.
Part 2
The second part increases the number of digits from 2 to 12, but all other rules remain the same. This is simple enough, but my hard coding of two digits has come back to not be so great.
To fix this, I first created a function to track both the largest number as well as the index that number was found at.
function getLargestNumberAndIndex(numbers, minIndex = 0) {
const largest = Math.max(...numbers);
return [
largest,
// This lets us track the index in the original array
numbers.indexOf(largest) + minIndex
];
}
The purpose of the index is to make it easier to pass the correct number of digits to the next call, as well as track where the slice is within the original array.
const firstResult = getLargestNumberAndIndex(numbers.slice(0, -11));
const secondResult = getLargestNumberAndIndex(
// The index after the first result
// leaving a minimum of 10 digits for the rest
numbers.slice(firstResult[1] + 1, -10),
// pass through the offset of the slice from
// the original array
firstResult[1] + 1
);
This pattern is then repeated for each of the 12 digits needed to create the final number. I should have used a loop here to reduce the code, but it was honestly easier to get working this way and I never refactored it.
Each magic number here would be a pain to change which means in a real code base I would have definitely made it more dynamic.
const thirdResult = getLargestNumberAndIndex(numbers.slice(secondResult[1] + 1, -9), secondResult[1] + 1);
const fourthResult = getLargestNumberAndIndex(numbers.slice(thirdResult[1] + 1, -8), thirdResult[1] + 1);
const fifthResult = getLargestNumberAndIndex(numbers.slice(fourthResult[1] + 1, -7), fourthResult[1] + 1);
const sixthResult = getLargestNumberAndIndex(numbers.slice(fifthResult[1] + 1, -6), fifthResult[1] + 1);
const seventhResult = getLargestNumberAndIndex(numbers.slice(sixthResult[1] + 1, -5), sixthResult[1] + 1);
const eighthResult = getLargestNumberAndIndex(numbers.slice(seventhResult[1] + 1, -4), seventhResult[1] + 1);
const ninthResult = getLargestNumberAndIndex(numbers.slice(eighthResult[1] + 1, -3), eighthResult[1] + 1);
const tenthResult = getLargestNumberAndIndex(numbers.slice(ninthResult[1] + 1, -2), ninthResult[1] + 1);
const eleventhResult = getLargestNumberAndIndex(numbers.slice(tenthResult[1] + 1, -1), tenthResult[1] + 1);
const twelfthResult = getLargestNumberAndIndex(numbers.slice(eleventhResult[1] + 1), eleventhResult[1] + 1);
And with that we have all 12 digits needed to create the largest possible number for each row.
Showing the results
I used the widget below to generate the number and submit my answer. Have a play with the inputs yourself!
Want to read more? Check out more posts below!