diff --git a/data/examples/10.txt b/data/examples/10.txt new file mode 100644 index 0000000..cada9b3 --- /dev/null +++ b/data/examples/10.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 diff --git a/src/bin/10.rs b/src/bin/10.rs new file mode 100644 index 0000000..16e0c4b --- /dev/null +++ b/src/bin/10.rs @@ -0,0 +1,165 @@ +advent_of_code::solution!(10); + +use std::iter::successors; + +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] +struct Pos { + x: usize, + y: usize, +} + +#[derive(Debug)] +struct Topo { + map: Vec>, + max: Pos, +} + +impl Topo { + fn init(input: &str) -> Result { + let map: Vec> = input + .strip_suffix("\n") + .unwrap_or(input) + .lines() + .map(|l| { + l.chars() + .flat_map(|c| c.to_digit(10u32)) + .map(|d| d as u8) + .collect() + }) + .collect(); + let max = Pos { + x: map[0].len(), + y: map.len(), + }; + Ok(Self { map, max }) + } + + fn get_trailheads(&self) -> Vec { + self.map + .clone() + .into_iter() + .enumerate() + .flat_map(|(y, l)| { + l.into_iter() + .enumerate() + .map(move |(x, c)| (Pos { x, y }, c)) + }) + .filter_map(|(p, c)| if c == 0 { Some(p) } else { None }) + .collect() + } + + fn get_neighbors(&self, p: &Pos) -> Vec<(Pos, u8)> { + let mut res: Vec<(Pos, u8)> = Vec::new(); + if p.x > 0 { + let x = p.x - 1; + let y = p.y; + res.push((Pos { x, y }, self.map[y][x])); + } + if p.y > 0 { + let x = p.x; + let y = p.y - 1; + res.push((Pos { x, y }, self.map[y][x])); + } + if p.x < self.max.x - 1 { + let x = p.x + 1; + let y = p.y; + res.push((Pos { x, y }, self.map[y][x])); + } + if p.y < self.max.y - 1 { + let x = p.x; + let y = p.y + 1; + res.push((Pos { x, y }, self.map[y][x])); + } + res + } + + fn get_successors(&self, p: (Pos, u8)) -> Vec<(Pos, u8)> { + self.get_neighbors(&p.0) + .into_iter() + .filter_map(|(p2, d2)| if d2 == p.1 + 1 { Some((p2, d2)) } else { None }) + .collect() + } + + fn dfs(&self, p: (Pos, u8), path: &mut Vec<(Pos, u8)>, all_routes: &mut Vec>) { + if p.1 == 9 { + all_routes.push(path.clone()); + return; + } + + for n in self.get_successors(p) { + path.push(n.clone()); + self.dfs(n, path, all_routes); + path.pop(); + } + } +} + +fn print_route(route: Vec<(Pos, u8)>, max: &Pos) { + println!("=========="); + for y in 0..max.y { + print!("|"); + for x in 0..max.x { + let mut found = false; + for r in &route { + if r.0.x == x && r.0.y == y { + found = true; + print!("{}", r.1); + } + } + if !found { + print!(" "); + } + } + println!("|"); + } + println!("=========="); +} + +pub fn part_one(input: &str) -> Option { + let topo = Topo::init(input).ok()?; + let ths = topo.get_trailheads(); + let mut score = 0; + + for th in ths { + let mut all_routes: Vec> = Vec::new(); + let mut path: Vec<(Pos, u8)> = vec![(th.clone(), 0)]; + topo.dfs((th, 0), &mut path, &mut all_routes); + let mut ends = all_routes + .into_iter() + .map(|v| v.last().unwrap().0.clone()) + .collect::>(); + ends.sort(); + ends.dedup(); + score += ends.len(); + } + Some(score) +} + +pub fn part_two(input: &str) -> Option { + let topo = Topo::init(input).ok()?; + let ths = topo.get_trailheads(); + let mut all_routes: Vec> = Vec::new(); + + for th in ths { + let mut path: Vec<(Pos, u8)> = vec![(th.clone(), 0)]; + topo.dfs((th, 0), &mut path, &mut all_routes); + } + Some(all_routes.len()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(36)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(81)); + } +}