fyrox_impl/utils/behavior/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![warn(missing_docs)]
22
23//! Everything related to AI behavior and behavior trees.
24//!
25//! Behavior trees are simple but very powerful mechanism to implement artificial intelligence for
26//! games. The main concept is in its name. Tree is a set of connected nodes, where each node could
27//! have single parent and zero or more children nodes. Execution path of the tree is defined by the
28//! actions of the nodes. Behavior tree has a set of hard coded nodes as well as leaf nodes with
29//! user-defined logic. Hard coded nodes are: Sequence, Selector, Leaf. Leaf is special - it has
30//! custom method `tick` that can contain any logic you want.
31//!
32//! For more info see:
33//! - [Wikipedia article](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control))
34//! - [Gamasutra](https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php)
35
36use crate::{
37    core::{
38        pool::{Handle, Pool},
39        visitor::prelude::*,
40    },
41    utils::behavior::{
42        composite::{CompositeNode, CompositeNodeKind},
43        inverter::Inverter,
44        leaf::LeafNode,
45    },
46};
47use std::{
48    fmt::Debug,
49    ops::{Index, IndexMut},
50};
51
52pub mod composite;
53pub mod inverter;
54pub mod leaf;
55
56/// Status of execution of behavior tree node.
57pub enum Status {
58    /// Action was successful.
59    Success,
60    /// Failed to perform an action.
61    Failure,
62    /// Need another iteration to perform an action.
63    Running,
64}
65
66/// A trait for user-defined actions for behavior tree.
67pub trait Behavior<'a>: Visit + Default + PartialEq + Debug + Clone {
68    /// A context in which the behavior will be performed.
69    type Context;
70
71    /// A function that will be called each frame depending on
72    /// the current execution path of the behavior tree it belongs
73    /// to.
74    fn tick(&mut self, context: &mut Self::Context) -> Status;
75}
76
77/// Root node of the tree.
78#[derive(Debug, PartialEq, Visit, Eq, Clone)]
79pub struct RootNode<B>
80where
81    B: Clone,
82{
83    child: Handle<BehaviorNode<B>>,
84}
85
86impl<B> Default for RootNode<B>
87where
88    B: Clone,
89{
90    fn default() -> Self {
91        Self {
92            child: Default::default(),
93        }
94    }
95}
96
97/// Possible variations of behavior nodes.
98#[derive(Debug, PartialEq, Visit, Eq, Clone)]
99pub enum BehaviorNode<B>
100where
101    B: Clone,
102{
103    #[doc(hidden)]
104    Unknown,
105    /// Root node of the tree.
106    Root(RootNode<B>),
107    /// Composite (sequence or selector) node of the tree.
108    Composite(CompositeNode<B>),
109    /// A node with custom logic.
110    Leaf(LeafNode<B>),
111    /// A node, that inverts its child state ([`Status::Failure`] becomes [`Status::Success`] and vice versa, [`Status::Running`] remains
112    /// unchanged)
113    Inverter(Inverter<B>),
114}
115
116impl<B> Default for BehaviorNode<B>
117where
118    B: Clone,
119{
120    fn default() -> Self {
121        Self::Unknown
122    }
123}
124
125/// See module docs.
126#[derive(Debug, PartialEq, Visit, Clone)]
127pub struct BehaviorTree<B>
128where
129    B: Clone,
130{
131    nodes: Pool<BehaviorNode<B>>,
132    root: Handle<BehaviorNode<B>>,
133}
134
135impl<B> Default for BehaviorTree<B>
136where
137    B: Clone + 'static,
138{
139    fn default() -> Self {
140        Self {
141            nodes: Default::default(),
142            root: Default::default(),
143        }
144    }
145}
146
147impl<B> BehaviorTree<B>
148where
149    B: Clone + 'static,
150{
151    /// Creates new behavior tree with single root node.
152    pub fn new() -> Self {
153        let mut nodes = Pool::new();
154        let root = nodes.spawn(BehaviorNode::Root(RootNode {
155            child: Default::default(),
156        }));
157        Self { nodes, root }
158    }
159
160    /// Adds a node to the tree, returns its handle.
161    pub fn add_node(&mut self, node: BehaviorNode<B>) -> Handle<BehaviorNode<B>> {
162        self.nodes.spawn(node)
163    }
164
165    /// Sets entry nodes of the tree. Execution will start from this node.
166    pub fn set_entry_node(&mut self, entry: Handle<BehaviorNode<B>>) {
167        if let BehaviorNode::Root(root) = &mut self.nodes[self.root] {
168            root.child = entry;
169        } else {
170            unreachable!("must be root")
171        }
172    }
173
174    fn tick_recursive<'a, Ctx>(&self, handle: Handle<BehaviorNode<B>>, context: &mut Ctx) -> Status
175    where
176        B: Behavior<'a, Context = Ctx>,
177    {
178        match self.nodes[handle] {
179            BehaviorNode::Root(ref root) => {
180                if root.child.is_some() {
181                    self.tick_recursive(root.child, context)
182                } else {
183                    Status::Success
184                }
185            }
186            BehaviorNode::Composite(ref composite) => match composite.kind {
187                CompositeNodeKind::Sequence => {
188                    let mut all_succeeded = true;
189                    for child in composite.children.iter() {
190                        match self.tick_recursive(*child, context) {
191                            Status::Failure => {
192                                all_succeeded = false;
193                                break;
194                            }
195                            Status::Running => {
196                                return Status::Running;
197                            }
198                            _ => (),
199                        }
200                    }
201                    if all_succeeded {
202                        Status::Success
203                    } else {
204                        Status::Failure
205                    }
206                }
207                CompositeNodeKind::Selector => {
208                    for child in composite.children.iter() {
209                        match self.tick_recursive(*child, context) {
210                            Status::Success => return Status::Success,
211                            Status::Running => return Status::Running,
212                            _ => (),
213                        }
214                    }
215                    Status::Failure
216                }
217            },
218            BehaviorNode::Leaf(ref leaf) => {
219                leaf.behavior.as_ref().unwrap().borrow_mut().tick(context)
220            }
221            BehaviorNode::Inverter(ref inverter) => {
222                match self.tick_recursive(inverter.child, context) {
223                    Status::Success => Status::Failure,
224                    Status::Failure => Status::Success,
225                    Status::Running => Status::Running,
226                }
227            }
228            BehaviorNode::Unknown => {
229                unreachable!()
230            }
231        }
232    }
233
234    /// Tries to get a shared reference to a node by given handle.
235    pub fn node(&self, handle: Handle<BehaviorNode<B>>) -> Option<&BehaviorNode<B>> {
236        self.nodes.try_borrow(handle)
237    }
238
239    /// Tries to get a mutable reference to a node by given handle.
240    pub fn node_mut(&mut self, handle: Handle<BehaviorNode<B>>) -> Option<&mut BehaviorNode<B>> {
241        self.nodes.try_borrow_mut(handle)
242    }
243
244    /// Performs a single update tick with given context.
245    pub fn tick<'a, Ctx>(&self, context: &mut Ctx) -> Status
246    where
247        B: Behavior<'a, Context = Ctx>,
248    {
249        self.tick_recursive(self.root, context)
250    }
251}
252
253impl<B: Clone + 'static> Index<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
254    type Output = BehaviorNode<B>;
255
256    fn index(&self, index: Handle<BehaviorNode<B>>) -> &Self::Output {
257        &self.nodes[index]
258    }
259}
260
261impl<B: Clone + 'static> IndexMut<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
262    fn index_mut(&mut self, index: Handle<BehaviorNode<B>>) -> &mut Self::Output {
263        &mut self.nodes[index]
264    }
265}
266
267/// Creates a new sequence.
268pub fn sequence<B, const N: usize>(
269    children: [Handle<BehaviorNode<B>>; N],
270    tree: &mut BehaviorTree<B>,
271) -> Handle<BehaviorNode<B>>
272where
273    B: Clone + 'static,
274{
275    CompositeNode::new_sequence(children.to_vec()).add_to(tree)
276}
277
278/// Creates a new selector.
279pub fn selector<B, const N: usize>(
280    children: [Handle<BehaviorNode<B>>; N],
281    tree: &mut BehaviorTree<B>,
282) -> Handle<BehaviorNode<B>>
283where
284    B: Clone + 'static,
285{
286    CompositeNode::new_selector(children.to_vec()).add_to(tree)
287}
288
289/// Creates a new leaf.
290pub fn leaf<B>(behavior: B, tree: &mut BehaviorTree<B>) -> Handle<BehaviorNode<B>>
291where
292    B: Clone + 'static,
293{
294    LeafNode::new(behavior).add_to(tree)
295}
296
297/// Creates a new inverter.
298pub fn inverter<B>(
299    child: Handle<BehaviorNode<B>>,
300    tree: &mut BehaviorTree<B>,
301) -> Handle<BehaviorNode<B>>
302where
303    B: Clone + 'static,
304{
305    Inverter::new(child).add_to(tree)
306}
307
308#[cfg(test)]
309mod test {
310    use crate::{
311        core::{futures::executor::block_on, visitor::prelude::*},
312        utils::behavior::{
313            composite::{CompositeNode, CompositeNodeKind},
314            leaf::LeafNode,
315            Behavior, BehaviorTree, Status,
316        },
317    };
318    use std::{env, fs::File, io::Write, path::PathBuf};
319
320    #[derive(Debug, PartialEq, Default, Visit, Clone)]
321    struct WalkAction;
322
323    impl Behavior<'_> for WalkAction {
324        type Context = Environment;
325
326        fn tick(&mut self, context: &mut Self::Context) -> Status {
327            if context.distance_to_door <= 0.0 {
328                Status::Success
329            } else {
330                context.distance_to_door -= 0.1;
331                println!(
332                    "Approaching door, remaining distance: {}",
333                    context.distance_to_door
334                );
335                Status::Running
336            }
337        }
338    }
339
340    #[derive(Debug, PartialEq, Default, Visit, Clone)]
341    struct OpenDoorAction;
342
343    impl Behavior<'_> for OpenDoorAction {
344        type Context = Environment;
345
346        fn tick(&mut self, context: &mut Self::Context) -> Status {
347            if !context.door_opened {
348                context.door_opened = true;
349                println!("Door was opened!");
350            }
351            Status::Success
352        }
353    }
354
355    #[derive(Debug, PartialEq, Default, Visit, Clone)]
356    struct StepThroughAction;
357
358    impl Behavior<'_> for StepThroughAction {
359        type Context = Environment;
360
361        fn tick(&mut self, context: &mut Self::Context) -> Status {
362            if context.distance_to_door < -1.0 {
363                Status::Success
364            } else {
365                context.distance_to_door -= 0.1;
366                println!(
367                    "Stepping through doorway, remaining distance: {}",
368                    -1.0 - context.distance_to_door
369                );
370                Status::Running
371            }
372        }
373    }
374
375    #[derive(Debug, PartialEq, Default, Visit, Clone)]
376    struct CloseDoorAction;
377
378    impl Behavior<'_> for CloseDoorAction {
379        type Context = Environment;
380
381        fn tick(&mut self, context: &mut Self::Context) -> Status {
382            if context.door_opened {
383                context.door_opened = false;
384                context.done = true;
385                println!("Door was closed");
386            }
387            Status::Success
388        }
389    }
390
391    #[derive(Debug, PartialEq, Visit, Clone)]
392    enum BotBehavior {
393        None,
394        Walk(WalkAction),
395        OpenDoor(OpenDoorAction),
396        StepThrough(StepThroughAction),
397        CloseDoor(CloseDoorAction),
398    }
399
400    impl Default for BotBehavior {
401        fn default() -> Self {
402            Self::None
403        }
404    }
405
406    #[derive(Default, Visit)]
407    struct Environment {
408        // > 0 - door in front of
409        // < 0 - door is behind
410        distance_to_door: f32,
411        door_opened: bool,
412        done: bool,
413    }
414
415    impl Behavior<'_> for BotBehavior {
416        type Context = Environment;
417
418        fn tick(&mut self, context: &mut Self::Context) -> Status {
419            match self {
420                BotBehavior::None => unreachable!(),
421                BotBehavior::Walk(v) => v.tick(context),
422                BotBehavior::OpenDoor(v) => v.tick(context),
423                BotBehavior::StepThrough(v) => v.tick(context),
424                BotBehavior::CloseDoor(v) => v.tick(context),
425            }
426        }
427    }
428
429    fn create_tree() -> BehaviorTree<BotBehavior> {
430        let mut tree = BehaviorTree::new();
431
432        let entry = CompositeNode::new(
433            CompositeNodeKind::Sequence,
434            vec![
435                LeafNode::new(BotBehavior::Walk(WalkAction)).add_to(&mut tree),
436                LeafNode::new(BotBehavior::OpenDoor(OpenDoorAction)).add_to(&mut tree),
437                LeafNode::new(BotBehavior::StepThrough(StepThroughAction)).add_to(&mut tree),
438                LeafNode::new(BotBehavior::CloseDoor(CloseDoorAction)).add_to(&mut tree),
439            ],
440        )
441        .add_to(&mut tree);
442
443        tree.set_entry_node(entry);
444
445        tree
446    }
447
448    #[test]
449    fn test_behavior() {
450        let tree = create_tree();
451
452        let mut ctx = Environment {
453            distance_to_door: 3.0,
454            door_opened: false,
455            done: false,
456        };
457
458        while !ctx.done {
459            tree.tick(&mut ctx);
460        }
461    }
462
463    #[test]
464    fn test_behavior_save_load() {
465        let (bin, txt) = {
466            let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
467            let root = PathBuf::from(manifest_dir).join("test_output");
468            if !root.exists() {
469                std::fs::create_dir(&root).unwrap();
470            }
471            (
472                root.join(format!("{}.bin", "behavior_save_load")),
473                root.join(format!("{}.txt", "behavior_save_load")),
474            )
475        };
476
477        // Save
478        let mut saved_tree = create_tree();
479        let mut visitor = Visitor::new();
480        saved_tree.visit("Tree", &mut visitor).unwrap();
481        visitor.save_binary(bin.clone()).unwrap();
482        let mut file = File::create(txt).unwrap();
483        file.write_all(visitor.save_text().as_bytes()).unwrap();
484
485        // Load
486        let mut visitor = block_on(Visitor::load_binary(bin)).unwrap();
487        let mut loaded_tree = BehaviorTree::<BotBehavior>::default();
488        loaded_tree.visit("Tree", &mut visitor).unwrap();
489
490        assert_eq!(saved_tree, loaded_tree);
491    }
492}