···
2
+
input = builtins.readFile ../../shared/07/input.txt;
3
+
lines = builtins.filter (s: builtins.isString s && s != "") (builtins.split "\n" input);
5
+
# Parse grid into 2D array of characters
6
+
grid = builtins.map (line: builtins.genList (i: builtins.substring i 1 line) (builtins.stringLength line)) lines;
8
+
rows = builtins.length grid;
9
+
cols = builtins.length (builtins.head grid);
11
+
# Find starting position
14
+
findInRow = rowIdx: row:
16
+
colIdx = builtins.foldl' (acc: i:
17
+
if acc != null then acc
18
+
else if builtins.elemAt row i == "S" then i
20
+
) null (builtins.genList (i: i) (builtins.length row));
21
+
in if colIdx != null then { r = rowIdx; c = colIdx; } else null;
23
+
result = builtins.foldl' (acc: i:
24
+
if acc != null then acc
25
+
else findInRow i (builtins.elemAt grid i)
26
+
) null (builtins.genList (i: i) (builtins.length grid));
29
+
startPos = findStart grid;
31
+
# Check if position is in bounds
32
+
inBounds = r: c: r >= 0 && r < rows && c >= 0 && c < cols;
34
+
# Get cell at position
37
+
then builtins.elemAt (builtins.elemAt grid r) c
40
+
# Part 1: Count total splits
43
+
# Process one step of beams
46
+
beams = state.beams;
47
+
nextBeams = builtins.concatMap (beam:
54
+
if !inBounds nr nc then []
56
+
let cell = getCell nr nc;
58
+
if cell == "." || cell == "S" then [{ r = nr; c = nc; }]
59
+
else if cell == "^" then
60
+
# Split into left and right
62
+
left = if inBounds nr (nc - 1) then [{ r = nr; c = nc - 1; }] else [];
63
+
right = if inBounds nr (nc + 1) then [{ r = nr; c = nc + 1; }] else [];
65
+
else [{ r = nr; c = nc; }]
68
+
# Count splits in this step
69
+
splits = builtins.foldl' (acc: beam:
75
+
cell = if inBounds nr nc then getCell nr nc else ".";
76
+
in if cell == "^" then acc + 1 else acc
79
+
# Remove duplicates using string keys
82
+
keysSet = builtins.foldl' (acc: beam:
83
+
acc // { "${toString beam.r},${toString beam.c}" = beam; }
85
+
in builtins.attrValues keysSet;
88
+
beams = uniqueBeams;
89
+
totalSplits = state.totalSplits + splits;
92
+
# Run simulation until no beams left
94
+
if builtins.length state.beams == 0
95
+
then state.totalSplits
96
+
else simulate (stepBeams state);
99
+
beams = [{ r = startPos.r; c = startPos.c; }];
102
+
in simulate initialState;
104
+
# Part 2: Count total timelines
107
+
# Helper to parse "r,c" key
110
+
parts = builtins.match "([0-9]+),([0-9]+)" key;
112
+
r = builtins.fromJSON (builtins.head parts);
113
+
c = builtins.fromJSON (builtins.elemAt parts 1);
116
+
# Process one step with timeline counts
117
+
stepTimelines = states:
119
+
# For each position with count, move forward
120
+
nextStates = builtins.foldl' (acc: key:
122
+
count = builtins.getAttr key states;
123
+
pos = parseKey key;
129
+
if !inBounds nr nc then acc
131
+
let cell = getCell nr nc;
133
+
if cell == "." || cell == "S" then
134
+
# Continue straight
135
+
let nkey = "${toString nr},${toString nc}";
136
+
in acc // { ${nkey} = (acc.${nkey} or 0) + count; }
137
+
else if cell == "^" then
138
+
# Split into two timelines
140
+
left = if inBounds nr (nc - 1) then
141
+
let lkey = "${toString nr},${toString (nc - 1)}";
142
+
in { ${lkey} = (acc.${lkey} or 0) + count; }
144
+
right = if inBounds nr (nc + 1) then
145
+
let rkey = "${toString nr},${toString (nc + 1)}";
146
+
in { ${rkey} = (acc.${rkey} or 0) + count; }
148
+
in acc // left // right
150
+
# Continue straight
151
+
let nkey = "${toString nr},${toString nc}";
152
+
in acc // { ${nkey} = (acc.${nkey} or 0) + count; }
153
+
) {} (builtins.attrNames states);
156
+
# Run simulation for all rows
157
+
simulate = states: step:
159
+
nextStates = if step >= rows then {} else stepTimelines states;
160
+
hasStates = builtins.length (builtins.attrNames nextStates) > 0;
162
+
if step >= rows || !hasStates
163
+
then states # Return last valid states
164
+
else simulate nextStates (step + 1);
166
+
initialStates = { "${toString startPos.r},${toString startPos.c}" = 1; };
167
+
finalStates = simulate initialStates 0;
169
+
# Sum all timeline counts
170
+
totalTimelines = builtins.foldl' (acc: key:
171
+
acc + builtins.getAttr key finalStates
172
+
) 0 (builtins.attrNames finalStates);
176
+
inherit part1 part2;