use std::marker::PhantomData;
pub mod search;
pub trait Searchable {
type NextStatesIter: Iterator<Item = Self>;
fn next_states(&self) -> Self::NextStatesIter;
fn is_solution(&self) -> bool;
}
pub trait ScoredSearchable: Searchable {
type Score: Ord;
fn score(&self) -> Self::Score;
}
struct OrderedSearchable<T, C> {
state: T,
score: C,
}
impl<S> From<S> for OrderedSearchable<S, S::Score>
where
S: ScoredSearchable,
{
fn from(state: S) -> Self {
let score = state.score();
OrderedSearchable { state, score }
}
}
impl<S> From<StateParentPair<S>> for OrderedSearchable<StateParentPair<S>, S::Score>
where
S: ScoredSearchable,
{
fn from(pair: StateParentPair<S>) -> Self {
let score = pair.0.score();
OrderedSearchable { state: pair, score }
}
}
impl<T, C> PartialEq for OrderedSearchable<T, C>
where
C: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.score == other.score
}
}
impl<T, C> Eq for OrderedSearchable<T, C> where C: Eq {}
impl<T, C> PartialOrd for OrderedSearchable<T, C>
where
C: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
other.score.partial_cmp(&self.score)
}
}
impl<T, C> Ord for OrderedSearchable<T, C>
where
C: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.score.cmp(&self.score)
}
}
pub struct NoContext<S>(S);
impl<S> AsRef<S> for NoContext<S> {
fn as_ref(&self) -> &S {
&self.0
}
}
#[derive(Clone)]
pub struct StateParentPair<S>(S, Option<usize>);
impl<S> AsRef<S> for StateParentPair<S> {
fn as_ref(&self) -> &S {
&self.0
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct OrdF32(f32);
impl Eq for OrdF32 {}
impl Ord for OrdF32 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.total_cmp(&other.0)
}
}
impl From<OrdF32> for f32 {
fn from(OrdF32(value): OrdF32) -> Self {
value
}
}
impl From<f32> for OrdF32 {
fn from(value: f32) -> Self {
OrdF32(value)
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct OrdF64(f64);
impl Eq for OrdF64 {}
impl Ord for OrdF64 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.total_cmp(&other.0)
}
}
impl From<OrdF64> for f64 {
fn from(OrdF64(value): OrdF64) -> Self {
value
}
}
impl From<f64> for OrdF64 {
fn from(value: f64) -> Self {
OrdF64(value)
}
}
pub trait ExplorationManager<S> {
type YieldResult;
type FringeItem: AsRef<S>;
type CurrentStateContext;
fn initialize(initial_state: S) -> Self;
fn pop_state(&mut self) -> Option<Self::FringeItem>;
fn prepare_result_from(&self, item: Self::FringeItem) -> Self::YieldResult;
fn valid_state(&mut self, item: &Self::FringeItem) -> bool;
fn place_state(&mut self, item: Self::FringeItem);
fn register_current_state(&mut self, item: &Self::FringeItem) -> Self::CurrentStateContext;
fn prepare_state(&self, context: &Self::CurrentStateContext, state: S) -> Self::FringeItem;
}
pub struct Searcher<M, S> {
pub manager: M,
_marker: PhantomData<S>,
}
impl<M, S> Searcher<M, S> {
pub fn new(initial_state: S) -> Self
where
M: ExplorationManager<S>,
{
Self {
manager: M::initialize(initial_state),
_marker: PhantomData,
}
}
pub fn new_with_default() -> Self
where
S: Default,
M: ExplorationManager<S>,
{
Self::new(Default::default())
}
}
impl<M, S> Iterator for Searcher<M, S>
where
M: ExplorationManager<S>,
S: Searchable,
{
type Item = M::YieldResult;
fn next(&mut self) -> Option<Self::Item> {
loop {
let current_state = self.manager.pop_state()?;
if current_state.as_ref().is_solution() {
return Some(self.manager.prepare_result_from(current_state));
}
let context = self.manager.register_current_state(¤t_state);
for state in current_state.as_ref().next_states() {
let new_item = self.manager.prepare_state(&context, state);
if self.manager.valid_state(&new_item) {
self.manager.place_state(new_item);
}
}
}
}
}