Skip to main content

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 one succeeds.
34    ///
35    /// Tries the next behavior if one fails. Fails if the last fails.
36    /// A short-circuited logical OR gate.
37    ///
38    /// Resumes the running child across ticks. Use `.memory(false)` to restart
39    /// from the first child every tick instead.
40    Select(Vec<Behavior<A>>),
41    /// `If(condition, success, failure)`
42    If(Box<Behavior<A>>, Box<Behavior<A>>, Box<Behavior<A>>),
43    /// Runs behaviors one by one until all succeed.
44    ///
45    /// Fails if any behavior fails. Succeeds if all succeed.
46    /// A short-circuited logical AND gate.
47    ///
48    /// Resumes the running child across ticks. Use `.memory(false)` to restart
49    /// from the first child every tick instead.
50    Sequence(Vec<Behavior<A>>),
51    /// Reactive `Sequence`: re-walks children from the first one every tick.
52    /// Built via `Sequence(...).memory(false)`, not constructed directly.
53    #[doc(hidden)]
54    MemorylessSequence(Vec<Behavior<A>>),
55    /// Reactive `Select`: re-walks children from the first one every tick.
56    /// Built via `Select(...).memory(false)`, not constructed directly.
57    #[doc(hidden)]
58    MemorylessSelector(Vec<Behavior<A>>),
59    /// Loops while conditional behavior is running.
60    ///
61    /// Succeeds if the conditional behavior succeeds.
62    /// Fails if the conditional behavior fails,
63    /// or if any behavior in the loop body fails.
64    ///
65    /// # Panics
66    ///
67    /// Panics if the given behavior sequence is empty.
68    While(Box<Behavior<A>>, Vec<Behavior<A>>),
69
70    /// Runs a sequence on repeat as long as a conditional behavior
71    /// that precedes the sequence is running.
72    ///
73    /// Conditional behavior is **only** checked before the sequence runs and
74    /// not during the sequence.
75    ///
76    /// Succeeds if the conditional behavior succeeds.
77    /// Fails if the conditional behavior fails,
78    /// or if any behavior in the sequence fails.
79    ///
80    /// # Panics
81    ///
82    /// Panics if the given behavior sequence is empty.
83    ///
84    ///
85    /// ```
86    ///
87    ///use bonsai_bt::{BT, Running, Failure, Success, Action, UpdateArgs, Behavior::WhileAll, ActionArgs};
88    ///use bonsai_bt::Event;
89    ///
90    ///#[derive(Clone, Debug)]
91    ///
92    ///enum Ex { A, B, C }
93    ///
94    ///let rs = WhileAll(
95    ///    Box::new(Action(Ex::A)),
96    ///    vec![Action(Ex::B), Action(Ex::C)],
97    ///);
98    ///
99    ///let (SUCCESS, FAILURE, RUNNING ) = ((Success, 0.0), (Failure, 0.0), (Running, 0.0));
100    ///
101    ///let mut bt = BT::new(rs, ());
102    ///
103    ///let mut i = 0;
104    ///let status = bt.tick(&Event::zero_dt_args(), &mut |args: ActionArgs<Event, Ex>, _| {
105    ///    match args.action {
106    ///        Ex::A => {
107    ///            i += 1;
108    ///            if i == 4 {
109    ///                SUCCESS
110    ///            }
111    ///            else {
112    ///                RUNNING
113    ///            }
114    ///        }
115    ///        Ex::B => {
116    ///            i += 1;
117    ///            SUCCESS
118    ///        }
119    ///        Ex::C => {
120    ///            i += 1;
121    ///            SUCCESS
122    ///        }
123    ///    }
124    ///});
125    ///assert!(i == 4);
126    /// ```
127    WhileAll(Box<Behavior<A>>, Vec<Behavior<A>>),
128    /// Runs all behaviors in parallel until all succeeded.
129    ///
130    /// Succeeds if all behaviors succeed.
131    /// Fails is any behavior fails.
132    WhenAll(Vec<Behavior<A>>),
133    /// Runs all behaviors in parallel until one succeeds.
134    ///
135    /// Succeeds if one behavior succeeds.
136    /// Fails if all behaviors failed.
137    WhenAny(Vec<Behavior<A>>),
138    /// Runs all behaviors in parallel until all succeeds in sequence.
139    ///
140    /// Succeeds if all behaviors succeed, but only if succeeding in sequence.
141    /// Fails if one behavior fails.
142    After(Vec<Behavior<A>>),
143    /// Runs all behaviors in parallel until one completes (succeeds or fails).
144    ///
145    /// Returns the status of the first behavior to complete,
146    /// whether that is `Success` or `Failure`.
147    /// If all behaviors remain `Running`, returns `Running`.
148    Race(Vec<Behavior<A>>),
149}
150
151impl<A> Behavior<A> {
152    /// Set whether a `Sequence` or `Select` keeps memory across ticks.
153    ///
154    /// `true` (the default) resumes the running child each tick; `false` restarts
155    /// from the first child every tick (reactive). No effect on other node types.
156    #[must_use]
157    pub fn memory(self, on: bool) -> Self {
158        match (self, on) {
159            (Behavior::Sequence(c), false) => Behavior::MemorylessSequence(c),
160            (Behavior::Select(c), false) => Behavior::MemorylessSelector(c),
161            (Behavior::MemorylessSequence(c), true) => Behavior::Sequence(c),
162            (Behavior::MemorylessSelector(c), true) => Behavior::Select(c),
163            (other, _) => other,
164        }
165    }
166}
167
168#[cfg(test)]
169#[cfg(feature = "serde")]
170mod tests {
171    use crate::{
172        Behavior::{self, Action, Select, Sequence, Wait, WaitForever, WhenAny, While},
173        Float,
174    };
175
176    #[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq)]
177    pub(crate) enum EnemyAction {
178        /// Circles forever around target pos.
179        Circling,
180        /// Waits until player is within distance.
181        PlayerWithinDistance(Float),
182        /// Fly toward player.
183        FlyTowardPlayer,
184        /// Waits until player is far away from target.
185        PlayerFarAwayFromTarget(Float),
186        /// Makes player loose more blood.
187        AttackPlayer(Float),
188    }
189
190    #[test]
191    fn test_create_complex_behavior() {
192        let circling = Action(EnemyAction::Circling);
193        let circle_until_player_within_distance = Sequence(vec![
194            While(Box::new(Wait(5.0)), vec![circling.clone()]),
195            While(
196                Box::new(Action(EnemyAction::PlayerWithinDistance(50.0))),
197                vec![circling],
198            ),
199        ]);
200        let give_up_or_attack = WhenAny(vec![
201            Action(EnemyAction::PlayerFarAwayFromTarget(100.0)),
202            Sequence(vec![
203                Action(EnemyAction::PlayerWithinDistance(10.0)),
204                Action(EnemyAction::AttackPlayer(0.1)),
205            ]),
206        ]);
207        let attack_attempt = While(Box::new(give_up_or_attack), vec![Action(EnemyAction::FlyTowardPlayer)]);
208        let enemy_behavior = While(
209            Box::new(WaitForever),
210            vec![circle_until_player_within_distance, attack_attempt],
211        );
212
213        let bt_serialized = serde_json::to_string_pretty(&enemy_behavior).unwrap();
214        let _bt_deserialized: Behavior<EnemyAction> = serde_json::from_str(&bt_serialized).unwrap();
215    }
216
217    #[test]
218    fn test_deserialize_behavior() {
219        let bt_json = r#"
220            {
221                "While": ["WaitForever", [{
222                    "Sequence": [{
223                        "While": [{
224                                "Wait": 5.0
225                            },
226                            [{
227                                "Action": "Circling"
228                            }]
229                        ]
230                    }, {
231                        "While": [{
232                                "Action": {
233                                    "PlayerWithinDistance": 50.0
234                                }
235                            },
236                            [{
237                                "Action": "Circling"
238                            }]
239                        ]
240                    }]
241                }, {
242                    "While": [{
243                            "WhenAny": [{
244                                "Action": {
245                                    "PlayerFarAwayFromTarget": 100.0
246                                }
247                            }, {
248                                "Sequence": [{
249                                    "Action": {
250                                        "PlayerWithinDistance": 10.0
251                                    }
252                                }, {
253                                    "Action": {
254                                        "AttackPlayer": 0.1
255                                    }
256                                }]
257                            }]
258                        },
259                        [{
260                            "Action": "FlyTowardPlayer"
261                        }]
262                    ]
263                }]]
264            }
265        "#;
266
267        let _bt_deserialized: Behavior<EnemyAction> = serde_json::from_str(bt_json).unwrap();
268    }
269
270    #[test]
271    fn serde_roundtrip_memoryless_sequence() {
272        let rs: Behavior<EnemyAction> = Sequence(vec![
273            Action(EnemyAction::Circling),
274            Action(EnemyAction::FlyTowardPlayer),
275        ])
276        .memory(false);
277        let json = serde_json::to_string(&rs).unwrap();
278        assert!(json.contains("MemorylessSequence"));
279        let back: Behavior<EnemyAction> = serde_json::from_str(&json).unwrap();
280        assert_eq!(rs, back);
281    }
282
283    #[test]
284    fn serde_roundtrip_memoryless_select() {
285        let rs: Behavior<EnemyAction> = Select(vec![
286            Action(EnemyAction::Circling),
287            Action(EnemyAction::FlyTowardPlayer),
288        ])
289        .memory(false);
290        let json = serde_json::to_string(&rs).unwrap();
291        assert!(json.contains("MemorylessSelector"));
292        let back: Behavior<EnemyAction> = serde_json::from_str(&json).unwrap();
293        assert_eq!(rs, back);
294    }
295
296    #[test]
297    fn serde_deserializes_tuple_sequence() {
298        // The original array form deserializes into the tuple `Sequence` variant.
299        let json = r#"{ "Sequence": [{ "Action": "Circling" }] }"#;
300        let back: Behavior<EnemyAction> = serde_json::from_str(json).unwrap();
301        assert_eq!(back, Sequence(vec![Action(EnemyAction::Circling)]));
302    }
303}