use super::super::{ordered_insert, Cost, Path};
use crate::{node_id::*, NodeID};
pub fn dijkstra_search<NeighborIter: Iterator<Item = (NodeID, Cost)>>(
mut get_all_neighbors: impl FnMut(NodeID) -> NeighborIter,
mut is_walkable: impl FnMut(NodeID) -> bool,
start: NodeID,
goals: &[NodeID],
) -> NodeIDMap<Path<NodeID>> {
let mut visited = NodeIDMap::default();
let mut next = vec![(start, 0)];
visited.insert(start, (0, start));
let mut remaining_goals = goals.to_vec();
let mut goal_costs = NodeIDMap::with_capacity_and_hasher(goals.len(), Default::default());
while let Some((current_id, _)) = next.pop() {
let cost = visited[¤t_id].0;
let mut found_one = false;
for &goal_id in remaining_goals.iter() {
if current_id == goal_id {
goal_costs.insert(goal_id, cost);
found_one = true;
}
}
if found_one {
remaining_goals.retain(|&id| id != current_id);
if remaining_goals.is_empty() {
break;
}
}
for (other_id, delta_cost) in get_all_neighbors(current_id) {
let other_cost = cost + delta_cost;
if !is_walkable(other_id) {
let mut is_goal = false;
for &goal_id in remaining_goals.iter() {
if other_id == goal_id {
is_goal = true;
}
}
if !is_goal {
continue;
}
}
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), |&(_, cost)| cost);
visited.insert(other_id, (other_cost, current_id));
}
}
}
let mut goal_data = NodeIDMap::with_capacity_and_hasher(goal_costs.len(), Default::default());
for (&goal, &cost) in goal_costs.iter() {
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
};
goal_data.insert(goal, Path::new(steps, cost));
}
goal_data
}