use indexmap::IndexSet;
use num_traits::Zero;
use std::{hash::Hash, ops::ControlFlow};
pub fn idastar<N, C, FN, IN, FH, FS>(
start: &N,
mut successors: FN,
mut heuristic: FH,
mut success: FS,
) -> Option<(Vec<N>, C)>
where
N: Eq + Clone + Hash,
C: Zero + Ord + Copy,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = (N, C)>,
FH: FnMut(&N) -> C,
FS: FnMut(&N) -> bool,
{
let mut path = IndexSet::from([start.clone()]);
std::iter::repeat(())
.try_fold(heuristic(start), |bound, ()| {
search(
&mut path,
Zero::zero(),
bound,
&mut successors,
&mut heuristic,
&mut success,
)
.map_break(Some)?
.map_or(ControlFlow::Break(None), ControlFlow::Continue)
})
.break_value()
.unwrap_or_default() }
fn search<N, C, FN, IN, FH, FS>(
path: &mut IndexSet<N>,
cost: C,
bound: C,
successors: &mut FN,
heuristic: &mut FH,
success: &mut FS,
) -> ControlFlow<(Vec<N>, C), Option<C>>
where
N: Eq + Clone + Hash,
C: Zero + Ord + Copy,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = (N, C)>,
FH: FnMut(&N) -> C,
FS: FnMut(&N) -> bool,
{
let neighbs = {
let start = &path[path.len() - 1];
let f = cost + heuristic(start);
if f > bound {
return ControlFlow::Continue(Some(f));
}
if success(start) {
return ControlFlow::Break((path.iter().cloned().collect(), f));
}
let mut neighbs: Vec<(N, C, C)> = successors(start)
.into_iter()
.filter_map(|(n, c)| {
(!path.contains(&n)).then(|| {
let h = heuristic(&n);
(n, c, c + h)
})
})
.collect::<Vec<_>>();
neighbs.sort_unstable_by(|(_, _, c1), (_, _, c2)| c1.cmp(c2));
neighbs
};
let mut min = None;
for (node, extra, _) in neighbs {
let (idx, _) = path.insert_full(node);
match search(path, cost + extra, bound, successors, heuristic, success)? {
Some(m) if min.is_none_or(|n| n >= m) => min = Some(m),
_ => (),
}
path.swap_remove_index(idx);
}
ControlFlow::Continue(min)
}