behavior_tree_lite/
lib.rs

1//! # behavior-tree-lite (Rust crate)
2//!
3//! An experimental Rust crate for minimal behavior tree implementation
4//!
5//!
6//! ## Overview
7//!
8//! This is an implementation of behavior tree in Rust, inspired by [BehaviorTreeCPP](https://github.com/BehaviorTree/BehaviorTree.CPP.git).
9//!
10//! A behavior tree is an extension to finite state machines that makes describing transitional behavior easier.
11//! See [BehaviorTreeCPP's documentation](https://www.behaviortree.dev/) for the thorough introduction to the idea.
12//!
13//! See the historical notes at the bottom of this README.md for more full history.
14//!
15//!
16//! ## How it looks like
17//!
18//! First, you define the state with a data structure.
19//!
20//! ```rust
21//! struct Arm {
22//!     name: String,
23//! }
24//!
25//! struct Body {
26//!     left_arm: Arm,
27//!     right_arm: Arm,
28//! }
29//!
30//! let body = Body {
31//!     left_arm: Arm {
32//!         name: "leftArm".to_string(),
33//!     },
34//!     right_arm: Arm {
35//!         name: "rightArm".to_string(),
36//!     },
37//! };
38//! ```
39//!
40//! Then register the data to the context.
41//!
42//! ```rust
43//! # use behavior_tree_lite::Context;
44//! # let body = 1;
45//! let mut ctx = Context::default();
46//! ctx.set("body", body);
47//! ```
48//!
49//! Then, you define a behavior tree.
50//! Note that `add_child` method takes a mapping of blackboard variables as the second argument.
51//!
52//! ```rust
53//! # use behavior_tree_lite::*;
54//! # struct PrintBodyNode;
55//! # impl BehaviorNode for PrintBodyNode { fn tick(&mut self, _: BehaviorCallback, _: &mut Context) -> BehaviorResult { BehaviorResult::Success }}
56//! # struct PrintArmNode;
57//! # impl BehaviorNode for PrintArmNode { fn tick(&mut self, _: BehaviorCallback, _: &mut Context) -> BehaviorResult { BehaviorResult::Success }}
58//! let mut root = BehaviorNodeContainer::new_node(SequenceNode::default());
59//! root.add_child(BehaviorNodeContainer::new_node(PrintBodyNode));
60//!
61//! let mut print_arms = BehaviorNodeContainer::new_node(SequenceNode::default());
62//! print_arms.add_child(BehaviorNodeContainer::new(Box::new(PrintArmNode), hash_map!("arm" => "left_arm")));
63//! print_arms.add_child(BehaviorNodeContainer::new(Box::new(PrintArmNode), hash_map!("arm" => "right_arm")));
64//!
65//! root.add_child(print_arms);
66//! ```
67//!
68//! and call `tick()`.
69//!
70//! ```rust
71//! # use behavior_tree_lite::*;
72//! # let mut root = SequenceNode::default();
73//! # let mut ctx = Context::default();
74//! let result = root.tick(&mut |_| None, &mut ctx);
75//! ```
76//!
77//! The first argument to the `tick` has weird value `&mut |_| None`.
78//! It is a callback for the behavior nodes to communicate with the environment.
79//! You could supply a closure to handle messages from the behavior nodes.
80//! The closure, aliased as `BehaviorCallback`, takes a `&dyn std::any::Any` and returns a `Box<dyn std::any::Any>`,
81//! which allows the user to pass or return any type, but in exchange, the user needs to
82//! check the type with `downcast_ref` in order to use it like below.
83//!
84//! ```rust
85//! # use behavior_tree_lite::*;
86//! # let mut tree = SequenceNode::default();
87//! tree.tick(
88//!     &mut |v: &dyn std::any::Any| {
89//!         println!("{}", *v.downcast_ref::<bool>().unwrap());
90//!         None
91//!     },
92//!     &mut Context::default(),
93//! );
94//! ```
95//!
96//! This design was adopted because there is no other good ways to communicate between behavior nodes and the envrionment _whose lifetime is not 'static_.
97//!
98//! It is easy to communicate with global static variables, but users often want
99//! to use behavior tree with limited lifetimes, like enemies' AI in a game.
100//! Because you can't name a lifetime until you actually use the behavior tree,
101//! you can't define a type that can send/receive data with arbitrary type having
102//! lifetime shorter than 'static.
103//! `std::any::Any` can't circumvent the limitation, because it is also bounded by 'static lifetime,
104//! so as soon as you put your custom payload into it, you can't put any references other than `&'static`.
105//!
106//! With a closure, we don't have to name the lifetime and it will clearly outlive the duration of the closure body, so we can pass references around.
107//!
108//! Of course, you can also use blackboard variables, but they have the same limitation of lifetimes; you can't pass a reference through blackboard.
109//! A callback is much more direct (and doesn't require indirection of port names)
110//! way to communicate with the environment.
111//!
112//!
113//! ## How to define your own node
114//!
115//! The core of the library is the `BehaviorNode` trait.
116//! You can implement the trait to your own type to make a behavior node of your own.
117//!
118//! It is very similar to BehaviorTreeCPP.
119//! For example a node to print the name of the arm can be defined like below.
120//!
121//! ```rust
122//! # use behavior_tree_lite::*;
123//! # #[derive(Debug)]
124//! # struct Arm { name: String };
125//! struct PrintArmNode;
126//!
127//! impl BehaviorNode for PrintArmNode {
128//!     fn tick(&mut self, _arg: BehaviorCallback, ctx: &mut Context) -> BehaviorResult {
129//!         println!("Arm {:?}", ctx);
130//!
131//!         if let Some(arm) = ctx.get::<Arm>("arm") {
132//!             println!("Got {}", arm.name);
133//!         }
134//!         BehaviorResult::Success
135//!     }
136//! }
137//! ```
138//!
139//! In order to pass the variables, you need to set the variables to the blackboard.
140//! This is done by `Context::set` method.
141//!
142//! ```rust
143//! # use behavior_tree_lite::*;
144//! # #[derive(Debug)]
145//! # struct Body { left_arm: (), right_arm: () };
146//! struct PrintBodyNode;
147//!
148//! impl BehaviorNode for PrintBodyNode {
149//!     fn tick(&mut self, _arg: BehaviorCallback, ctx: &mut Context) -> BehaviorResult {
150//!         if let Some(body) = ctx.get::<Body>("body") {
151//!             let left_arm = body.left_arm.clone();
152//!             let right_arm = body.right_arm.clone();
153//!             println!("Got Body: {:?}", body);
154//!             ctx.set("left_arm", left_arm);
155//!             ctx.set("right_arm", right_arm);
156//!             BehaviorResult::Success
157//!         } else {
158//!             BehaviorResult::Fail
159//!         }
160//!     }
161//! }
162//! ```
163//!
164//! ### Optimizing port access by caching symbols
165//!
166//! If you use ports a lot, you can try to minimize the cost of comparing and finding the port names as strings by using symbols.
167//! Symbols are pointers that are guaranteed to compare equal if they point to the same string.
168//! So you can simply compare the address to check the equality of them.
169//!
170//! You can use `Lazy<Symbol>` to use cache-on-first-use pattern on the symbol like below.
171//! `Lazy` is re-exported type from `once_cell`.
172//!
173//! ```rust
174//! # struct Body;
175//! use ::behavior_tree_lite::{
176//!     BehaviorNode, BehaviorResult, BehaviorCallback, Symbol, Lazy, Context
177//! };
178//!
179//! struct PrintBodyNode;
180//!
181//! impl BehaviorNode for PrintBodyNode {
182//!     fn tick(&mut self, _: BehaviorCallback, ctx: &mut Context) -> BehaviorResult {
183//!         static BODY_SYM: Lazy<Symbol> = Lazy::new(|| "body".into());
184//!         static LEFT_ARM_SYM: Lazy<Symbol> = Lazy::new(|| "left_arm".into());
185//!         static RIGHT_ARM_SYM: Lazy<Symbol> = Lazy::new(|| "right_arm".into());
186//!
187//!         if let Some(body) = ctx.get::<Body>(*BODY_SYM) {
188//!             // ...
189//!             BehaviorResult::Success
190//!         } else {
191//!             BehaviorResult::Fail
192//!         }
193//!     }
194//! }
195//! ```
196//!
197//!
198//! ### Provided ports
199//!
200//! You can declare what ports you would use in a node by defining `provided_ports` method.
201//! This is optional, and only enforced if you specify `check_ports` argument in the `load` function explained later.
202//! However, declaring provided_ports will help statically checking the code and source file consistency, so is generally encouraged.
203//!
204//! ```rust
205//! use ::behavior_tree_lite::{
206//!     BehaviorNode, BehaviorCallback, BehaviorResult, Context, Symbol, Lazy, PortSpec
207//! };
208//!
209//! struct PrintBodyNode;
210//!
211//! impl BehaviorNode for PrintBodyNode {
212//!     fn provided_ports(&self) -> Vec<PortSpec> {
213//!         vec![
214//!             PortSpec::new_in("body"),
215//!             PortSpec::new_out("left_arm"),
216//!             PortSpec::new_out("right_arm")
217//!         ]
218//!     }
219//!
220//!     fn tick(&mut self, _: BehaviorCallback, ctx: &mut Context) -> BehaviorResult {
221//!         // ...
222//!         BehaviorResult::Success
223//!     }
224//! }
225//! ```
226//!
227//! See [example code](examples/main.rs) for the full code.
228//!
229//! ### Loading the tree structure from a yaml file
230//!
231//! Deprecated in favor of <a href="#The custom config file format">the custom config file format</a>.
232//! It doesn't have much advantage over our custom format, except that it can be parsed by any yaml parser library (not limited to Rust).
233//! However, parsing is only a small part of the whole process of loading dynamically configured behavior tree.
234//! There are validation of port mapping and specifying input/output,
235//! which is not any easier with yaml.
236//! Our custom format has much more flexibility in load-time validation and
237//! error handling.
238//!
239//! You can define the tree structure in a yaml file and configure it on runtime.
240//! Yaml file is very good for writing human readable/writable configuration file.
241//! It also looks similar to the actual tree structure.
242//!
243//! ```yaml
244//! behavior_tree:
245//!   type: Sequence
246//!   children:
247//!   - type: PrintBodyNode
248//!   - type: Sequence
249//!     children:
250//!     - type: PrintArmNode
251//!       ports:
252//!         arm: left_arm
253//!     - type: PrintArmNode
254//!       ports:
255//!         arm: right_arm
256//! ```
257//!
258//! In order to load a tree from a yaml file, you need to register the node types
259//! to the registry.
260//!
261//! ```rust
262//! # use ::behavior_tree_lite::*;
263//! # struct PrintBodyNode;
264//! # impl BehaviorNode for PrintBodyNode { fn tick(&mut self, _: BehaviorCallback, _: &mut Context) -> BehaviorResult { BehaviorResult::Success }}
265//! # struct PrintArmNode;
266//! # impl BehaviorNode for PrintArmNode { fn tick(&mut self, _: BehaviorCallback, _: &mut Context) -> BehaviorResult { BehaviorResult::Success }}
267//! let mut registry = Registry::default();
268//! registry.register("PrintArmNode", boxify(|| PrintArmNode));
269//! registry.register("PrintBodyNode", boxify(|| PrintBodyNode));
270//! ```
271//!
272//! Some node types are registered by default, e.g. `SequenceNode` and `FallbackNode`.
273//! ## The custom config file format
274//!
275//! We have specific file format for describing behavior tree structure of our own.
276//! With this format, the same tree shown as YAML earlier can be written even more concisely like below.
277//!
278//! ```raw
279//! tree main = Sequence {
280//!   PrintBodyNode
281//!   Sequence {
282//!     PrintArmNode (arm <- left_arm)
283//!     PrintArmNode (arm <- right_arm)
284//!   }
285//! }
286//! ```
287//!
288//! It can be converted to an AST with `parse_file` function.
289//! The AST (Abstract Syntax Tree) is a intermediate format of behavior tree,
290//! from which you can instantiate actual behavior trees as many times as you want.
291//! Note that the AST borrows lifetime of the argument string, so you cannot free the
292//! source string before the AST.
293//!
294//! ```rust
295//! # use ::behavior_tree_lite::*;
296//! # let source_string = "";
297//! # (|| -> Result<(&str, _), nom::error::Error<&str>> {
298//! let (_, tree_source) = parse_file(source_string)?;
299//! # Ok((source_string, tree_source))
300//! # })();
301//! ```
302//!
303//! and subsequently be instantiated to a tree.
304//! The second argument `registry` is the same as yaml parser.
305//! The third argument `check_ports` will switch port direction checking during loading.
306//! If your `BehaviorNode::provided_ports` and the source file's direction arrow (`<-`, `->` or `<->`) disagree, it will become an error.
307//!
308//! ```rust
309//! # use ::behavior_tree_lite::*;
310//! # use ::nom::IResult;
311//! # let source_string = "";
312//! # let mut registry = Registry::default();
313//! # let check_ports = true;
314//! # (|| -> Result<(), error::LoadError> {
315//! # let (_, tree_source) = parse_file(source_string).unwrap();
316//! let tree = load(&tree_source, &registry, check_ports)?;
317//! # Ok(())
318//! # })();
319//! ```
320//!
321//! ### Line comments
322//!
323//! You can put a line comment starting with a hash (`#`).
324//!
325//! ```raw
326//! # This is a comment at the top level.
327//!
328//! tree main = Sequence { # This is a comment after opening brace.
329//!            # This is a comment in a whole line.
330//!     var a  # This is a comment after a variable declaration.
331//!     Yes    # This is a comment after a node.
332//! }          # This is a comment after a closing brace.
333//! ```
334//!
335//!
336//! ### Node definition
337//!
338//! A node can be specified like below.
339//! It starts with a node name defined in Rust code.
340//! After that, it can take an optional list of input/output ports in parentheses.
341//!
342//! ```raw
343//! PrintString (input <- "Hello, world!")
344//! ```
345//!
346//! The direction of the arrow in the ports specify input, output or inout port types.
347//! The left hand side of the arrow is the port name defined in the node.
348//! The right hand side is the blackboard variable name or a literal.
349//!
350//! ```raw
351//! a <- b      input port
352//! a -> b      output port
353//! a <-> b     inout port
354//! ```
355//!
356//! You can specify a literal string, surrounded by double quotes, to an input port,
357//! but specifying a literal to output or inout node is an error.
358//! The type of the literal is always a string, so if you want a number,
359//! you may want to use `Context::get_parse()` method, which will automatically try
360//! to parse from a string, if the type was not desired one.
361//!
362//! ```raw
363//! a <- "Hi!"
364//! a -> "Error!"
365//! a <-> "Error too!"
366//! ```
367//!
368//! It is an error to try to read from an output port or write to an input port,
369//! but inout port can do both.
370//!
371//!
372//! ### Child nodes
373//!
374//! A node can have a list of child nodes in braces.
375//!
376//! ```raw
377//! Sequence {
378//!     PrintString (input <- "First")
379//!     PrintString (input <- "Second")
380//! }
381//! ```
382//!
383//! Or even both ports and children.
384//!
385//! ```raw
386//! Repeat (n <- "100") {
387//!     PrintString (input <- "Spam")
388//! }
389//! ```
390//!
391//! ### Subtrees
392//!
393//! It is very easy to define subtrees and organize your huge tree into modular structure.
394//!
395//! ```raw
396//! tree main = Sequence {
397//!     CanICallSubTree
398//!     SubTree
399//! }
400//!
401//! tree SubTree = Sequence {
402//!     PrintString (input <- "Hi there!")
403//! }
404//! ```
405//!
406//! A subtree has its own namespace of blackboard variables.
407//! It will keep the blackboard from being a huge table of global variables.
408//!
409//! If you need blackboard variables to communicate between the parent tree
410//! and the subtree, you can put parentheses and a comma-separated list
411//! after subtree name to specify the port definition of a subtree.
412//!
413//! A port "parameter" can be prefixed by either `in`, `out` or `inout`.
414//! It will indicate the direction of data flow.
415//!
416//! The syntax is intentionally made similar to a function definition, because
417//! it really is.
418//!
419//! ```raw
420//! tree main = Sequence {
421//!     SubTree (input <- "42", output -> subtreeResult)
422//!     PrintString (input <- subtreeResult)
423//! }
424//!
425//! tree SubTree(in input, out output) = Sequence {
426//!     Calculate (input <- input, result -> output)
427//! }
428//! ```
429//!
430//!
431//! ### Conditional syntax
432//!
433//! Like a programming language, the format supports conditional syntax.
434//!
435//! ```raw
436//! tree main = Sequence {
437//!     if (ConditionNode) {
438//!         Yes
439//!     }
440//! }
441//! ```
442//!
443//! If the `ConditionNode` returns `Success`, the inner braces are ticked
444//! as a Sequence, otherwise it skips the nodes.
445//!
446//! It is not an `if` statement per se, because behavior tree does not have
447//! the concept of a statement.
448//! It is internally just a behavior node, having a special syntax for the
449//! ease of editing and understanding.
450//!
451//! The code above desugars into this:
452//!
453//! ```raw
454//! tree main = Sequence {
455//!     if {
456//!         ConditionNode
457//!         Sequence {
458//!             Yes
459//!         }
460//!     }
461//! }
462//! ```
463//!
464//! As you may expect, you can put `else` clause.
465//!
466//! ```raw
467//! tree main = Sequence {
468//!     if (ConditionNode) {
469//!         Yes
470//!     } else {
471//!         No
472//!     }
473//! }
474//! ```
475//!
476//! `if` is a built-in node type, which can take 2 or 3 child nodes.
477//! The first child is the condition, the second is the `then` clause, and
478//! the optional third child is the `else` clause.
479//! `then` and `else` clause are implicitly wrapped in a `Sequence`.
480//!
481//! The syntax also supports negation operator (`!`) in front of the condition node.
482//! This code below is
483//!
484//! ```raw
485//! tree main = Sequence {
486//!     if (!ConditionNode) {
487//!         Yes
488//!     }
489//! }
490//! ```
491//!
492//! equivalent to this one:
493//!
494//! ```raw
495//! tree main = Sequence {
496//!     if (ConditionNode) {
497//!     } else {
498//!         Yes
499//!     }
500//! }
501//! ```
502//!
503//! You can put logical operators (`&&` and `||`) like conditional expressions in programming languages.
504//! `&&` is just a shorthand for a Sequence node and `||` is a Fallback node.
505//!
506//! ```raw
507//! tree main = Sequence {
508//!     if (!a || b && c) {}
509//! }
510//! ```
511//!
512//! In fact, a child node is implicitly a logical expression, so you can write like this:
513//!
514//! ```raw
515//! tree main = Sequence {
516//!     !a || b && c
517//! }
518//! ```
519//!
520//! Parentheses can be used to group operators.
521//!
522//! ```raw
523//! tree main = Sequence {
524//!     (!a || b) && c
525//! }
526//! ```
527//!
528//! `if` node without else clause is semantically the same as a Sequence node like below,
529//! but Sequence or Fallback nodes cannot represent `else` clause easily.
530//!
531//! ```raw
532//! tree main = Sequence {
533//!     Sequence {
534//!         ConditionNode
535//!         Sequence {
536//!             Yes
537//!         }
538//!     }
539//! }
540//! ```
541//!
542//!
543//! ### Blackboard variable declarations
544//!
545//! You can optionally declare and initialize a blackboard variable.
546//! It can be used as a node name, and its value is evaluated as a boolean.
547//! So, you can put the variable into a `if` condition.
548//!
549//! ```raw
550//! tree main = Sequence {
551//!     var flag = true
552//!     if (flag) {
553//!         Yes
554//!     }
555//! }
556//! ```
557//!
558//! Currently, only `true` or `false` is allowed as the initializer (the right hand side of `=`).
559//!
560//! The variable declaration with initialization will desugar into a `SetBool` node.
561//! A reference to a variable name will desugar into a `IsTrue` node.
562//!
563//! ```raw
564//! tree main = Sequence {
565//!     SetBool (value <- "true", output -> flag)
566//!     if (IsTrue (input <- flag)) {
567//!         Yes
568//!     }
569//! }
570//! ```
571//!
572//! However, it is necessary to declare the variable name in order to use it as a
573//! variable reference.
574//! For example, the code below will be a `load` error for `MissingNode`, even though
575//! the variable is set with `SetBool`.
576//!
577//! ```raw
578//! tree main = Sequence {
579//!     SetBool (value <- "true", output -> flag)
580//!     if (flag) {
581//!         Yes
582//!     }
583//! }
584//! ```
585//!
586//! This design is a step towards statically checked source code.
587//!
588//!
589//!
590//! ### Syntax specification
591//!
592//! Here is a pseudo-EBNF notation of the syntax.
593//!
594//! Note that this specification is by no means accurate EBNF.
595//! The syntax is defined by recursive descent parser with parser combinator,
596//! which removes ambiguity, but this EBNF may have ambiguity.
597//!
598//! ```raw
599//! tree = "tree" tree-name [ "(" tree-port-list ")" ] "=" node
600//!
601//! tree-port-list = port-def | tree-port-list "," port-def
602//!
603//! port-def = ( "in" | "out" | "inout" ) tree-port-name
604//!
605//! tree-port-name = identifier
606//!
607//! node = if-syntax | conditional | var-def-syntax | var-assign
608//!
609//! if-syntax = "if" "(" conditional ")"
610//!
611//! conditional-factor = "!" conditional-factor | node-syntax
612//!
613//! conditional-and =  conditional-factor | conditional "&&" conditional-factor
614//!
615//! conditional =  conditional-and | conditional "||" conditional-and
616//!
617//! node-syntax = node-name [ "(" port-list ")" ] [ "{" node* "}" ]
618//!
619//! port-list = port [ "," port-list ]
620//!
621//! port = node-port-name ("<-" | "->" | "<->") blackboard-port-name
622//!
623//! node-port-name = identifier
624//!
625//! blackboard-port-name = identifier
626//!
627//! var-def-syntax = "var" identifier "=" initializer
628//!
629//! var-assign = identifier "=" initializer
630//!
631//! initializer = "true" | "false"
632//! ```
633//!
634//!
635//! ## TODO
636//!
637//! * [x] Easier way to define constructors (macros?)
638//! * [ ] Full set of control nodes
639//!   * [x] Reactive nodes
640//!   * [ ] Star nodes
641//!   * [x] Decorator nodes
642//! * [x] Performance friendly blackboard keys
643//! * [x] DSL for defining behavior tree structure
644//!   * [x] Programming language-like flow control syntax
645//! * [ ] Static type checking for behavior tree definition file
646//!
647//! # Historical notes
648//!
649//! This is a sister project of [tiny-behavior-tree](https://github.com/msakuta/rusty_tiny_behavior_tree) which in turn inspired by [BehaviorTreeCPP](https://github.com/BehaviorTree/BehaviorTree.CPP.git).
650//!
651//! While tiny-behavior-tree aims for more innovative design and experimental features, this crate aims for more traditional behavior tree implementation.
652//! The goal is to make a crate lightweight enough to use in WebAssembly.
653//!
654//! ## The difference from tiny-behavior-tree
655//!
656//! The main premise of tiny-behavior-tree is that it passes data with function arguments.
657//! This is very good for making fast and small binary, but it suffers from mixed node types in a tree.
658//!
659//! It requires ugly boilerplate code or macros to convert types between different node argument types, and there is the concept of "PeelNode" which would be unnecessary in traditional behavior tree design.
660//!
661//! On top of that, uniform types make it much easier to implement configuration file parser that can change the behavior tree at runtime.
662//!
663//!
664//! ## Performance consideration
665//!
666//! One of the issues with behavior tree in general regarding performance is that the nodes communicate with blackboard variables, which is essentially a key-value store.
667//! It is not particularly bad, but if you read/write a lot of variables in the blackboard (which easily happens with a large behavior tree), you would pay the cost of constructing a string and looking up HashMap every time.
668//!
669//! One of the tiny-behavior-tree's goals is to address this issue by passing variables with function call arguments.
670//! Why would you pay the cost of looking up HashMap if you already know the address of the variable?
671//!
672//! Also, the blackboard is not very scalable, since it is essentially a huge table of global variables.
673//! Although there is sub-blackboards in subtrees, it is difficult to keep track of similar to scripting language's stack frame without proper debugging tools.
674//!
675//! I might experiment with non-string keys to make it more efficient, but the nature of the variables need to be handled dynamically in uniformly typeds nodes.
676
677mod container;
678mod context;
679pub mod error;
680mod nodes;
681pub mod parser;
682mod port;
683mod registry;
684mod symbol;
685
686use std::any::Any;
687use std::collections::HashMap;
688use std::rc::Rc;
689
690pub use crate::container::BehaviorNodeContainer;
691pub use crate::context::Context;
692pub use crate::nodes::{tick_child_node, FallbackNode, SequenceNode};
693pub use crate::symbol::Symbol;
694pub use crate::{
695    parser::{load, load_yaml, node_def, parse_file, parse_nodes, NodeDef},
696    port::{AbstractPortMap, BlackboardValueOwned, PortSpec, PortType},
697    registry::{boxify, Constructor, Registry},
698};
699pub use ::once_cell::sync::*;
700
701#[derive(PartialEq, Eq, Debug, Clone, Copy)]
702pub enum BehaviorResult {
703    Success,
704    Fail,
705    /// The node should keep running in the next tick
706    Running,
707}
708
709#[derive(Debug)]
710pub enum BlackboardValue {
711    Ref(Symbol, PortType),
712    Literal(String),
713}
714
715impl From<&str> for BlackboardValue {
716    fn from(s: &str) -> Self {
717        Self::Literal(s.to_owned())
718    }
719}
720
721/// Blackboard is a mapping of a variable names and their values.
722/// The value is wrapped in an `Any` trait object, so it can be any type.
723///
724/// # Implementation note
725///
726/// You may wonder why the value is wrapped in an `Rc`, not a `Box`.
727/// It seems unnecessary to have reference count for owned values inside a
728/// blackboard.
729/// The reason is that we need to pass the copy of the variables to the subtree by
730/// copying values, but `Clone` trait is not object safe.
731///
732/// Another way to work around this is to define a new trait, like `AnyClone`,
733/// that can clone and be object safe at the same time.
734/// The signature for the clone method would be something like:
735///
736/// ```
737/// # use std::any::Any;
738/// trait AnyClone: Any {
739///     fn any_clone(&self) -> Box<dyn AnyClone>;
740/// }
741/// ```
742///
743/// The difference from `Clone` trait is that it returns a boxed copy of the object
744/// to avoid the requirement that returned value of a function needs to be `Sized`.
745/// However, requiring every type that can exist in the blackboard to implement this
746/// trait is a bit too much to ask to the users.
747/// By wrapping in an `Rc`, it looks like cloneable, but it doesn't require the value
748/// type to implement anything but `Any`.
749///
750/// Interestingly, this is the exactly the same issue when you try to implement
751/// a function call in your own programming language, i.e. pass-by-value v.s. pass-by-reference.
752/// In essence, a subtree in behavior tree is a function in a programming language.
753/// The third sect in the society is copy-on-write reference, which is what `Rc` does.
754pub type Blackboard = HashMap<Symbol, Rc<dyn Any>>;
755pub type BBMap = HashMap<Symbol, BlackboardValue>;
756pub type BehaviorCallback<'a> = &'a mut dyn FnMut(&dyn Any) -> Option<Box<dyn Any>>;
757
758#[derive(PartialEq, Eq)]
759pub enum NumChildren {
760    Finite(usize),
761    Infinite,
762}
763
764impl PartialOrd for NumChildren {
765    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
766        Some(match (self, other) {
767            (NumChildren::Finite(_), NumChildren::Infinite) => std::cmp::Ordering::Less,
768            (NumChildren::Infinite, NumChildren::Finite(_)) => std::cmp::Ordering::Greater,
769            (NumChildren::Finite(lhs), NumChildren::Finite(rhs)) => lhs.cmp(rhs),
770            (NumChildren::Infinite, NumChildren::Infinite) => return None,
771        })
772    }
773}
774
775pub trait BehaviorNode {
776    fn provided_ports(&self) -> Vec<PortSpec> {
777        vec![]
778    }
779
780    fn tick(&mut self, arg: BehaviorCallback, ctx: &mut Context) -> BehaviorResult;
781
782    fn max_children(&self) -> NumChildren {
783        NumChildren::Finite(0)
784    }
785}
786
787#[macro_export]
788macro_rules! hash_map {
789    () => {
790        std::collections::HashMap::default()
791    };
792    ($name: literal => $val: expr) => {{
793        let mut ret = std::collections::HashMap::default();
794        ret.insert($name.into(), $val.into());
795        ret
796    }};
797}