Skip to content

Commit ad69dc7

Browse files
committed
chore: y2024::day_10
1 parent 3189115 commit ad69dc7

File tree

5 files changed

+187
-1
lines changed

5 files changed

+187
-1
lines changed

aoclp/src/dij.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::collections::hash_map::Entry;
2+
use std::collections::{HashMap, HashSet};
3+
use std::hash::Hash;
4+
5+
pub trait Graph<T> {
6+
fn neighbours(&self, node: &T) -> impl Iterator<Item = T>;
7+
fn dist(&self, a: &T, b: &T) -> usize {
8+
let (_, _) = (a, b);
9+
1
10+
}
11+
}
12+
13+
pub struct Output<T> {
14+
pub dist: HashMap<T, usize>,
15+
pub prev: HashMap<T, T>,
16+
}
17+
18+
pub fn build<T, G>(graph: &G, start: T) -> Output<T>
19+
where
20+
T: Clone + Eq + Hash,
21+
G: Graph<T>,
22+
{
23+
// https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
24+
25+
let mut dist = HashMap::new();
26+
let mut prev = HashMap::new();
27+
dist.insert(start.clone(), 0);
28+
29+
let mut q = HashSet::new();
30+
q.insert(start.clone());
31+
32+
while !q.is_empty() {
33+
let u = q
34+
.iter()
35+
.map(|n| (n.clone(), dist.get(n).copied().unwrap_or(usize::MAX)))
36+
.min_by_key(|(_, u_dist)| *u_dist);
37+
match u {
38+
None => break,
39+
Some((u, u_dist)) => {
40+
q.remove(&u);
41+
graph.neighbours(&u).for_each(|v| {
42+
let alt = u_dist + graph.dist(&u, &v);
43+
match dist.entry(v.clone()) {
44+
Entry::Vacant(e) => {
45+
q.insert(v.clone());
46+
e.insert(alt);
47+
prev.insert(v, u.clone());
48+
},
49+
Entry::Occupied(mut e) if alt < *e.get() => {
50+
e.insert(alt);
51+
prev.insert(v, u.clone());
52+
},
53+
_ => (),
54+
}
55+
});
56+
},
57+
}
58+
}
59+
60+
Output { dist, prev }
61+
}
62+
63+
pub fn assemble_path<T>(prev: &HashMap<T, T>, start: &T, end: &T) -> impl Iterator<Item = T>
64+
where
65+
T: Clone + Eq + Hash,
66+
{
67+
let mut path = Vec::new();
68+
let mut n = end.clone();
69+
while n != *start {
70+
path.push(n.clone());
71+
n = match prev.get(&n) {
72+
Some(p) => p.clone(),
73+
None => break,
74+
}
75+
}
76+
path.push(start.clone());
77+
path.into_iter().rev()
78+
}

aoclp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Helper library for Advent of Code.
22
33
pub mod captures;
4+
pub mod dij;
45
pub mod forth;
56
pub mod functional;
67
pub mod looping;

aoclp_solutions/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ pub mod y2025;
1010

1111
build_solvers! {
1212
{ 2017, [01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] },
13-
{ 2024, [01, 02, 03, 04, 05, 06, 07, 08, 09] },
13+
{ 2024, [01, 02, 03, 04, 05, 06, 07, 08, 09, 10] },
1414
{ 2025, [01, 02, 03, 04, 05, 06, 07] }
1515
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::collections::HashMap;
2+
3+
use aoclp::dij;
4+
use aoclp::positioning::direction::four_points::Direction4;
5+
use aoclp::positioning::pt::{matrix_to_map, Pt};
6+
use aoclp::solvers_impl::input::safe_get_input_as_terrain;
7+
use strum::IntoEnumIterator;
8+
9+
pub fn part_1() -> usize {
10+
Map::default().trailheads().map(|h| h.score).sum()
11+
}
12+
13+
pub fn part_2() -> usize {
14+
Map::default().trailheads().map(|h| h.rating).sum()
15+
}
16+
17+
#[derive(Debug, Copy, Clone)]
18+
struct Tile {
19+
height: usize,
20+
}
21+
22+
impl From<char> for Tile {
23+
fn from(c: char) -> Self {
24+
Self { height: c.to_digit(10).unwrap() as usize }
25+
}
26+
}
27+
28+
#[derive(Debug, Copy, Clone)]
29+
struct Trailhead {
30+
start: Pt,
31+
score: usize,
32+
rating: usize,
33+
}
34+
35+
#[derive(Debug)]
36+
struct Map {
37+
heightmap: HashMap<Pt, usize>,
38+
}
39+
40+
impl Map {
41+
fn trailheads(&self) -> impl Iterator<Item = Trailhead> + use<'_> {
42+
self.heightmap
43+
.iter()
44+
.filter(|(_, &t)| t == 0)
45+
.filter_map(move |(&p, _)| {
46+
let dist = dij::build(self, p).dist;
47+
let score = dist.values().filter(|d| **d == 9).count();
48+
if score == 0 {
49+
return None;
50+
}
51+
52+
let mut cache = HashMap::new();
53+
let trailhead = Trailhead {
54+
start: p,
55+
score,
56+
rating: dist
57+
.iter()
58+
.filter(|(_, d)| **d == 9)
59+
.map(|(&p, _)| Self::path_count(p, &dist, &mut cache))
60+
.sum(),
61+
};
62+
Some(trailhead)
63+
})
64+
}
65+
66+
fn path_count(p: Pt, dist: &HashMap<Pt, usize>, cache: &mut HashMap<Pt, usize>) -> usize {
67+
if let Some(count) = cache.get(&p) {
68+
return *count;
69+
}
70+
71+
let p_dist = dist[&p];
72+
if p_dist == 0 {
73+
return 1;
74+
}
75+
76+
let count = Direction4::iter()
77+
.map(|d| p + d)
78+
.filter(|n| dist.get(n).is_some_and(|d| *d == p_dist - 1))
79+
.map(|n| Self::path_count(n, dist, cache))
80+
.sum();
81+
cache.insert(p, count);
82+
83+
count
84+
}
85+
}
86+
87+
impl dij::Graph<Pt> for Map {
88+
fn neighbours(&self, node: &Pt) -> impl Iterator<Item = Pt> {
89+
let height = self.heightmap[node];
90+
Direction4::iter()
91+
.map(|d| *node + d)
92+
.filter(move |n| self.heightmap.get(n).is_some_and(|h| *h == height + 1))
93+
}
94+
}
95+
96+
impl Default for Map {
97+
fn default() -> Self {
98+
Self {
99+
heightmap: matrix_to_map(
100+
safe_get_input_as_terrain::<Tile>(2024, 10)
101+
.into_iter()
102+
.map(|y| y.into_iter().map(|t| t.height)),
103+
),
104+
}
105+
}
106+
}

aoclp_solutions/src/y2024/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pub mod day_06;
77
pub mod day_07;
88
pub mod day_08;
99
pub mod day_09;
10+
pub mod day_10;

0 commit comments

Comments
 (0)