use crate::{BehaviorNode, BoxedBehavior, NodeStatus};
use async_trait::async_trait;
use mecha10_core::Context;
use tracing::{debug, warn};
#[derive(Debug)]
pub struct SequenceNode {
children: Vec<BoxedBehavior>,
current_index: usize,
}
impl SequenceNode {
pub fn new(children: Vec<BoxedBehavior>) -> Self {
Self {
children,
current_index: 0,
}
}
pub fn add_child(&mut self, child: BoxedBehavior) {
self.children.push(child);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
}
#[async_trait]
impl BehaviorNode for SequenceNode {
async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
if self.children.is_empty() {
return Ok(NodeStatus::Success);
}
let total_children = self.children.len();
while self.current_index < total_children {
debug!(
"Sequence: ticking child {} of {}",
self.current_index + 1,
total_children
);
let child = &mut self.children[self.current_index];
match child.tick(ctx).await? {
NodeStatus::Success => {
self.current_index += 1;
}
NodeStatus::Failure => {
debug!("Sequence: child {} failed", self.current_index + 1);
return Ok(NodeStatus::Failure);
}
NodeStatus::Running => {
return Ok(NodeStatus::Running);
}
}
}
debug!("Sequence: all children succeeded");
Ok(NodeStatus::Success)
}
crate::reset_index_field!(current_index);
fn name(&self) -> &str {
"sequence"
}
fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
crate::boxed_children_mut(self.children.iter_mut())
}
}
#[derive(Debug)]
pub struct SelectOrNode {
children: Vec<BoxedBehavior>,
current_index: usize,
}
impl SelectOrNode {
pub fn new(children: Vec<BoxedBehavior>) -> Self {
Self {
children,
current_index: 0,
}
}
pub fn add_child(&mut self, child: BoxedBehavior) {
self.children.push(child);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
}
#[async_trait]
impl BehaviorNode for SelectOrNode {
async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
if self.children.is_empty() {
return Ok(NodeStatus::Failure);
}
let total_children = self.children.len();
while self.current_index < total_children {
debug!(
"Selector: ticking child {} of {}",
self.current_index + 1,
total_children
);
let child = &mut self.children[self.current_index];
match child.tick(ctx).await? {
NodeStatus::Success => {
debug!("Selector: child {} succeeded", self.current_index + 1);
return Ok(NodeStatus::Success);
}
NodeStatus::Failure => {
warn!("Selector: child {} failed, trying next", self.current_index + 1);
self.current_index += 1;
}
NodeStatus::Running => {
return Ok(NodeStatus::Running);
}
}
}
debug!("Selector: all children failed");
Ok(NodeStatus::Failure)
}
crate::reset_index_field!(current_index);
fn name(&self) -> &str {
"selector"
}
fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
crate::boxed_children_mut(self.children.iter_mut())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParallelPolicy {
RequireAll,
RequireOne,
RequireN(usize),
}
#[derive(Debug)]
pub struct ParallelNode {
children: Vec<BoxedBehavior>,
policy: ParallelPolicy,
child_statuses: Vec<NodeStatus>,
}
impl ParallelNode {
pub fn new(children: Vec<BoxedBehavior>, policy: ParallelPolicy) -> Self {
let child_statuses = vec![NodeStatus::Running; children.len()];
Self {
children,
policy,
child_statuses,
}
}
pub fn add_child(&mut self, child: BoxedBehavior) {
self.children.push(child);
self.child_statuses.push(NodeStatus::Running);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
}
#[async_trait]
impl BehaviorNode for ParallelNode {
async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
if self.children.is_empty() {
return Ok(NodeStatus::Success);
}
for (i, child) in self.children.iter_mut().enumerate() {
if self.child_statuses[i].is_running() {
let status = child.tick(ctx).await?;
self.child_statuses[i] = status;
debug!("Parallel: child {} status: {}", i + 1, status);
}
}
let success_count = self.child_statuses.iter().filter(|s| s.is_success()).count();
let failure_count = self.child_statuses.iter().filter(|s| s.is_failure()).count();
let _running_count = self.child_statuses.iter().filter(|s| s.is_running()).count();
match self.policy {
ParallelPolicy::RequireAll => {
if failure_count > 0 {
debug!("Parallel (RequireAll): at least one child failed");
Ok(NodeStatus::Failure)
} else if success_count == self.children.len() {
debug!("Parallel (RequireAll): all children succeeded");
Ok(NodeStatus::Success)
} else {
Ok(NodeStatus::Running)
}
}
ParallelPolicy::RequireOne => {
if success_count > 0 {
debug!("Parallel (RequireOne): at least one child succeeded");
Ok(NodeStatus::Success)
} else if failure_count == self.children.len() {
debug!("Parallel (RequireOne): all children failed");
Ok(NodeStatus::Failure)
} else {
Ok(NodeStatus::Running)
}
}
ParallelPolicy::RequireN(n) => {
if success_count >= n {
debug!("Parallel (RequireN({})): {} children succeeded", n, success_count);
Ok(NodeStatus::Success)
} else if (self.children.len() - failure_count) < n {
debug!("Parallel (RequireN({})): not enough children can succeed", n);
Ok(NodeStatus::Failure)
} else {
Ok(NodeStatus::Running)
}
}
}
}
crate::reset_child_statuses_field!(child_statuses);
fn name(&self) -> &str {
"parallel"
}
fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
crate::boxed_children_mut(self.children.iter_mut())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Default)]
struct DummyBehavior;
#[async_trait]
impl BehaviorNode for DummyBehavior {
async fn tick(&mut self, _ctx: &Context) -> anyhow::Result<NodeStatus> {
unimplemented!("not exercised by these tests")
}
}
#[test]
fn sequence_children_mut_exposes_every_child() {
let mut node = SequenceNode::new(vec![Box::new(DummyBehavior), Box::new(DummyBehavior)]);
assert_eq!(node.children_mut().len(), 2);
}
#[tokio::test]
async fn sequence_reset_rewinds_current_index() {
let mut node = SequenceNode::new(vec![Box::new(DummyBehavior)]);
node.current_index = 1;
node.reset().await.unwrap();
assert_eq!(node.current_index, 0);
}
#[test]
fn selector_children_mut_exposes_every_child() {
let mut node = SelectOrNode::new(vec![Box::new(DummyBehavior), Box::new(DummyBehavior)]);
assert_eq!(node.children_mut().len(), 2);
}
#[tokio::test]
async fn selector_reset_rewinds_current_index() {
let mut node = SelectOrNode::new(vec![Box::new(DummyBehavior)]);
node.current_index = 1;
node.reset().await.unwrap();
assert_eq!(node.current_index, 0);
}
#[test]
fn parallel_children_mut_exposes_every_child() {
let mut node = ParallelNode::new(
vec![Box::new(DummyBehavior), Box::new(DummyBehavior)],
ParallelPolicy::RequireAll,
);
assert_eq!(node.children_mut().len(), 2);
}
#[tokio::test]
async fn parallel_reset_clears_child_statuses_back_to_running() {
let mut node = ParallelNode::new(vec![Box::new(DummyBehavior)], ParallelPolicy::RequireAll);
node.child_statuses[0] = NodeStatus::Success;
node.reset().await.unwrap();
assert_eq!(node.child_statuses[0], NodeStatus::Running);
}
}