use super::super::{ordered_insert, Cost, Path};
use crate::{node_id::*, NodeID};
pub fn a_star_search<NeighborIter: Iterator<Item = (NodeID, Cost)>>(
mut get_all_neighbors: impl FnMut(NodeID) -> NeighborIter,
mut is_walkable: impl FnMut(NodeID) -> bool,
start: NodeID,
goal: NodeID,
mut heuristic: impl FnMut(NodeID) -> Cost,
) -> Option<Path<NodeID>> {
if start == goal {
return Some(Path::from_slice(&[start, start], 0));
}
let mut visited = NodeIDMap::default();
let mut next = vec![(start, 0)];
visited.insert(start, (0, start));
'search: while let Some((current_id, _)) = next.pop() {
if current_id == goal {
break 'search;
}
let current_cost = visited[¤t_id].0;
for (other_id, delta_cost) in get_all_neighbors(current_id) {
let other_cost = current_cost + delta_cost;
if !is_walkable(other_id) && other_id != goal {
continue;
}
let heuristic = heuristic(other_id);
if let Some(&(prev_cost, _)) = visited.get(&other_id) {
if prev_cost > other_cost {
next.retain(|&(id, _)| id != other_id);
}
}
if !visited.contains_key(&other_id) || visited[&other_id].0 > other_cost {
ordered_insert(
&mut next,
(other_id, other_cost + heuristic),
|&(_, cost)| cost,
);
visited.insert(other_id, (other_cost, current_id));
}
}
}
if !visited.contains_key(&goal) {
return None;
}
let steps = {
let mut steps = vec![];
let mut current = goal;
while current != start {
steps.push(current);
let (_, prev) = visited[¤t];
current = prev;
}
steps.push(start);
steps.reverse();
steps
};
Some(Path::new(steps, visited[&goal].0))
}