1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
//! Conditions serve purpose of a boolean query to the memory state.
//!
//! See [`Condition`] for more info about conditions.
//!
//! See [`crate::combinators`] for more info about combinators (operations on sets of conditions).
use crate::{consideration::*, Scalar};
/// Condition represent the simplest question about the state of the world via provided memory.
///
/// Imagine memory stores information about number of bannanas in the backpack, and you want to know
/// if there are at least 3 so you can use that information to decide if you need to find more of
/// them to not get hungry during the day.
///
/// User should make conditions as lightweight and as small as possible. The reason for that is to
/// make them reused and combined into bigger sets of conditions using combinators
/// ([`crate::combinators`]).
///
/// # Example
/// ```
/// use emergent::prelude::*;
///
/// struct Memory { counter: usize }
///
/// struct AboveThreshold(usize);
///
/// impl Condition<Memory> for AboveThreshold {
/// fn validate(&self, memory: &Memory) -> bool {
/// memory.counter > self.0
/// }
/// }
///
/// let mut memory = Memory { counter: 1 };
/// assert!(AboveThreshold(0).validate(&memory));
/// ```
pub trait Condition<M = ()>: Send + Sync {
/// Tells if given condition is met based on the state of the memory provided.
fn validate(&self, memory: &M) -> bool;
}
impl<M> Condition<M> for bool {
fn validate(&self, _: &M) -> bool {
*self
}
}
/// Condition that wraps a closure.
///
/// # Example
/// ```
/// use emergent::prelude::*;
///
/// struct Memory { counter: usize }
///
/// let mut memory = Memory { counter: 1 };
/// let condition = ClosureCondition::new(|memory: &Memory| memory.counter > 0);
/// assert!(condition.validate(&memory));
/// ```
pub struct ClosureCondition<M = ()>(pub Box<dyn Fn(&M) -> bool + Send + Sync>);
impl<M> ClosureCondition<M> {
pub fn new<F>(f: F) -> Self
where
F: Fn(&M) -> bool + 'static + Send + Sync,
{
Self(Box::new(f))
}
}
impl<M> Condition<M> for ClosureCondition<M> {
fn validate(&self, memory: &M) -> bool {
(self.0)(memory)
}
}
impl<M> std::fmt::Debug for ClosureCondition<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClosureCondition").finish()
}
}
/// Condition that wraps another condition and inverts/negates its result.
///
/// # Example
/// ```
/// use emergent::prelude::*;
///
/// struct Memory { counter: usize }
///
/// let mut memory = Memory { counter: 1 };
/// let condition = ConditionInvert::new(
/// ClosureCondition::new(|memory: &Memory| memory.counter == 0),
/// );
/// assert!(condition.validate(&memory));
/// ```
pub struct ConditionInvert<M = ()>(pub Box<dyn Condition<M>>);
impl<M> ConditionInvert<M> {
pub fn new<C>(condition: C) -> Self
where
C: Condition<M> + 'static,
{
Self(Box::new(condition))
}
}
impl<M> Condition<M> for ConditionInvert<M> {
fn validate(&self, memory: &M) -> bool {
!self.0.validate(memory)
}
}
impl<M> std::fmt::Debug for ConditionInvert<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConditionInvert").finish()
}
}
/// Condition that wraps [`Consideration`] and converts it into condition.
///
/// Returns true if consideration returns score greater than [`ConsiderationCondition::threshold`]
///
/// # Example
/// ```
/// use emergent::prelude::*;
///
/// struct Memory { value: Scalar }
///
/// let mut memory = Memory { value: 1.0 };
/// let condition = ConsiderationCondition::new(
/// ClosureConsideration::new(|memory: &Memory| memory.value),
/// 0.5,
/// );
/// assert!(condition.validate(&memory));
/// ```
pub struct ConsiderationCondition<M = ()> {
pub consideration: Box<dyn Consideration<M>>,
pub threshold: Scalar,
}
impl<M> ConsiderationCondition<M> {
pub fn new<C>(consideration: C, threshold: Scalar) -> Self
where
C: Consideration<M> + 'static,
{
Self {
consideration: Box::new(consideration),
threshold,
}
}
}
impl<M> Condition<M> for ConsiderationCondition<M> {
fn validate(&self, memory: &M) -> bool {
self.consideration.score(memory) > self.threshold
}
}
impl<M> std::fmt::Debug for ConsiderationCondition<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConsiderationCondition")
.field("threshold", &self.threshold)
.finish()
}
}