···1111# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12121313[dependencies]
1414+indexmap = "1.9.2"
1415nom = "7"
···11-use crate::solutions::year_2022::{Day2, Day3, Day4, Day5, Day6, Day7, Day8, Day9, Day10, Day11};
11+use crate::solutions::year_2022::{
22+ Day10, Day11, Day12, Day2, Day3, Day4, Day5, Day6, Day7, Day8, Day9,
33+};
24use aoc_lib::Solver;
35pub mod solutions;
4657fn main() {
66- let sol = Day11::solve_part2();
88+ let sol = Day12::solve_part2();
79 println!("{:?}", sol);
810}
+259
src/solutions/year_2022/day12.rs
···11+use std::{collections::BinaryHeap, fmt::Debug, hash::Hash, iter::repeat_with};
22+33+use aoc_lib::{Input, Solver};
44+use indexmap::{
55+ map::Entry::{Occupied, Vacant},
66+ IndexMap,
77+};
88+99+pub struct Day12 {
1010+ grid: Vec<Vec<Pos>>,
1111+ start: Pos,
1212+ goal: Pos,
1313+}
1414+1515+impl Solver for Day12 {
1616+ fn day() -> u8 {
1717+ 12
1818+ }
1919+ type OutputPart1 = usize;
2020+ type OutputPart2 = usize;
2121+ fn solution_part1(input: aoc_lib::Input) -> Option<Self::OutputPart1> {
2222+ let d = Day12::setup(input);
2323+ let ret = shortest_path(&d.start, |p| d.successors(p, false), |g| d.reached_end(g));
2424+ ret.map(|(xs, _)| xs.len() - 1)
2525+ }
2626+ fn solution_part2(input: aoc_lib::Input) -> Option<Self::OutputPart2> {
2727+ let d = Day12::setup(input);
2828+ let ret = shortest_path(&d.goal, |p| d.successors(p, true), |s| d.reached_a(s));
2929+ ret.map(|(xs, _)| xs.len() - 1)
3030+ }
3131+}
3232+3333+impl Day12 {
3434+ fn setup(input: Input) -> Self {
3535+ let mut start = None;
3636+ let mut goal = None;
3737+ let grid = input
3838+ .lines
3939+ .iter()
4040+ .enumerate()
4141+ .map(|(row, line)| {
4242+ line.chars()
4343+ .enumerate()
4444+ .map(|(col, c)| {
4545+ if c == 'S' {
4646+ let pos = Pos(row as i32, col as i32, 'a');
4747+ start = Some(pos);
4848+ pos
4949+ } else if c == 'E' {
5050+ let pos = Pos(row as i32, col as i32, 'z');
5151+ goal = Some(pos);
5252+ pos
5353+ } else {
5454+ Pos(row as i32, col as i32, c)
5555+ }
5656+ })
5757+ .collect::<Vec<_>>()
5858+ })
5959+ .collect::<Vec<Vec<_>>>();
6060+ Day12 {
6161+ grid,
6262+ start: start.unwrap(),
6363+ goal: goal.unwrap(),
6464+ }
6565+ }
6666+6767+ fn index(&self, x: &i32, y: &i32, compare: &char, desc: bool) -> Option<Pos> {
6868+ self.grid
6969+ .get(*x as usize)
7070+ .and_then(|r| r.get(*y as usize))
7171+ .and_then(|c| {
7272+ if (desc && (c.2 as u32) + 1 >= (*compare) as u32)
7373+ || (!desc && (c.2 as u32) <= ((*compare) as u32) + 1)
7474+ {
7575+ Some(Pos(*x, *y, c.2))
7676+ } else {
7777+ None
7878+ }
7979+ })
8080+ }
8181+8282+ fn successors(&self, Pos(x, y, c): &Pos, desc: bool) -> Vec<(Pos, usize)> {
8383+ vec![(-1, 0), (1, 0), (0, -1), (0, 1)]
8484+ .iter()
8585+ .map(|(dx, dy)| self.index(&(x + dx), &(y + dy), c, desc))
8686+ .into_iter()
8787+ .filter(|p| p.is_some())
8888+ .map(|p| (p.unwrap(), 1))
8989+ .collect()
9090+ }
9191+9292+ fn reached_end(&self, p: &Pos) -> bool {
9393+ p == &self.goal
9494+ }
9595+9696+ fn reached_a(&self, p: &Pos) -> bool {
9797+ p.2 == 'a'
9898+ }
9999+}
100100+101101+fn reverse_paths<Node: Eq + Hash + Clone + Copy, F>(
102102+ prevs: &mut IndexMap<Node, (VisitedIndex, Weight)>,
103103+ mut prev: F,
104104+ start: usize,
105105+) -> Vec<Node>
106106+where
107107+ F: FnMut(&(VisitedIndex, Weight)) -> usize,
108108+{
109109+ let mut i = start;
110110+ let xs = repeat_with(|| {
111111+ let ps = prevs.clone();
112112+ ps.get_index(i)
113113+ .map(|(n, t)| {
114114+ i = prev(t);
115115+ n
116116+ })
117117+ .copied()
118118+ })
119119+ .take_while(|x| x.is_some())
120120+ .flatten();
121121+122122+ let mut y = xs.collect::<Vec<_>>();
123123+ y.reverse();
124124+ y
125125+}
126126+127127+fn shortest_path<Node, FN, IN, FS>(
128128+ start_node: &Node,
129129+ mut next_nodes: FN,
130130+ mut stop_check: FS,
131131+) -> Option<(Vec<Node>, Weight)>
132132+where
133133+ Node: Eq + Hash + Clone + Debug + Copy,
134134+ FN: FnMut(&Node) -> IN,
135135+ IN: IntoIterator<Item = (Node, Weight)> + Debug,
136136+ FS: FnMut(&Node) -> bool,
137137+{
138138+ let mut q: BinaryHeap<State> = BinaryHeap::new();
139139+ let mut prev: IndexMap<Node, (VisitedIndex, Weight)> = IndexMap::new();
140140+141141+ // initiate queue
142142+ q.push(State::default());
143143+ // initiate prev
144144+ prev.insert(*start_node, (usize::MAX, 0));
145145+146146+ let mut destination_index: Option<usize> = None;
147147+ while let Some(State { weight, index }) = q.pop() {
148148+ let (node, (_, w)) = prev.get_index(index).unwrap();
149149+ // check if we need to stop
150150+ if stop_check(node) {
151151+ destination_index = Some(index);
152152+ break;
153153+ }
154154+155155+ // if we have already seen the node and the last path to it was best then
156156+ // don't bother, continue
157157+ if weight > *w {
158158+ continue;
159159+ }
160160+161161+ // at this point we need to look at the successors
162162+ let next_nodes = next_nodes(node);
163163+164164+ for (next_node, travel_weight) in next_nodes {
165165+ let new_weight = weight + travel_weight;
166166+ let i;
167167+ match prev.entry(next_node) {
168168+ Occupied(visited) => {
169169+ let visited_weight = visited.get().1;
170170+ if visited_weight > new_weight {
171171+ i = visited.index();
172172+ prev.insert(next_node, (index, new_weight));
173173+ } else {
174174+ continue;
175175+ }
176176+ }
177177+ Vacant(unvisited) => {
178178+ i = unvisited.index();
179179+ prev.insert(next_node, (index, new_weight));
180180+ }
181181+ }
182182+183183+ q.push(State {
184184+ index: i,
185185+ weight: new_weight,
186186+ });
187187+ }
188188+ }
189189+190190+ destination_index.map(|target| {
191191+ (
192192+ reverse_paths(&mut prev, |&(p, _)| p, target),
193193+ prev.get_index(target).unwrap().1 .1,
194194+ )
195195+ })
196196+}
197197+198198+type VisitedIndex = usize;
199199+type Weight = usize;
200200+201201+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
202202+struct Pos(i32, i32, char);
203203+204204+#[derive(Debug, Default)]
205205+struct State {
206206+ weight: Weight,
207207+ index: VisitedIndex,
208208+}
209209+210210+impl PartialEq for State {
211211+ fn eq(&self, other: &Self) -> bool {
212212+ self.weight == other.weight
213213+ }
214214+}
215215+impl Eq for State {}
216216+217217+impl Ord for State {
218218+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
219219+ other
220220+ .weight
221221+ .cmp(&self.weight) // this is key for Min-Priority heap
222222+ .then_with(|| self.index.cmp(&other.index)) // when tied, compare the indexes
223223+ }
224224+}
225225+226226+impl PartialOrd for State {
227227+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
228228+ Some(self.cmp(other))
229229+ }
230230+}
231231+232232+#[cfg(test)]
233233+mod tests {
234234+ use super::*;
235235+236236+ #[test]
237237+ fn test1() {
238238+ static GOAL: (i32, i32) = (4, 6);
239239+ let result = shortest_path(
240240+ &(1, 1),
241241+ |&(x, y)| {
242242+ vec![
243243+ (x + 1, y + 2),
244244+ (x + 1, y - 2),
245245+ (x - 1, y + 2),
246246+ (x - 1, y - 2),
247247+ (x + 2, y + 1),
248248+ (x + 2, y - 1),
249249+ (x - 2, y + 1),
250250+ (x - 2, y - 1),
251251+ ]
252252+ .into_iter()
253253+ .map(|p| (p, 1))
254254+ },
255255+ |&p| p == GOAL,
256256+ );
257257+ assert_eq!(result.expect("no path found").1, 4);
258258+ }
259259+}
+2
src/solutions/year_2022/mod.rs
···99mod day9;
1010mod day10;
1111mod day11;
1212+mod day12;
1213pub(crate) use day1::*;
1314pub(crate) use day2::*;
1415pub(crate) use day3::*;
···2021pub(crate) use day9::*;
2122pub(crate) use day10::*;
2223pub(crate) use day11::*;
2424+pub(crate) use day12::*;