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}