use super::super::{ordered_insert, Path};
use crate::{Point, PointMap};
pub fn dijkstra_search<NeighborIter: Iterator<Item = Point>>(
mut get_all_neighbors: impl FnMut(Point) -> NeighborIter,
mut get_cost: impl FnMut(Point) -> isize,
start: Point,
goals: &[Point],
) -> PointMap<Path<Point>> {
let mut visited = PointMap::default();
let mut next = vec![(start, 0)];
visited.insert(start, (0, start));
let mut remaining_goals = goals.to_vec();
let mut goal_costs = PointMap::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;
}
}
let delta_cost = get_cost(current_id);
if delta_cost < 0 {
continue;
}
let delta_cost = delta_cost as usize;
for other_id in get_all_neighbors(current_id) {
let other_cost = cost + delta_cost;
if get_cost(other_id) < 0 {
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 = PointMap::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
}