Day 3 of AoC 2025. Following up on , today I'm gonna try to solve the question using typescript, but in a Jupyter notebook, writing along while programming. Today's input looks like this:
import fs from "node:fs"
const input = fs.readFileSync("input.txt", "utf-8")
console.log(input.split('\n').slice(0,3))
[ "2215452689925244273244333436189317446384838478525478824435233342352236255624326767355438753493222423", "1222232323222232132222323221226222225212221213232122232311152232223212123622111212223162322221323211", "3786645737446363554463656544667372864465545434545435744345766553343446943531537627746253556233634463" ]
// sample input
const sample = `\
987654321111111
811111111111119
234234234234278
818181911112111\
`
console.log(sample)
987654321111111 811111111111119 234234234234278 818181911112111
The first part asks as to choose in each row two digits that taken together form the largest number in that row. The two digits must maintain their left-to-right order. For example: - in row 1: 987654321111111 forms 98 - in row 2: 811111111111119 forms 89 - in row 3: 234234234234278 forms 78 - in row 4: 818181911112111 forms 92
Then we sum them all and get
98+89+78+92
357
which is the final answer. Some insights: - the left digit dominates the size comparison - the left digit will never be the last one, because there won't be room for the right digit - if the same digit appears twice, it's always better to pick the left most one (only increase the right digit candidates this way)
Okay, so the algorithm will be as follows:
- left_digit = max(row[:-1])
- right_digit = max(row[ind_left_digit:])
// the name jolt is a ref to the AoC lore in the question
function maxJolt(nums: number[]) {
const lcand = nums.slice(0, -1)
const ldigit = Math.max(...lcand)
const lind = lcand.indexOf(ldigit)
const rdigit = Math.max(...nums.slice(lind+1, nums.length))
return ldigit * 10 + rdigit
}
function part1(input: string, debug=false) {
let sum = 0;
for (const r of input.split("\n")) {
const res = maxJolt(r.split('').map(n => parseInt(n)))
sum += res
if (debug) {
console.log(r)
console.log(res)
console.log(sum)
}
}
return sum
}
console.log(part1(sample, true))
987654321111111 98 98 811111111111119 89 187 234234234234278 78 265 818181911112111 92 357 357
Looks good, let's try the full input
console.log(part1(input))
17346
And we were right! let's move on to part 2. This time I leave more intermediate cells so you can see sum of the incremental process.
Now the twist is that instead of 2 digits we need to choose 12 digits the form the largest number! I think the same idea as before can be extended to the general case: (i+1)_digit = max(row[i_digit: -(12-i)]). Might be easier to just build the algorithm to be generic to the number of digits, then we can verify if we reproduce the same results.
const r = "987654321111111"
const nums = r.split('').map(n => parseInt(n))
const digits = []
const n_digits = 2
let last_ind = -1
let i = n_digits - 1
nums
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1 ]
We'll add to digits one by one
let slice = nums.slice(last_ind + 1, nums.length-i)
slice
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1 ]
let digit = Math.max(...slice)
digits.push(digit)
digit
9
last_ind = nums.slice(last_ind+1, nums.length).indexOf(digit)
last_ind
0
i = i - 1
slice = nums.slice(last_ind + 1, nums.length-i)
slice
[ 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1 ]
digit = Math.max(...slice)
digits.push(digit)
digit
8
digits
[ 9, 8 ]
Okay, so we kinda get how to body should work, let's merge it all, and try the 12 digits. Should get 987654321111
const r = "987654321111111"
const nums = r.split('').map(n => parseInt(n))
const digits = []
const n_digits = 12
let last_ind = -1
// let i = n_digits - 1
for (let i = n_digits - 1; i >=0; i--) {
const slice = nums.slice(last_ind + 1, nums.length-i)
const digit = Math.max(...slice)
last_ind = last_ind + slice.indexOf(digit) + 1
digits.push(digit)
}
digits
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1 ]
To turn it into a number we can use reduce
digits.reduce((s, x) => s*10 + x, 0)
987654321111
Looks good, let's put it in a function and try the rest of the sample
function maxJolt2(nums, n_digits) {
const digits = []
let last_ind = -1
for (let i = n_digits - 1; i >=0; i--) {
const slice = nums.slice(last_ind + 1, nums.length-i)
const digit = Math.max(...slice)
last_ind = last_ind + slice.indexOf(digit) + 1
digits.push(digit)
}
return digits.reduce((s, x) => s*10 + x, 0)
}
let sum = 0
sample.split("\n").forEach(r => {
const nums = r.split('').map(n => parseInt(n))
const res = maxJolt2(nums, 12)
console.log(res)
sum += res
})
console.log(sum)
987654321111 811111111119 434234234278 888911112111 3121910778619
Looks correct. Finally, let's put that in a function and finish
function part2(input: string) {
let sum = 0
input.split("\n").forEach(r => {
const nums = r.split('').map(n => parseInt(n))
const res = maxJolt2(nums, 12)
// console.log(res)
sum += res
})
return sum
}
part2(input)
172981362045136
Success!
As a bonus, I want to see if we can rewrite maxJolt2 in a functional way. I'm thinking recursion
function rMaxJolt2(nums, n_digits) {
if (n_digits == 1) return Math.max(...nums);
const digit = Math.max(...nums.slice(0, -n_digits + 1))
return digit * 10**(n_digits - 1) + rMaxJolt2(nums.slice(nums.indexOf(digit) + 1, nums.length), n_digits - 1)
}
rMaxJolt2("818181911112111".split("").map(n => parseInt(n)), 12)
888911112111