1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::fmt::Debug;
use crate::{state::State, ActionArgs, Behavior, Float, Status, UpdateEvent};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// The execution state of a behavior tree, along with a "blackboard" (state
/// shared between all nodes in the tree).
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BT<A, B> {
/// constructed behavior tree
state: State<A>,
/// keep the initial state
initial_behavior: Behavior<A>,
/// The data storage shared by all nodes in the tree. This is generally
/// referred to as a "blackboard". State is written to and read from a
/// blackboard, allowing nodes to share state and communicate each other.
bb: B,
/// Whether the tree has been finished before.
finished: bool,
}
impl<A: Clone, B> BT<A, B> {
pub fn new(behavior: Behavior<A>, blackboard: B) -> Self {
let backup_behavior = behavior.clone();
let bt = State::new(behavior);
Self {
state: bt,
initial_behavior: backup_behavior,
bb: blackboard,
finished: false,
}
}
/// Updates the cursor that tracks an event. Returns [`None`] if attempting
/// to tick after this tree has already returned [`Status::Success`] or
/// [`Status::Failure`].
///
/// The action need to return status and remaining delta time.
/// Returns status and the remaining delta time.
///
/// Passes event, delta time in seconds, action and state to closure.
/// The closure should return a status and remaining delta time.
///
/// return: (Status, Float)
/// function returns the result of the tree traversal, and how long
/// it actually took to complete the traversal and propagate the
/// results back up to the root node
#[inline]
pub fn tick<E, F>(&mut self, e: &E, f: &mut F) -> Option<(Status, Float)>
where
E: UpdateEvent,
F: FnMut(ActionArgs<E, A>, &mut B) -> (Status, Float),
{
if self.finished {
return None;
}
match self.state.tick(e, &mut self.bb, f) {
result @ (Status::Success | Status::Failure, _) => {
self.finished = true;
Some(result)
}
result => Some(result),
}
}
/// Retrieve an immutable reference to the blackboard for
/// this Behavior Tree
pub fn blackboard(&self) -> &B {
&self.bb
}
/// Retrieve a mutable reference to the blackboard for
/// this Behavior Tree
pub fn blackboard_mut(&mut self) -> &mut B {
&mut self.bb
}
/// The behavior tree is a stateful data structure in which the immediate
/// state of the BT is allocated and updated in heap memory through the lifetime
/// of the BT. The state of the BT is said to be `transient` meaning upon entering
/// a this state, the process may never return this state again. If a behavior concludes,
/// only the latest results will be stored in heap memory.
///
/// If your BT has surpassed a desired state or that your BT has reached a steady state - meaning
/// that the behavior has concluded and ticking the BT won't progress any further - then it could
/// be desirable to return the BT to it's initial state at t=0.0 before it was ever ticked.
///
/// PS! invoking reset_bt does not reset the Blackboard.
pub fn reset_bt(&mut self) {
let initial_behavior = self.initial_behavior.to_owned();
self.state = State::new(initial_behavior);
self.finished = false;
}
/// Whether this behavior tree is in a completed state (the last tick returned
/// [`Status::Success`] or [`Status::Failure`]).
pub fn is_finished(&self) -> bool {
self.finished
}
}
#[cfg(feature = "visualize")]
impl<A: Clone + Debug, B: Debug> BT<A, B> {
/// Compile the behavior tree into a [graphviz](https://graphviz.org/) compatible [DiGraph](https://docs.rs/petgraph/latest/petgraph/graph/type.DiGraph.html).
///
/// ```rust
/// use std::collections::HashMap;
/// use bonsai_bt::{
/// Behavior::{Action, Sequence, Wait, WaitForever, While},
/// BT
/// };
///
/// #[derive(Clone, Debug, Copy)]
/// pub enum Counter {
/// // Increment accumulator.
/// Inc,
/// // Decrement accumulator.
/// Dec,
/// }
///
///
/// // create the behavior
/// let behavior = While(Box::new(WaitForever), vec![Wait(0.5), Action(Counter::Inc), WaitForever]);
///
/// let h: HashMap<String, i32> = HashMap::new();
/// let mut bt = BT::new(behavior, h);
///
/// // produce a string DiGraph compatible with graphviz
/// // paste the contents in graphviz, e.g: https://dreampuf.github.io/GraphvizOnline/#
/// let g = bt.get_graphviz();
/// println!("{}", g);
/// ```
pub fn get_graphviz(&mut self) -> String {
self.get_graphviz_with_graph_instance().0
}
pub(crate) fn get_graphviz_with_graph_instance(
&mut self,
) -> (String, petgraph::Graph<crate::visualizer::NodeType<A>, u32>) {
use crate::visualizer::NodeType;
use petgraph::dot::{Config, Dot};
use petgraph::Graph;
let behavior = self.initial_behavior.to_owned();
let mut graph = Graph::<NodeType<A>, u32, petgraph::Directed>::new();
let root_id = graph.add_node(NodeType::Root);
Self::dfs_recursive(&mut graph, behavior, root_id);
let digraph = Dot::with_config(&graph, &[Config::EdgeNoLabel]);
(format!("{:?}", digraph), graph)
}
}