advent of code 2025 in ts and nix
1let
2 input = builtins.readFile ../../shared/03/input.txt;
3 banks = builtins.filter (s: builtins.isString s && s != "") (builtins.split "\n" input);
4
5 # Helper to convert string to list of digit ints
6 stringToDigits = str:
7 let
8 len = builtins.stringLength str;
9 indices = builtins.genList (i: i) len;
10 in map (i: builtins.fromJSON (builtins.substring i 1 str)) indices;
11
12 # Part 1: Find highest pair (largestI * 10 + jVal) for each bank
13 part1 =
14 let
15 processBank = bank:
16 let
17 digits = stringToDigits bank;
18 L = builtins.length digits;
19
20 # For each position j, find the largest digit before it and form pair
21 processPair = state: j:
22 let
23 jVal = builtins.elemAt digits j;
24 # Find largest digit in digits[0..j-1]
25 largestI = builtins.foldl'
26 (max: i: let iVal = builtins.elemAt digits i; in if iVal > max then iVal else max)
27 (-1)
28 (builtins.genList (i: i) j);
29 pair = largestI * 10 + jVal;
30 newHighest = if pair > state.highest then pair else state.highest;
31 in { highest = newHighest; };
32
33 # Process j from 1 to L-1
34 result = builtins.foldl'
35 processPair
36 { highest = -1; }
37 (builtins.genList (i: i + 1) (L - 1));
38 in result.highest;
39
40 jolts = map processBank banks;
41 in builtins.foldl' builtins.add 0 jolts;
42
43 # Part 2: Monotonic stack to find best K-digit number
44 part2 =
45 let
46 K = 12;
47
48 processBank = bank:
49 let
50 digits = stringToDigits bank;
51 L = builtins.length digits;
52
53 # Process each digit with monotonic stack approach
54 processDigit = state: j:
55 let
56 jVal = builtins.elemAt digits j;
57 remaining = L - j;
58
59 # Pop smaller tail digits if we can still reach K
60 popSmaller = stack:
61 let
62 stackLen = builtins.length stack;
63 canPop = stackLen > 0 &&
64 (builtins.elemAt stack (stackLen - 1)) < jVal &&
65 stackLen - 1 + remaining >= K;
66 in
67 if canPop then
68 popSmaller (builtins.genList (i: builtins.elemAt stack i) (stackLen - 1))
69 else
70 stack;
71
72 newStack = popSmaller state.stack;
73 stackLen = builtins.length newStack;
74
75 # Add current digit if we still need more
76 finalStack = if stackLen < K then newStack ++ [jVal] else newStack;
77 in { stack = finalStack; };
78
79 # Process all digits
80 result = builtins.foldl'
81 processDigit
82 { stack = []; }
83 (builtins.genList (i: i) L);
84
85 # Convert stack to number
86 stackToNum = builtins.foldl' (acc: d: acc * 10 + d) 0 result.stack;
87 in stackToNum;
88
89 jolts = map processBank banks;
90 in builtins.foldl' builtins.add 0 jolts;
91
92in {
93 inherit part1 part2;
94}