use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
pub type BeliefId = String;
#[derive(Debug, Clone)]
pub enum BeliefValue {
Boolean(bool),
Numeric(f64),
}
impl BeliefValue {
pub fn as_bool(&self) -> bool {
match self {
BeliefValue::Boolean(b) => *b,
BeliefValue::Numeric(v) => *v > 0.0,
}
}
pub fn as_f64(&self) -> f64 {
match self {
BeliefValue::Boolean(b) => if *b { 1.0 } else { 0.0 },
BeliefValue::Numeric(v) => *v,
}
}
}
pub struct Belief<S> {
pub name: BeliefId,
evaluator: Arc<dyn Fn(&S) -> BeliefValue + Send + Sync>,
}
impl<S> Clone for Belief<S> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
evaluator: Arc::clone(&self.evaluator),
}
}
}
impl<S> Belief<S> {
pub fn value(&self, state: &S) -> BeliefValue {
(self.evaluator)(state)
}
pub fn evaluate(&self, state: &S) -> bool {
self.value(state).as_bool()
}
pub fn evaluate_numeric(&self, state: &S) -> f64 {
self.value(state).as_f64()
}
}
impl<S> fmt::Debug for Belief<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Belief({})", self.name)
}
}
pub struct BeliefBuilder<S> {
name: BeliefId,
evaluator: Option<Arc<dyn Fn(&S) -> BeliefValue + Send + Sync>>,
}
impl<S: 'static> BeliefBuilder<S> {
pub fn new(name: impl Into<BeliefId>) -> Self {
Self { name: name.into(), evaluator: None }
}
pub fn condition(mut self, f: impl Fn(&S) -> bool + Send + Sync + 'static) -> Self {
self.evaluator = Some(Arc::new(move |s| BeliefValue::Boolean(f(s))));
self
}
pub fn numeric(mut self, f: impl Fn(&S) -> f64 + Send + Sync + 'static) -> Self {
self.evaluator = Some(Arc::new(move |s| BeliefValue::Numeric(f(s))));
self
}
pub fn build(self) -> Belief<S> {
Belief {
name: self.name,
evaluator: self.evaluator.unwrap_or_else(|| Arc::new(|_| BeliefValue::Boolean(false))),
}
}
}
pub struct BeliefSet<S> {
beliefs: HashMap<BeliefId, Belief<S>>,
}
impl<S> Default for BeliefSet<S> {
fn default() -> Self {
Self { beliefs: HashMap::new() }
}
}
impl<S> BeliefSet<S> {
pub fn new() -> Self { Self::default() }
pub fn add(&mut self, belief: Belief<S>) {
self.beliefs.insert(belief.name.clone(), belief);
}
pub fn get(&self, name: &str) -> Option<&Belief<S>> {
self.beliefs.get(name)
}
pub fn evaluate(&self, name: &str, state: &S) -> bool {
self.beliefs.get(name).map(|b| b.evaluate(state)).unwrap_or(false)
}
pub fn evaluate_numeric(&self, name: &str, state: &S) -> f64 {
self.beliefs.get(name).map(|b| b.evaluate_numeric(state)).unwrap_or(0.0)
}
pub fn iter(&self) -> impl Iterator<Item = (&BeliefId, &Belief<S>)> {
self.beliefs.iter()
}
}
impl<S> fmt::Debug for BeliefSet<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<&str> = self.beliefs.keys().map(|s| s.as_str()).collect();
f.debug_struct("BeliefSet")
.field("beliefs", &names)
.finish()
}
}