emergent/
condition.rs

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