use super::measures::Measure;
use crate::traits::*;
use hashbrown::{
HashMap,
hash_map::Entry::{Occupied, Vacant},
};
use std::{collections::BinaryHeap, marker::PhantomData};
use super::MinScored;
pub fn astar<'a, G, F, H, IsGoal, K, T: GraphType>(
graph: &'a G,
start: usize,
mut is_goal: IsGoal,
edge_cost: F,
mut estimate_cost: H,
) -> Option<(K, Vec<usize>)>
where
G: CommonProperties<'a, T, NodeIndex = usize, EdgeIndex = usize> + ?Sized,
IsGoal: FnMut(usize) -> bool,
F: Fn(usize) -> K,
H: FnMut(usize) -> K,
K: Measure + Copy,
{
let mut visit_next = BinaryHeap::new();
let mut scores = HashMap::new(); let mut estimate_scores = HashMap::new(); let mut path_tracker = PathTracker::<G>::new();
let zero_score = K::default();
scores.insert(start, zero_score);
visit_next.push(MinScored(estimate_cost(start), start));
while let Some(MinScored(estimate_score, node)) = visit_next.pop() {
if is_goal(node) {
let path = path_tracker.reconstruct_path_to(node);
let cost = scores[&node];
return Some((cost, path));
}
let node_score = scores[&node];
match estimate_scores.entry(node) {
Occupied(mut entry) => {
if *entry.get() <= estimate_score {
continue;
}
entry.insert(estimate_score);
}
Vacant(entry) => {
entry.insert(estimate_score);
}
}
for (next_score, nb) in
graph.costed_neighbours(node, |edge_index| node_score + edge_cost(edge_index))
{
for next in nb {
match scores.entry(next) {
Occupied(mut entry) => {
if *entry.get() <= next_score {
continue;
}
entry.insert(next_score);
}
Vacant(entry) => {
entry.insert(next_score);
}
}
path_tracker.set_predecessor(next, node);
let next_estimate_score = next_score + estimate_cost(next);
visit_next.push(MinScored(next_estimate_score, next));
}
}
}
None
}
struct PathTracker<'a, G>
where
G: GraphBasics<'a, NodeIndex = usize, EdgeIndex = usize> + ?Sized,
{
came_from: HashMap<usize, usize>,
_pd: PhantomData<&'a G>,
}
impl<'a, G> PathTracker<'a, G>
where
G: GraphBasics<'a, NodeIndex = usize, EdgeIndex = usize> + ?Sized,
{
fn new() -> PathTracker<'a, G> {
PathTracker {
came_from: HashMap::new(),
_pd: PhantomData,
}
}
fn set_predecessor(&mut self, node: usize, previous: usize) {
self.came_from.insert(node, previous);
}
fn reconstruct_path_to(&self, last: usize) -> Vec<usize> {
let mut path = vec![last];
let mut current = last;
while let Some(&previous) = self.came_from.get(¤t) {
path.push(previous);
current = previous;
}
path.reverse();
path
}
}