bonsai_bt/
behavior.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::Float;
5
6/// Describes a behavior.
7///
8/// This is used for more complex event logic.
9/// Can also be used for game AI.
10#[derive(Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub enum Behavior<A> {
13    /// Waits an amount of time before continuing
14    ///
15    /// Float: Time in seconds
16    Wait(Float),
17    /// Wait forever.
18    WaitForever,
19    /// A high level description of an action.
20    ///
21    /// An Action can either be "condition" which does not
22    /// alter the system and returns either `Success` or `Failure`
23    /// - e.g IsDoorOpen? IsNetworkDown?
24    ///
25    /// Or it can be an "act" that can alter the system
26    /// and returns either `Success`, `Failure` or `Running`
27    /// - e.g OpenDoor, NetworkShutdown
28    Action(A),
29    /// Converts `Success` into `Failure` and vice versa.
30    Invert(Box<Behavior<A>>),
31    /// Ignores failures and returns `Success`.
32    AlwaysSucceed(Box<Behavior<A>>),
33    /// Runs behaviors one by one until a behavior succeeds.
34    ///
35    /// If a behavior fails it will try the next one.
36    /// Fails if the last behavior fails.
37    /// Can be thought of as a short-circuited logical OR gate.
38    Select(Vec<Behavior<A>>),
39    /// `If(condition, success, failure)`
40    If(Box<Behavior<A>>, Box<Behavior<A>>, Box<Behavior<A>>),
41    /// Runs behaviors one by one until all succeeded.
42    ///
43    /// The sequence fails if a behavior fails.
44    /// The sequence succeeds if all the behavior succeeds.
45    /// Can be thought of as a short-circuited logical AND gate.
46    Sequence(Vec<Behavior<A>>),
47    /// Loops while conditional behavior is running.
48    ///
49    /// Succeeds if the conditional behavior succeeds.
50    /// Fails if the conditional behavior fails,
51    /// or if any behavior in the loop body fails.
52    ///
53    /// # Panics
54    ///
55    /// Panics if the given behavior sequence is empty.
56    While(Box<Behavior<A>>, Vec<Behavior<A>>),
57
58    /// Runs a sequence on repeat as long as a conditional behavior
59    /// that precedes the sequence is running.
60    ///
61    /// Conditional behavior is **only** checked before the sequence runs and
62    /// not during the sequence.
63    ///
64    /// Succeeds if the conditional behavior succeeds.
65    /// Fails if the conditional behavior fails,
66    /// or if any behavior in the sequence fails.
67    ///
68    /// # Panics
69    ///
70    /// Panics if the given behavior sequence is empty.
71    ///
72    ///
73    /// ```
74    ///
75    ///use bonsai_bt::{BT, Running, Failure, Success, Action, UpdateArgs, Behavior::WhileAll, ActionArgs};
76    ///use bonsai_bt::Event;
77    ///
78    ///#[derive(Clone, Debug)]
79    ///
80    ///enum Ex { A, B, C }
81    ///
82    ///let rs = WhileAll(
83    ///    Box::new(Action(Ex::A)),
84    ///    vec![Action(Ex::B), Action(Ex::C)],
85    ///);
86    ///
87    ///let (SUCCESS, FAILURE, RUNNING ) = ((Success, 0.0), (Failure, 0.0), (Running, 0.0));
88    ///
89    ///let mut bt = BT::new(rs, ());
90    ///
91    ///let mut i = 0;
92    ///let status = bt.tick(&Event::zero_dt_args(), &mut |args: ActionArgs<Event, Ex>, _| {
93    ///    match args.action {
94    ///        Ex::A => {
95    ///            i += 1;
96    ///            if i == 4 {
97    ///                SUCCESS
98    ///            }
99    ///            else {
100    ///                RUNNING
101    ///            }
102    ///        }
103    ///        Ex::B => {
104    ///            i += 1;
105    ///            SUCCESS
106    ///        }
107    ///        Ex::C => {
108    ///            i += 1;
109    ///            SUCCESS
110    ///        }
111    ///    }
112    ///});
113    ///assert!(i == 4);
114    /// ```
115    WhileAll(Box<Behavior<A>>, Vec<Behavior<A>>),
116    /// Runs all behaviors in parallel until all succeeded.
117    ///
118    /// Succeeds if all behaviors succeed.
119    /// Fails is any behavior fails.
120    WhenAll(Vec<Behavior<A>>),
121    /// Runs all behaviors in parallel until one succeeds.
122    ///
123    /// Succeeds if one behavior succeeds.
124    /// Fails if all behaviors failed.
125    WhenAny(Vec<Behavior<A>>),
126    /// Runs all behaviors in parallel until all succeeds in sequence.
127    ///
128    /// Succeeds if all behaviors succeed, but only if succeeding in sequence.
129    /// Fails if one behavior fails.
130    After(Vec<Behavior<A>>),
131}
132
133#[cfg(test)]
134#[cfg(feature = "serde")]
135mod tests {
136    use crate::{
137        Behavior::{self, Action, Sequence, Wait, WaitForever, WhenAny, While},
138        Float,
139    };
140
141    #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
142    pub(crate) enum EnemyAction {
143        /// Circles forever around target pos.
144        Circling,
145        /// Waits until player is within distance.
146        PlayerWithinDistance(Float),
147        /// Fly toward player.
148        FlyTowardPlayer,
149        /// Waits until player is far away from target.
150        PlayerFarAwayFromTarget(Float),
151        /// Makes player loose more blood.
152        AttackPlayer(Float),
153    }
154
155    #[test]
156    fn test_create_complex_behavior() {
157        let circling = Action(EnemyAction::Circling);
158        let circle_until_player_within_distance = Sequence(vec![
159            While(Box::new(Wait(5.0)), vec![circling.clone()]),
160            While(
161                Box::new(Action(EnemyAction::PlayerWithinDistance(50.0))),
162                vec![circling],
163            ),
164        ]);
165        let give_up_or_attack = WhenAny(vec![
166            Action(EnemyAction::PlayerFarAwayFromTarget(100.0)),
167            Sequence(vec![
168                Action(EnemyAction::PlayerWithinDistance(10.0)),
169                Action(EnemyAction::AttackPlayer(0.1)),
170            ]),
171        ]);
172        let attack_attempt = While(Box::new(give_up_or_attack), vec![Action(EnemyAction::FlyTowardPlayer)]);
173        let enemy_behavior = While(
174            Box::new(WaitForever),
175            vec![circle_until_player_within_distance, attack_attempt],
176        );
177
178        let bt_serialized = serde_json::to_string_pretty(&enemy_behavior).unwrap();
179        let _bt_deserialized: Behavior<EnemyAction> = serde_json::from_str(&bt_serialized).unwrap();
180    }
181
182    #[test]
183    fn test_deserialize_behavior() {
184        let bt_json = r#"
185            {
186                "While": ["WaitForever", [{
187                    "Sequence": [{
188                        "While": [{
189                                "Wait": 5.0
190                            },
191                            [{
192                                "Action": "Circling"
193                            }]
194                        ]
195                    }, {
196                        "While": [{
197                                "Action": {
198                                    "PlayerWithinDistance": 50.0
199                                }
200                            },
201                            [{
202                                "Action": "Circling"
203                            }]
204                        ]
205                    }]
206                }, {
207                    "While": [{
208                            "WhenAny": [{
209                                "Action": {
210                                    "PlayerFarAwayFromTarget": 100.0
211                                }
212                            }, {
213                                "Sequence": [{
214                                    "Action": {
215                                        "PlayerWithinDistance": 10.0
216                                    }
217                                }, {
218                                    "Action": {
219                                        "AttackPlayer": 0.1
220                                    }
221                                }]
222                            }]
223                        },
224                        [{
225                            "Action": "FlyTowardPlayer"
226                        }]
227                    ]
228                }]]
229            }
230        "#;
231
232        let _bt_deserialized: Behavior<EnemyAction> = serde_json::from_str(bt_json).unwrap();
233    }
234}