Skip to content

Commit ae979ca

Browse files
committed
Day 16
1 parent d0b8e0a commit ae979ca

File tree

6 files changed

+816
-124
lines changed

6 files changed

+816
-124
lines changed

src/day15.rs

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

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

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

src/day16.rs

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

0 commit comments

Comments
 (0)