Day 07 - Laboratories
Language: Rust
Problem https://adventofcode.com/2025/day/7
Part 1:
A beam enters at S and moves straight down. Empty cells do nothing, and a splitter ^
kills the current beam and spawns two new ones going left and right.
The key insight is that you don’t need to simulate full beam paths. After each row, all that
matters is which columns are currently active. So I track a HashSet<usize> of active columns
and rebuild it row by row:
for r in 1..h {
let row = &grid[r];
let mut new_beams = HashSet::new();
for c in 0..row.len() {
if row[c] == '^' && active_beams.contains(&c) {
splits += 1;
new_beams.insert(c - 1);
new_beams.insert(c + 1);
} else if active_beams.contains(&c) && row[c] != '^' {
new_beams.insert(c);
}
}
active_beams = new_beams;
}
Not elegant, but readable and it works. The answer is just the total split count.
Part 2: Now the question is how many timelines exist at the end, not how many splits occurred. That sounds different but it’s really the same structure, just tracking counts instead of presence.
Instead of a HashSet of active columns, I use a HashMap<column, count> where the count
is how many timelines are sitting in that column. Each row is then a transition: empty cells
pass counts straight down, splitters divide the count into the two neighbor columns:
for r in 1..h {
let mut next = HashMap::new();
for (&c, &count) in &timelines {
match grid[r][c] {
'.' => { *next.entry(c).or_insert(0) += count; }
'^' => {
*next.entry(c - 1).or_insert(0) += count;
*next.entry(c + 1).or_insert(0) += count;
}
_ => { *next.entry(c).or_insert(0) += count; }
}
}
timelines = next;
}
The answer is the sum of all counts at the end. It’s basically dynamic programming, just
phrased as timelines. HashSet for “exists”, HashMap for “how many”. Same idea, one more
level of information. Obvious in hindsight, took me longer than I’d like to admit to get there.
Solution: https://github.com/Elyrial/AdventOfCode/blob/main/src/solutions/year2025/day07.rs
No C writeup yet.