From 79b809c3b7bf91d0ffc20999be999ba4ee3a4fbf Mon Sep 17 00:00:00 2001 From: Lars Martens Date: Tue, 10 Dec 2024 07:37:53 +0100 Subject: [PATCH] Solve 10 --- src/bin/10.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/bin/10.rs diff --git a/src/bin/10.rs b/src/bin/10.rs new file mode 100644 index 0000000..a0f6fa4 --- /dev/null +++ b/src/bin/10.rs @@ -0,0 +1,111 @@ +use aoc::*; +use glam::IVec2; +use std::collections::{HashMap, HashSet}; +use std::iter; + +const INPUT: &str = include_str!("../../input/10"); + +fn main() { + assert_example!(part1, "10-test", 36); + println!("Part 1: {}", part1(INPUT)); + assert_example!(part2, "10-test", 81); + println!("Part 2: {}", part2(INPUT)); +} + +fn part1(input: &str) -> usize { + let map = Map::parse(input); + map.trailheads().map(|t| map.reachable(t).len()).sum() +} + +fn part2(input: &str) -> usize { + let map = Map::parse(input); + map.trailheads().map(|t| map.distinct_trails(t)).sum() +} + +fn directions() -> [IVec2; 4] { + [ + IVec2::new(1, 0), + IVec2::new(-1, 0), + IVec2::new(0, 1), + IVec2::new(0, -1), + ] +} + +#[derive(Debug)] +struct Map { + height: HashMap, +} + +impl Map { + fn reachable(&self, from: IVec2) -> HashSet { + let from_height = match self.get_height(from) { + Some(9) => return [from].into(), + None => return [].into(), + Some(h) => h, + }; + + self.climb_up(from, from_height) + .into_iter() + .map(|pos| self.reachable(pos)) + .fold(HashSet::new(), |mut acc, other| { + acc.extend(other); + acc + }) + } + + fn distinct_trails(&self, from: IVec2) -> usize { + let from_height = match self.get_height(from) { + Some(9) => return 1, + None => return 0, + Some(h) => h, + }; + + self.climb_up(from, from_height) + .into_iter() + .map(|pos| self.distinct_trails(pos)) + .sum() + } + + fn climb_up(&self, from: IVec2, from_height: i64) -> Vec { + directions() + .into_iter() + .filter_map(|dir| { + let pos = from + dir; + self.get_height(pos).map(|height| (pos, height)) + }) + .filter_map(|(pos, h)| { + if h == from_height + 1 { + Some(pos) + } else { + None + } + }) + .collect() + } + + fn get_height(&self, at: IVec2) -> Option { + self.height.get(&at).copied() + } + + fn trailheads(&self) -> impl Iterator + use<'_> { + self.height.iter().filter_map(|(&pos, &h)| match h { + 0 => Some(pos), + _ => None, + }) + } + + fn parse(input: &str) -> Self { + let height = input + .lines() + .enumerate() + .flat_map(|(y, line)| line.chars().enumerate().zip(iter::repeat(y))) + .map(|((x, c), y)| { + let pos = IVec2::new(x as i32, y as i32); + let height = c.to_digit(10).unwrap() as i64; + (pos, height) + }) + .collect(); + + Self { height } + } +}