use std::collections::HashMap;
use arora_behavior::graph::{Graph, LinkSource};
use uuid::Uuid;
use crate::error::BehaviorTreeError;
use crate::schema::{Expression, Node as SchemaNode, NodeParameterId};
use crate::variable::VariableResolver;
use crate::{load_behavior_tree_nodes_with, BehaviorTree};
pub fn graph_to_bt_nodes(graph: &Graph) -> Result<Vec<SchemaNode>, BehaviorTreeError> {
let mut args_by_node: HashMap<Uuid, HashMap<Uuid, Expression>> = HashMap::new();
for link in &graph.links {
let expr = link_source_to_expression(&link.source);
args_by_node
.entry(link.target.node)
.or_default()
.insert(link.target.port, expr);
}
let mut ordered: Vec<&Uuid> = graph.nodes.keys().collect();
if let Some(root) = &graph.root {
if !graph.nodes.contains_key(root) {
return Err(BehaviorTreeError::InconsistentTreeError {
message: format!("graph root {root} is not a node"),
});
}
ordered.sort_by_key(|id| *id != root);
}
let mut nodes = Vec::with_capacity(graph.nodes.len());
for id in ordered {
let node = &graph.nodes[id];
nodes.push(SchemaNode {
id: node.id,
function: node.function,
arguments: args_by_node.remove(&node.id).unwrap_or_default(),
children: node.children.clone(),
});
}
Ok(nodes)
}
pub fn build_behavior_tree(
graph: &Graph,
resolver: &VariableResolver,
) -> Result<BehaviorTree, BehaviorTreeError> {
let nodes = graph_to_bt_nodes(graph)?;
load_behavior_tree_nodes_with(nodes, resolver, &graph.variables)
}
fn link_source_to_expression(source: &LinkSource) -> Expression {
match source {
LinkSource::Literal(value) => Expression::Value(value.clone()),
LinkSource::Variable(id) => Expression::VariableId(*id),
LinkSource::Port(port) => Expression::NodeArgument(NodeParameterId {
node: port.node,
parameter: port.port,
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nodes::{FAIL_FUNCTION_ID, SEQ_FUNCTION_ID, SUCCEED_FUNCTION_ID};
use crate::run_behavior_tree;
use arora_behavior::graph::{GraphDiff, Node as GraphNode};
use arora_types::call::{Call, CallBridge, CallError, CallResult, Callable, CallableId};
use arora_types::value::Value;
use std::rc::Rc;
#[derive(Default)]
struct NativeBridge {
registered: HashMap<u64, Rc<dyn Callable>>,
next_id: u64,
}
impl CallBridge for NativeBridge {
fn arora_call(&mut self, _module: &Uuid, call: Call) -> Result<CallResult, CallError> {
Err(CallError::FunctionNotFound { id: call.id })
}
fn arora_register_callable(&mut self, callable: Rc<dyn Callable>) -> CallableId {
let id = self.next_id;
self.next_id += 1;
self.registered.insert(id, callable);
CallableId { id }
}
fn arora_unregister_callable(&mut self, callable_id: &CallableId) {
self.registered.remove(&callable_id.id);
}
fn arora_call_indirect(&mut self, callable_id: &CallableId) -> Result<Value, CallError> {
let callable =
self.registered
.get(&callable_id.id)
.cloned()
.ok_or(CallError::Generic {
message: format!("unknown callable {}", callable_id.id),
})?;
callable.call(self)
}
}
fn control(id: Uuid, function: Uuid, children: Vec<Uuid>) -> GraphNode {
GraphNode {
id,
function,
children: Some(children),
..GraphNode::default()
}
}
fn leaf(id: Uuid, function: Uuid) -> GraphNode {
GraphNode {
id,
function,
..GraphNode::default()
}
}
fn run(graph: &Graph) -> crate::arora_generated::behavior_tree::status::Status {
let tree = build_behavior_tree(graph, &|_| None).expect("tree builds");
let mut bridge = NativeBridge::default();
run_behavior_tree(&tree, Rc::new(HashMap::new()), &mut bridge, false).expect("run")
}
#[test]
fn seq_of_builtins_lowers_and_runs() {
use crate::arora_generated::behavior_tree::status::Status;
let root = Uuid::from_u128(0x100);
let a = Uuid::from_u128(0x1);
let b = Uuid::from_u128(0x2);
let mut graph = Graph::empty();
graph.root = Some(root);
graph
.nodes
.insert(root, control(root, SEQ_FUNCTION_ID, vec![a, b]));
graph.nodes.insert(a, leaf(a, SUCCEED_FUNCTION_ID));
graph.nodes.insert(b, leaf(b, SUCCEED_FUNCTION_ID));
assert_eq!(run(&graph), Status::Success);
graph
.apply(GraphDiff {
add_nodes: vec![leaf(b, FAIL_FUNCTION_ID)],
..GraphDiff::default()
})
.unwrap();
assert_eq!(run(&graph), Status::Failure);
}
#[test]
fn root_node_is_lowered_first() {
let root = Uuid::from_u128(0xEEE);
let child = Uuid::from_u128(0x1);
let mut graph = Graph::empty();
graph.root = Some(root);
graph.nodes.insert(child, leaf(child, SUCCEED_FUNCTION_ID));
graph
.nodes
.insert(root, control(root, SEQ_FUNCTION_ID, vec![child]));
let nodes = graph_to_bt_nodes(&graph).unwrap();
assert_eq!(nodes.first().unwrap().id, root);
}
#[test]
fn missing_root_node_is_an_error() {
let mut graph = Graph::empty();
graph.root = Some(Uuid::from_u128(0xDEAD));
assert!(graph_to_bt_nodes(&graph).is_err());
}
#[test]
fn groot_lowers_to_graph_and_runs() {
use crate::arora_generated::behavior_tree::status::Status;
use crate::schema_groot::BehaviorTree as GrootTree;
let xml = r#"<root main_tree_to_execute="MainTree">
<BehaviorTree ID="MainTree">
<Sequence>
<Succeed/>
<Succeed/>
</Sequence>
</BehaviorTree>
</root>"#;
let groot = GrootTree::try_from_groot_xml(xml).expect("parse");
let graph = groot.into_graph(&HashMap::new()).expect("lower to graph");
assert!(graph.root.is_some());
assert_eq!(graph.nodes.len(), 3, "sequence + two leaves");
assert_eq!(run(&graph), Status::Success);
}
}