Day 10
This commit is contained in:
parent
3b3a11bf7f
commit
17b62f7e79
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue