use crate::{
cells::{CellRef, State},
config::NewState,
rules::Rule,
world::World,
};
use rand::{thread_rng, Rng};
#[cfg(doc)]
use crate::cells::LifeCell;
#[cfg(feature = "serde")]
use crate::{
error::Error,
save::{ReasonSer, SetCellSer},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod backjump;
mod lifesrc;
pub use backjump::Backjump;
pub use lifesrc::LifeSrc;
pub(crate) use reason::Reason;
mod reason {
use crate::{cells::CellRef, rules::Rule};
#[cfg(feature = "serde")]
use crate::save::ReasonSer;
pub trait Reason<R: Rule> {
const KNOWN: Self;
const DECIDED: Self;
fn from_cell(cell: CellRef<R>) -> Self;
fn from_sym(cell: CellRef<R>) -> Self;
fn is_decided(&self) -> bool;
#[cfg(feature = "serde")]
#[cfg_attr(any(docs_rs, github_io), doc(cfg(feature = "serde")))]
fn ser(&self) -> ReasonSer;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Status {
Initial,
Found,
None,
Searching,
}
#[cfg_attr(not(github_io), doc = "Most of its items are hidden in the doc.")]
pub trait Algorithm<R: Rule>: private::Sealed {
#[cfg_attr(not(github_io), doc(hidden))]
type Reason: Reason<R>;
#[cfg_attr(not(github_io), doc(hidden))]
type ConflReason;
fn new() -> Self;
#[cfg_attr(not(github_io), doc(hidden))]
fn confl_from_cell(cell: CellRef<R>) -> Self::ConflReason;
#[cfg_attr(not(github_io), doc(hidden))]
fn confl_from_sym(cell: CellRef<R>, sym: CellRef<R>) -> Self::ConflReason;
#[cfg_attr(not(github_io), doc(hidden))]
fn init_front(world: World<R, Self>) -> World<R, Self>;
#[cfg_attr(not(github_io), doc(hidden))]
#[cfg_attr(github_io, allow(rustdoc::private_intra_doc_links))]
fn set_cell(
world: &mut World<R, Self>,
cell: CellRef<R>,
state: State,
reason: Self::Reason,
) -> Result<(), Self::ConflReason>;
#[cfg_attr(not(github_io), doc(hidden))]
fn go(world: &mut World<R, Self>, step: &mut u64) -> bool;
#[cfg_attr(not(github_io), doc(hidden))]
fn retreat(world: &mut World<R, Self>) -> bool;
#[cfg(feature = "serde")]
#[cfg_attr(any(docs_rs, github_io), doc(cfg(feature = "serde")))]
fn deser_reason(world: &World<R, Self>, ser: &ReasonSer) -> Result<Self::Reason, Error>;
}
mod private {
pub trait Sealed: Sized {}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SetCell<R: Rule, A: Algorithm<R>> {
pub(crate) cell: CellRef<R>,
pub(crate) reason: A::Reason,
}
impl<R: Rule, A: Algorithm<R>> SetCell<R, A> {
#[inline]
pub(crate) const fn new(cell: CellRef<R>, reason: A::Reason) -> Self {
Self { cell, reason }
}
#[cfg(feature = "serde")]
#[cfg_attr(any(docs_rs, github_io), doc(cfg(feature = "serde")))]
#[inline]
pub(crate) fn ser(&self) -> SetCellSer {
SetCellSer {
coord: self.cell.coord,
state: self.cell.state.get().unwrap(),
reason: self.reason.ser(),
}
}
}
impl<R: Rule, A: Algorithm<R>> World<R, A> {
#[inline]
fn consistify(&mut self, cell: CellRef<R>) -> Result<(), A::ConflReason> {
Rule::consistify(self, cell)
}
#[inline]
fn consistify10(&mut self, cell: CellRef<R>) -> Result<(), A::ConflReason> {
self.consistify(cell)?;
if let Some(pred) = cell.pred {
self.consistify(pred)?;
}
for &neigh in cell.nbhd.iter() {
if let Some(neigh) = neigh {
self.consistify(neigh)?;
}
}
Ok(())
}
pub(crate) fn proceed(&mut self) -> Result<(), A::ConflReason> {
while self.check_index < self.set_stack.len() as u32 {
let cell = self.set_stack[self.check_index as usize].cell;
let state = cell.state.get().unwrap();
for &sym in &cell.sym {
if let Some(old_state) = sym.state.get() {
if state != old_state {
return Err(A::confl_from_sym(cell, sym));
}
} else {
self.set_cell(sym, state, A::Reason::from_sym(cell))?;
}
}
self.consistify10(cell)?;
self.check_index += 1;
}
Ok(())
}
#[inline]
pub(crate) fn retreat(&mut self) -> bool {
A::retreat(self)
}
#[inline]
fn decide(&mut self) -> Option<bool> {
if let Some(cell) = self.get_unknown() {
self.next_unknown = cell.next;
let state = match self.config.new_state {
NewState::ChooseDead => cell.background,
NewState::ChooseAlive => !cell.background,
NewState::Random => State(thread_rng().gen_range(0..self.rule.gen())),
};
Some(self.set_cell(cell, state, A::Reason::DECIDED).is_ok())
} else {
None
}
}
#[inline]
pub(crate) fn presearch(mut self) -> Self {
loop {
if self.proceed().is_ok() {
self.set_stack.clear();
self.check_index = 0;
return self;
} else {
self.conflicts += 1;
if !self.retreat() {
return self;
}
}
}
}
pub fn search(&mut self, max_step: Option<u64>) -> Status {
let mut step_count = 0;
if self.next_unknown.is_none() && !self.retreat() {
return Status::None;
}
while A::go(self, &mut step_count) {
if let Some(result) = self.decide() {
if !result && !self.retreat() {
return Status::None;
}
} else if !self.is_boring() {
if self.config.reduce_max {
self.config.max_cell_count = Some(self.cell_count() - 1);
}
return Status::Found;
} else if !self.retreat() {
return Status::None;
}
if let Some(max) = max_step {
if step_count > max {
return Status::Searching;
}
}
}
Status::None
}
}