advent of code 2025 in ts and nix

feat: add nix day 6

dunkirk.sh eaf214bd f5cc3bfd

verified
Changed files
+115
nix
+115
nix/06/solution.nix
···
···
+
let
+
input = builtins.readFile ../../shared/06/input.txt;
+
lines = builtins.filter (s: builtins.isString s && s != "") (builtins.split "\n" input);
+
+
# Helper to get character at position or space if out of bounds
+
charAt = str: idx:
+
if idx < 0 || idx >= builtins.stringLength str
+
then " "
+
else builtins.substring idx 1 str;
+
+
# Find whitespace columns (columns that are all spaces/tabs in data rows)
+
dataRows = builtins.genList (i: builtins.elemAt lines i) (builtins.length lines - 1);
+
maxLen = builtins.foldl' (max: row:
+
let len = builtins.stringLength row;
+
in if len > max then len else max
+
) 0 dataRows;
+
+
# Find columns that are all whitespace
+
splitCols = builtins.filter (col: col != null) (builtins.genList (i:
+
let
+
allWS = builtins.foldl' (acc: row:
+
acc && (let ch = charAt row i; in ch == " " || ch == " ")
+
) true dataRows;
+
in if allWS then i else null
+
) maxLen);
+
+
# Split each row at whitespace columns
+
splitRow = row:
+
let
+
rowLen = builtins.stringLength row;
+
splitImpl = cuts: startPos: acc:
+
if builtins.length cuts == 0
+
then acc ++ [(builtins.substring startPos (rowLen - startPos) row)]
+
else
+
let
+
cut = builtins.head cuts;
+
restCuts = builtins.tail cuts;
+
end = if cut + 1 > rowLen then rowLen else cut + 1;
+
segment = builtins.substring startPos (end - startPos) row;
+
in splitImpl restCuts end (acc ++ [segment]);
+
in splitImpl splitCols 0 [];
+
+
# Split all lines (including operator row)
+
segmentedRows = builtins.map splitRow lines;
+
+
# Transpose to get columns (problems)
+
numProblems = builtins.length (builtins.head segmentedRows);
+
problems = builtins.genList (colIdx:
+
builtins.map (row: builtins.elemAt row colIdx) segmentedRows
+
) numProblems;
+
+
# Fast trim - just remove spaces and tabs
+
# Extract just digits from a string for numbers
+
extractNum = str:
+
let
+
cleaned = builtins.replaceStrings [" " " "] ["" ""] str;
+
in if builtins.stringLength cleaned == 0 then 0 else builtins.fromJSON cleaned;
+
+
# Extract operator
+
extractOp = str:
+
let
+
cleaned = builtins.replaceStrings [" " " "] ["" ""] str;
+
in cleaned;
+
+
# Part 1: Normal left-to-right evaluation
+
part1 = builtins.foldl' (total: problem:
+
let
+
lastIdx = builtins.length problem - 1;
+
operator = extractOp (builtins.elemAt problem lastIdx);
+
nums = builtins.map (s: extractNum s) (builtins.genList (i: builtins.elemAt problem i) lastIdx);
+
value =
+
if operator == "*"
+
then builtins.foldl' (acc: n: acc * n) 1 nums
+
else builtins.foldl' (acc: n: acc + n) 0 nums;
+
in total + value
+
) 0 problems;
+
+
# Part 2: Cepheid (vertical) reading
+
part2 = builtins.foldl' (total: problem:
+
let
+
lastIdx = builtins.length problem - 1;
+
operator = extractOp (builtins.elemAt problem lastIdx);
+
numStrs = builtins.genList (i: builtins.elemAt problem i) lastIdx;
+
+
# Find max width
+
maxWidth = builtins.foldl' (max: s:
+
let len = builtins.stringLength s;
+
in if len > max then len else max
+
) 0 numStrs;
+
+
# Read vertically from right to left
+
cephNums = builtins.filter (n: n != null) (builtins.genList (colR:
+
let
+
# Build digits string from this column (right to left)
+
idx = maxWidth - colR;
+
digitsStr = builtins.concatStringsSep "" (builtins.filter (ch: ch != " " && ch != " ") (builtins.map (s:
+
if idx >= 0 && idx < builtins.stringLength s
+
then builtins.substring idx 1 s
+
else " "
+
) numStrs));
+
in if builtins.stringLength digitsStr > 0
+
then builtins.fromJSON digitsStr
+
else null
+
) (maxWidth + 1));
+
+
value =
+
if operator == "*"
+
then builtins.foldl' (acc: n: acc * n) 1 cephNums
+
else builtins.foldl' (acc: n: acc + n) 0 cephNums;
+
in total + value
+
) 0 problems;
+
+
in {
+
inherit part1 part2;
+
}