This commit is contained in:
sepia 2024-12-10 08:22:59 -06:00
parent 3b3a11bf7f
commit 17b62f7e79
2 changed files with 112 additions and 1 deletions

109
src/day10.rs Normal file
View File

@ -0,0 +1,109 @@
use itertools::Itertools;
pub fn day10() {
let map = HikingMap::from_str(&crate::input(10));
let all_positions = (0..map.width()).cartesian_product(0..map.height());
let (scores, ratings) = all_positions
// only the 0s (trailheads)
.filter(|(x, y)| map.height_at(*x, *y) == 0)
// (score, rating) for each trailhead
.map(|(x, y)| find_trails(&map, x, y))
// (sum of scores, sum of ratings)
.fold((0, 0), |acc, e| (acc.0 + e.0, acc.1 + e.1));
println!("Sum of Trailhead Scores: {}", scores);
println!("Sum of Trailhead Ratings: {}", ratings);
}
fn find_trails(map: &HikingMap, x: usize, y: usize) -> (usize, usize) {
fn find_trails(
map: &HikingMap,
x: usize,
y: usize,
visited: &mut Vec<(usize, usize)>,
) -> (usize, usize) {
if map.height_at(x, y) == 9 {
let score = if !visited.contains(&(x, y)) { 1 } else { 0 };
visited.push((x, y));
return (score, 1);
}
Direction::all_variants()
.into_iter()
// all (nx, ny) that are accessible from (x, y)
.filter_map(|direction| map.travel(x, y, direction))
// each sum of (scores, ratings) for each adjacent position
.map(|(nx, ny)| find_trails(map, nx, ny, visited))
// sum of (scores, ratings) for this position's adjacent positions
.fold((0, 0), |acc, e| (acc.0 + e.0, acc.1 + e.1))
}
let mut visited = vec![];
find_trails(map, x, y, &mut visited)
}
struct HikingMap {
map: Vec<Vec<u8>>,
}
impl HikingMap {
fn from_str(s: &str) -> Self {
Self {
map: s
.lines()
.map(|line| {
line.chars()
.map(|c| c.to_digit(10).unwrap() as u8)
.collect()
})
.collect(),
}
}
fn travel(&self, x: usize, y: usize, direction: Direction) -> Option<(usize, usize)> {
let (nx, ny) = direction.next_position(x, y)?;
// valid positions are in-bounds and moving upwards gradually
let is_valid = nx < self.width()
&& ny < self.height()
&& self.height_at(nx, ny).checked_sub(self.height_at(x, y)) == Some(1);
is_valid.then_some((nx, ny))
}
fn height_at(&self, x: usize, y: usize) -> u8 {
self.map[y][x]
}
fn height(&self) -> usize {
self.map.len()
}
fn width(&self) -> usize {
self.map[0].len()
}
}
#[derive(Copy, Clone)]
enum Direction {
North,
East,
South,
West,
}
impl Direction {
fn all_variants() -> Vec<Direction> {
vec![Self::North, Self::East, Self::South, Self::West]
}
fn next_position(&self, x: usize, y: usize) -> Option<(usize, usize)> {
match self {
Self::North => Some((x, y.checked_sub(1)?)),
Self::East => Some((x.checked_add(1)?, y)),
Self::South => Some((x, y.checked_add(1)?)),
Self::West => Some((x.checked_sub(1)?, y)),
}
}
}

View File

@ -14,10 +14,12 @@ mod day6;
mod day7;
#[allow(dead_code)]
mod day8;
#[allow(dead_code)]
mod day9;
mod day10;
fn main() {
day9::day9();
day10::day10();
}
pub fn input(day: u8) -> String {