advent-of-code 2025.5.0

Solutions to Advent of Code
Documentation
use crate::input::Input;

pub struct Map {
    pub rows: usize,
    pub cols: usize,
    grid: Vec<bool>,
}

impl Map {
    fn parse(input: &str) -> Result<Self, String> {
        let rows = input.lines().count();
        let cols = input.lines().next().ok_or("Empty input")?.len();
        let grid: Vec<bool> = input
            .bytes()
            .filter_map(|c| if c == b'\n' { None } else { Some(c == b'#') })
            .collect();

        if grid.len() != cols * rows {
            return Err("Not all rows have equal length".into());
        } else if rows <= 2 || cols <= 2 {
            return Err("Too small grid".into());
        }

        Ok(Self { rows, cols, grid })
    }

    pub fn tree_at(&self, x: usize, y: usize) -> bool {
        // The map repeats to the right many times:
        let x = x % self.cols;
        self.grid[x + y * self.cols]
    }
}

pub fn solve(input: &Input) -> Result<u64, String> {
    let map = Map::parse(input.text)?;
    let slopes = input.part_values(vec![(3, 1)], vec![(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]);

    Ok(slopes.iter().fold(1, |acc, slope| {
        let initial_position = (0, 0);

        let trees_seen_now = std::iter::successors(Some(initial_position), |pos| {
            let new_pos = (pos.0 + slope.0, pos.1 + slope.1);
            if new_pos.1 < map.rows {
                Some(new_pos)
            } else {
                None
            }
        })
        .map(|(x, y)| map.tree_at(x, y))
        .filter(|tree| *tree)
        .count();

        acc * (trees_seen_now as u64)
    }))
}

#[test]
pub fn tests() {
    test_part_one!("..##.......
#...#...#..
.#....#..#.
..#.#...#.#
.#...##..#.
..#.##.....
.#.#.#....#
.#........#
#.##...#...
#...##....#
.#..#...#.#" => 7);

    test_part_one_error!("#.\n.\n" => "Not all rows have equal length");

    let real_input = include_str!("day03_input.txt");
    test_part_one!(real_input => 286);
    test_part_two!(real_input => 3_638_606_400);

    let real_input = include_str!("day03_input_2.txt");
    test_part_one!(real_input => 169);
    test_part_two!(real_input => 7_560_370_818);
}