searchlib 0.1.2

Satisficing and optimal search algorithms
Documentation
use crate::{trace, FxIndexMap};
use indexmap::map::Entry::Vacant;
use std::{hash::Hash, thread::sleep, time::Duration};

pub struct EHCS<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER>
where
    STATE: Eq + Hash + Clone,
    SUCCESSORS: FnMut(&STATE) -> ITER,
    SUCCESS: FnMut(&STATE) -> bool,
    HEURISTIC: FnMut(&STATE) -> usize,
    ITER: IntoIterator<Item = STATE>,
{
    path: Vec<STATE>,
    h: usize,
    index: usize,
    states: FxIndexMap<STATE, usize>,
    successors: SUCCESSORS,
    success: SUCCESS,
    heuristic: HEURISTIC,
}

impl<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER> EHCS<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER>
where
    STATE: Eq + Hash + Clone,
    SUCCESSORS: FnMut(&STATE) -> ITER,
    SUCCESS: FnMut(&STATE) -> bool,
    HEURISTIC: FnMut(&STATE) -> usize,
    ITER: IntoIterator<Item = STATE>,
{
    pub fn new(
        init: &STATE,
        successors: SUCCESSORS,
        success: SUCCESS,
        heuristic: HEURISTIC,
    ) -> Self {
        Self {
            path: vec![init.clone()],
            h: usize::MAX,
            index: 0,
            states: FxIndexMap::default(),
            successors,
            success,
            heuristic,
        }
    }
}

impl<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER> Iterator
    for EHCS<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER>
where
    STATE: Eq + Hash + Clone,
    SUCCESSORS: FnMut(&STATE) -> ITER,
    SUCCESS: FnMut(&STATE) -> bool,
    HEURISTIC: FnMut(&STATE) -> usize,
    ITER: IntoIterator<Item = STATE>,
{
    type Item = Option<Vec<STATE>>;

    fn next(&mut self) -> Option<Self::Item> {
        if !self.states.is_empty() {
            let (node, _) = self.states.get_index(self.index)?;
            let h = (self.heuristic)(node);
            if h < self.h {
                self.path.append(&mut trace(&self.states, self.index));
                self.h = h;
                self.states.clear();
            } else {
                for successor in (self.successors)(node) {
                    if let Vacant(e) = self.states.entry(successor) {
                        e.insert(self.index);
                    }
                }
            }
            self.index += 1;
            return Some(None);
        }
        let node = self.path.last().unwrap();
        if (self.success)(node) {
            return Some(Some(self.path.to_owned()));
        }
        let best_successor = (self.successors)(node)
            .into_iter()
            .map(|s| ((self.heuristic)(&s), s))
            .min_by(|a, b| a.0.cmp(&b.0));
        if let Some((v, s)) = best_successor {
            let p_v = (self.heuristic)(node);
            if v < p_v {
                self.path.push(s);
                self.h = p_v;
                return Some(None);
            } else {
                self.index = 0;
                self.states.clear();
                self.states.insert(node.clone(), 0);
            }
        } else {
            return None;
        }
        Some(None)
    }
}

/// ```
/// use searchlib::ehcs::solve;
/// const INIT: (isize, isize) = (1, 1);
/// const GOAL: (isize, isize) = (4, 6);
///
/// fn successors(state: &(isize, isize)) -> Vec<(isize, isize)> {
///     let (x, y) = state;
///     vec![(x + 1, *y), (x - 1, *y),(*x, y + 1), (*x, y - 1)]
/// }
///
/// fn heuristic(state: &(isize, isize)) -> usize {
///     state.0.abs_diff(GOAL.0) + state.1.abs_diff(GOAL.1)
/// }
///
/// let result = solve(&INIT, successors, |&s| s == GOAL, heuristic);
/// assert!(result.is_some());
/// ```
pub fn solve<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER>(
    init: &STATE,
    successors: SUCCESSORS,
    success: SUCCESS,
    heuristic: HEURISTIC,
) -> Option<Vec<STATE>>
where
    STATE: Eq + Hash + Clone,
    SUCCESSORS: FnMut(&STATE) -> ITER,
    SUCCESS: FnMut(&STATE) -> bool,
    HEURISTIC: FnMut(&STATE) -> usize,
    ITER: IntoIterator<Item = STATE>,
{
    EHCS::new(init, successors, success, heuristic)
        .into_iter()
        .find(|i| i.is_some())?
}

pub fn paths<STATE, SUCCESSORS, SUCCESS, HEURISTIC, ITER>(
    init: &STATE,
    successors: SUCCESSORS,
    success: SUCCESS,
    heuristic: HEURISTIC,
) -> impl Iterator<Item = Vec<STATE>>
where
    STATE: Eq + Hash + Clone,
    SUCCESSORS: FnMut(&STATE) -> ITER,
    SUCCESS: FnMut(&STATE) -> bool,
    HEURISTIC: FnMut(&STATE) -> usize,
    ITER: IntoIterator<Item = STATE>,
{
    EHCS::new(init, successors, success, heuristic)
        .into_iter()
        .filter_map(|i| i)
}