Skip to content

Commit 2107e13

Browse files
committed
Day 16
1 parent d0b8e0a commit 2107e13

File tree

6 files changed

+817
-124
lines changed

6 files changed

+817
-124
lines changed

src/day15.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use core::fmt;
22

33
use crate::{
44
grid::{Direction, Grid, GridParseError, GridParser, Index, Point, Vector},
5-
util::bfs,
5+
graph::bfs,
66
};
77

88
pub fn solve(input: &str) -> (usize, usize) {

src/day16.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use itertools::Itertools;
2+
3+
use crate::{
4+
graph::{dijkstra, dijkstra_all},
5+
grid::{Direction, Grid, GridParseError, GridParser, Index},
6+
util::IteratorExt,
7+
};
8+
9+
pub fn solve(input: &str) -> (usize, usize) {
10+
let (start, end, map) = parse(input).unwrap();
11+
12+
// println!(
13+
// "{}",
14+
// map.display(
15+
// |_| None,
16+
// |t| match t {
17+
// Thing::Wall => '#',
18+
// Thing::Floor => '.',
19+
// }
20+
// )
21+
// );
22+
23+
let (end_dir, cost) = part1(start.clone(), end, &map);
24+
let count_on_best_path = part2(
25+
start,
26+
Pose {
27+
position: end,
28+
heading: end_dir,
29+
},
30+
&map,
31+
);
32+
33+
(cost, count_on_best_path)
34+
}
35+
36+
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
37+
struct Pose {
38+
position: Index,
39+
heading: Direction,
40+
}
41+
42+
impl Ord for Pose {
43+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
44+
let pos: (_, _) = self.position.into();
45+
pos.cmp(&other.position.into())
46+
.then_with(|| self.heading.cmp(&other.heading))
47+
}
48+
}
49+
50+
impl PartialOrd for Pose {
51+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
52+
Some(self.cmp(&other))
53+
}
54+
}
55+
56+
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
57+
enum Thing {
58+
Wall,
59+
Floor,
60+
}
61+
62+
fn cost(path: &[Pose]) -> usize {
63+
path.iter()
64+
.tuple_windows()
65+
.map(|(p1, p2)| if p1.heading != p2.heading { 1000 } else { 1 })
66+
.sum()
67+
}
68+
69+
fn part1(start: Pose, end: Index, map: &Grid<Thing>) -> (Direction, usize) {
70+
// need to check all 4 possible end-headings
71+
Direction::all()
72+
.iter()
73+
.map(|&d| Pose {
74+
position: end,
75+
heading: d,
76+
})
77+
.map(|e| {
78+
(
79+
e.heading,
80+
dijkstra(start.clone(), |p| movements(p, map), e)
81+
.unwrap() // we're sure there's a way to the end
82+
.collect::<Vec<_>>(),
83+
)
84+
})
85+
.map(|(dir, path)| (dir, cost(&path)))
86+
.min_by_key(|(_, cost)| *cost)
87+
.unwrap() // we know the iterator is not empty
88+
}
89+
90+
fn movements(pose: &Pose, map: &Grid<Thing>) -> impl IntoIterator<Item = (usize, Pose)> {
91+
let straight = pose.position.neighbor(pose.heading);
92+
[
93+
map.at(straight).and_then(|t| match t {
94+
Thing::Floor => Some((
95+
1,
96+
Pose {
97+
position: straight,
98+
heading: pose.heading,
99+
},
100+
)),
101+
Thing::Wall => None,
102+
}),
103+
Some((
104+
1000,
105+
Pose {
106+
position: pose.position,
107+
heading: pose.heading.clockwise(),
108+
},
109+
)),
110+
Some((
111+
1000,
112+
Pose {
113+
position: pose.position,
114+
heading: pose.heading.anti_clockwise(),
115+
},
116+
)),
117+
]
118+
.into_iter()
119+
.filter_map(|p| p)
120+
}
121+
122+
fn part2(start: Pose, end: Pose, map: &Grid<Thing>) -> usize {
123+
dijkstra_all(start, |p| movements(p, map), end)
124+
.into_iter()
125+
.flatten()
126+
.map(|p| p.position)
127+
.uniques()
128+
.count()
129+
}
130+
131+
fn parse(input: &str) -> Result<(Pose, Index, Grid<Thing>), GridParseError> {
132+
let map = GridParser::new(|c| match c {
133+
'#' => Ok(Thing::Wall),
134+
'.' => Ok(Thing::Floor),
135+
'S' => Ok(Thing::Floor),
136+
'E' => Ok(Thing::Floor),
137+
_ => Err(GridParseError),
138+
})
139+
.parse(input)?;
140+
141+
let start = input
142+
.chars()
143+
.filter(|c| !c.is_whitespace())
144+
.enumerate()
145+
.filter_map(|(i, c)| match c {
146+
'S' => Some(map.make_index(i)),
147+
_ => None,
148+
})
149+
.next()
150+
.ok_or(GridParseError)?;
151+
152+
let start = Pose {
153+
position: start,
154+
heading: Direction::Right,
155+
};
156+
157+
let end = input
158+
.chars()
159+
.filter(|c| !c.is_whitespace())
160+
.enumerate()
161+
.filter_map(|(i, c)| match c {
162+
'E' => Some(map.make_index(i)),
163+
_ => None,
164+
})
165+
.next()
166+
.ok_or(GridParseError)?;
167+
168+
Ok((start, end, map))
169+
}

0 commit comments

Comments
 (0)