advent-of-code/2022/day12/rust/src/main.rs
2022-12-12 20:00:20 +01:00

93 lines
2.6 KiB
Rust

#![warn(clippy::pedantic)]
use std::io::stdin;
use petgraph::{
algo::k_shortest_path,
graph::{Graph, NodeIndex},
visit::IntoNodeReferences,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NodeKind {
Start,
End,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct NodeData {
kind: Option<NodeKind>,
height: u32,
}
fn main() {
// graph of "downhill" edges - edge from A to B means that A is reachable from B
let mut graph = Graph::<NodeData, (), _>::new();
let grid: Vec<Vec<NodeIndex>> = stdin()
.lines()
.map(|line| {
line.unwrap()
.chars()
.map(|c| {
let (kind, c) = match c {
'S' => (Some(NodeKind::Start), 'a'),
'E' => (Some(NodeKind::End), 'z'),
c => (None, c),
};
let height = u32::from(c) - u32::from('a');
let data = NodeData { kind, height };
graph.add_node(data)
})
.collect()
})
.collect();
for (x, row) in grid.iter().enumerate() {
for (y, &node_id) in row.iter().enumerate() {
let node = graph[node_id];
let x = i32::try_from(x).unwrap();
let y = i32::try_from(y).unwrap();
let neighbours =
[(-1, 0), (0, -1), (1, 0), (0, 1)]
.into_iter()
.filter_map(|(dx, dy)| {
let x = usize::try_from(x + dx).ok()?;
let y = usize::try_from(y + dy).ok()?;
grid.get(x)?.get(y)
});
for &neighbour_id in neighbours {
let neighbour = graph[neighbour_id];
if node.height <= (neighbour.height + 1) {
// `node` is reachable from `neighbour`
graph.add_edge(node_id, neighbour_id, ());
}
}
}
}
let end = graph
.node_references()
.find_map(|(id, node)| (node.kind == Some(NodeKind::End)).then_some(id))
.unwrap();
let paths = k_shortest_path(&graph, end, None, 1, |_| 1);
let start = graph
.node_references()
.find_map(|(id, node)| (node.kind == Some(NodeKind::Start)).then_some(id))
.unwrap();
println!("{:?}", paths[&start]);
println!(
"{:?}",
paths
.into_iter()
.filter_map(|(start_id, steps)| (graph[start_id].height == 0).then_some(steps))
.min()
.unwrap()
);
}