aoc/year2025/
day07.rs

1//! # Laboratories
2//!
3//! Examining the input shows that it consists of a triangular Christmas tree shape with both every
4//! second line and second space blank. Two splitters will never occur immediately next to each
5//! other. This structure speeds up and simplifies the solution, and we compute both parts together.
6//!
7//! The key insight to part two is that we only need the *total count* of paths, not each
8//! separate path. This means that if 2 paths enter a tile from the left and another 2 from the
9//! right, then we can simply sum the paths to 4. A dynamic programming approach counts the total
10//! number of paths one row at a time.
11//!
12//! When a beam hits a splitter, the count underneath the splitter will be zero, and the number
13//! of beams to either side is incremented by the count of the beams hitting the splitter.
14type Input = (u64, u64);
15
16pub fn parse(input: &str) -> Input {
17    let lines: Vec<_> = input.lines().map(str::as_bytes).collect();
18    let width = lines[0].len();
19    let center = width / 2;
20
21    let mut splits = 0;
22    let mut timelines = vec![0; width];
23    timelines[center] = 1;
24
25    // Only process every second line and every second tile on each line,
26    // starting in the center and growing in a triangle by 1 tile in each direction.
27    for (y, row) in lines.iter().skip(2).step_by(2).enumerate() {
28        for x in ((center - y)..(center + y + 1)).step_by(2) {
29            let count = timelines[x];
30
31            // Not all splitters are reachable, so check that there are beams from above.
32            if count > 0 && row[x] == b'^' {
33                splits += 1;
34                timelines[x] = 0;
35                timelines[x - 1] += count;
36                timelines[x + 1] += count;
37            }
38        }
39    }
40
41    (splits, timelines.iter().sum())
42}
43
44pub fn part1(input: &Input) -> u64 {
45    input.0
46}
47
48pub fn part2(input: &Input) -> u64 {
49    input.1
50}