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()
    }
}