use crate as bevior_tree;
use crate::node::prelude::*;
use super::Parallel;
use crate::sequential::variants::{AndResultStrategy, OrResultStrategy};
pub mod prelude {
pub use super::{Join, ParallelAnd, ParallelOr};
}
#[delegate_node(delegate)]
pub struct ParallelAnd {
delegate: Parallel,
}
impl ParallelAnd {
pub fn new(nodes: Vec<Box<dyn Node>>) -> Self {
Self {
delegate: Parallel::new(nodes, AndResultStrategy),
}
}
}
#[delegate_node(delegate)]
pub struct ParallelOr {
delegate: Parallel,
}
impl ParallelOr {
pub fn new(nodes: Vec<Box<dyn Node>>) -> Self {
Self {
delegate: Parallel::new(nodes, OrResultStrategy),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug)]
pub struct JoinResultStrategy;
#[cfg_attr(feature = "serde", typetag::serde)]
impl crate::sequential::ResultStrategy for JoinResultStrategy {
fn construct(&self, results: Vec<Option<NodeResult>>) -> Option<NodeResult> {
if results.contains(&None) {
None
} else {
Some(NodeResult::Success)
}
}
}
#[delegate_node(delegate)]
pub struct Join {
delegate: Parallel,
}
impl Join {
pub fn new(nodes: Vec<Box<dyn Node>>) -> Self {
Self {
delegate: Parallel::new(nodes, JoinResultStrategy),
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use crate::tester_util::prelude::*;
#[test]
fn test_and() {
let mut app = App::new();
app.add_plugins((TesterPlugin, BehaviorTreePlugin::default()));
let parallel = ParallelAnd::new(vec![
Box::new(TesterTask0::new(1, NodeResult::Success)),
Box::new(TesterTask1::new(2, NodeResult::Success)),
Box::new(TesterTask2::new(3, NodeResult::Failure)),
Box::new(TesterTask3::new(4, NodeResult::Success)),
]);
let tree = BehaviorTree::from_node(
parallel,
&mut app.world_mut().resource_mut::<Assets<BehaviorTreeRoot>>(),
);
let _entity = app.world_mut().spawn(tree).id();
app.update();
app.update(); app.update(); app.update(); app.update(); let expected: HashSet<TestLogEntry> = vec![
TestLogEntry {
task_id: 0,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 2,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 3,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 3,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 2,
frame: 3,
},
TestLogEntry {
task_id: 3,
updated_count: 2,
frame: 3,
},
]
.into_iter()
.collect();
let found: HashSet<TestLogEntry> = app
.world()
.get_resource::<TestLog>()
.unwrap()
.log
.clone()
.into_iter()
.collect();
assert!(
found == expected,
"ParallelAnd should match result. found: {:?}",
found
);
}
#[test]
fn test_or() {
let mut app = App::new();
app.add_plugins((TesterPlugin, BehaviorTreePlugin::default()));
let parallel = ParallelOr::new(vec![
Box::new(TesterTask0::new(1, NodeResult::Failure)),
Box::new(TesterTask1::new(2, NodeResult::Failure)),
Box::new(TesterTask2::new(3, NodeResult::Success)),
Box::new(TesterTask3::new(4, NodeResult::Failure)),
]);
let tree = BehaviorTree::from_node(
parallel,
&mut app.world_mut().resource_mut::<Assets<BehaviorTreeRoot>>(),
);
let _entity = app.world_mut().spawn(tree).id();
app.update();
app.update(); app.update(); app.update(); app.update(); let expected: HashSet<TestLogEntry> = vec![
TestLogEntry {
task_id: 0,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 2,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 3,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 3,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 2,
frame: 3,
},
TestLogEntry {
task_id: 3,
updated_count: 2,
frame: 3,
},
]
.into_iter()
.collect();
let found: HashSet<TestLogEntry> = app
.world()
.get_resource::<TestLog>()
.unwrap()
.log
.clone()
.into_iter()
.collect();
assert!(
found == expected,
"ParallelOr should match result. found: {:?}",
found
);
}
#[test]
fn test_join() {
let mut app = App::new();
app.add_plugins((TesterPlugin, BehaviorTreePlugin::default()));
let parallel = Join::new(vec![
Box::new(TesterTask0::new(1, NodeResult::Success)),
Box::new(TesterTask1::new(2, NodeResult::Success)),
Box::new(TesterTask2::new(3, NodeResult::Failure)),
Box::new(TesterTask3::new(4, NodeResult::Success)),
]);
let tree = BehaviorTree::from_node(
parallel,
&mut app.world_mut().resource_mut::<Assets<BehaviorTreeRoot>>(),
);
let _entity = app.world_mut().spawn(tree).id();
app.update();
app.update(); app.update(); app.update(); app.update(); app.update(); let expected: HashSet<TestLogEntry> = vec![
TestLogEntry {
task_id: 0,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 2,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 3,
updated_count: 0,
frame: 1,
},
TestLogEntry {
task_id: 1,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 3,
updated_count: 1,
frame: 2,
},
TestLogEntry {
task_id: 2,
updated_count: 2,
frame: 3,
},
TestLogEntry {
task_id: 3,
updated_count: 2,
frame: 3,
},
TestLogEntry {
task_id: 3,
updated_count: 3,
frame: 4,
},
]
.into_iter()
.collect();
let found: HashSet<TestLogEntry> = app
.world()
.get_resource::<TestLog>()
.unwrap()
.log
.clone()
.into_iter()
.collect();
assert!(
found == expected,
"Join should match result. found: {:?}",
found
);
}
}