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}