simple_bt 1.0.1

minimal(-ish) behavior tree implementation
Documentation
use std::fmt::Debug;
use std::sync::Arc;

use crate::{BehaviorArc, BehaviorNode, NodeResult, ReportStatus};

/// Repeats its child infintely
pub struct Repeated<B> {
    resume: Option<BehaviorArc<B>>,
    child: BehaviorArc<B>,
}

impl<B> Clone for Repeated<B> {
    fn clone(&self) -> Self {
        Self {
            resume: self.resume.clone(),
            child: self.child.clone(),
        }
    }
}

impl<B> Debug for Repeated<B> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Repeated")
            .field("child", &self.child)
            .finish()
    }
}

impl<B> Repeated<B> {
    pub fn new(child: BehaviorArc<B>) -> Self {
        Self {
            child,
            resume: None,
        }
    }
}

impl<B: 'static> BehaviorNode<B> for Repeated<B> {
    fn tick(mut self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
        if self.resume.is_some() {
            let inner = Arc::make_mut(&mut self);
            if let Some(resume) = inner.resume.take() {
                if let NodeResult::Running(resume) = resume.tick(blackboard) {
                    inner.resume = Some(resume);
                    return NodeResult::Running(self);
                }
            }
        }

        if let NodeResult::Running(resume) = self.child.clone().tick(blackboard) {
            let inner = Arc::make_mut(&mut self);
            inner.resume = Some(resume);
            return NodeResult::Running(self);
        }

        // Restart, cuz we never end
        NodeResult::Running(self)
    }

    fn status(&self, _parent: ReportStatus, context: &B) -> crate::BehaviorStatus {
        let has_run = self.resume.is_some();
        let status = if has_run {
            ReportStatus::Running
        } else {
            ReportStatus::Unexecuted
        };

        crate::BehaviorStatus {
            name: std::any::type_name_of_val(self).into(),
            status,
            children: vec![self.child.status(status, context)],
            current: if has_run { Some(0) } else { None },
        }
    }
}

/// Repeats its child a set number of times
pub struct LimitedRepeated<B> {
    child: BehaviorArc<B>,
    limit: usize,
    completed: usize,
    resume: Option<BehaviorArc<B>>,
}

impl<B> LimitedRepeated<B> {
    pub fn new(limit: usize, child: BehaviorArc<B>) -> Self {
        Self {
            child,
            limit,
            completed: 0,
            resume: None,
        }
    }
}

impl<B> Clone for LimitedRepeated<B> {
    fn clone(&self) -> Self {
        Self {
            child: self.child.clone(),
            resume: self.resume.clone(),
            limit: self.limit,
            completed: self.completed,
        }
    }
}

impl<B> Debug for LimitedRepeated<B> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("LimitedRepeated")
            .field("child", &self.child)
            .field("limit", &self.limit)
            .field("completed", &self.completed)
            .finish()
    }
}

impl<B: 'static> BehaviorNode<B> for LimitedRepeated<B> {
    fn tick(mut self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
        let mut completed = self.completed;

        if completed >= self.limit {
            return NodeResult::Success;
        }

        let inner = Arc::make_mut(&mut self);

        if let Some(resume) = inner.resume.take() {
            match resume.tick(blackboard) {
                NodeResult::Running(resume) => {
                    inner.resume = Some(resume);
                    return NodeResult::Running(self);
                }
                _ => {
                    completed += 1;
                }
            }
        }

        match inner.child.clone().tick(blackboard) {
            NodeResult::Running(resume) => {
                inner.resume = Some(resume);
                inner.completed = completed;
                return NodeResult::Running(self);
            }
            _ => {
                completed += 1;
            }
        }

        // Restart until we've completed the repetitions
        if completed >= inner.limit {
            NodeResult::Success
        } else {
            inner.completed = completed;
            NodeResult::Running(self)
        }
    }

    fn status(&self, _parent: ReportStatus, context: &B) -> crate::BehaviorStatus {
        let has_run = self.resume.is_some();
        let status = if has_run {
            ReportStatus::Running
        } else {
            ReportStatus::Unexecuted
        };

        crate::BehaviorStatus {
            name: std::any::type_name_of_val(self).into(),
            status,
            children: vec![self.child.status(status, context)],
            current: if has_run { Some(0) } else { None },
        }
    }
}

/// Repeats its child until its child fails
pub struct RepeatedUntilFailure<B> {
    resume: Option<BehaviorArc<B>>,
    child: BehaviorArc<B>,
}

impl<B> RepeatedUntilFailure<B> {
    pub fn new(child: BehaviorArc<B>) -> Self {
        Self {
            child,
            resume: None,
        }
    }
}

impl<B> Clone for RepeatedUntilFailure<B> {
    fn clone(&self) -> Self {
        Self {
            child: self.child.clone(),
            resume: self.resume.clone(),
        }
    }
}

impl<B> Debug for RepeatedUntilFailure<B> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RepeatedUntilFailure")
            .field("child", &self.child)
            .finish()
    }
}

impl<B: 'static> BehaviorNode<B> for RepeatedUntilFailure<B> {
    fn tick(mut self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
        if self.resume.is_some() {
            let inner = Arc::make_mut(&mut self);
            if let Some(resume) = inner.resume.take() {
                match resume.tick(blackboard) {
                    NodeResult::Running(resume) => {
                        inner.resume = Some(resume);
                        return NodeResult::Running(self);
                    }
                    NodeResult::Failure => return NodeResult::Success,
                    _ => (),
                }
            }
        }

        match self.child.clone().tick(blackboard) {
            NodeResult::Running(resume) => {
                let inner = Arc::make_mut(&mut self);
                inner.resume = Some(resume);
                NodeResult::Running(self)
            }
            NodeResult::Success => {
                // Restart whenever we succeed
                NodeResult::Running(self)
            }
            NodeResult::Failure => {
                // We have encounted a failure, so *succeed*
                NodeResult::Success
            }
        }
    }

    fn status(&self, _parent: ReportStatus, context: &B) -> crate::BehaviorStatus {
        let has_run = self.resume.is_some();
        let status = if has_run {
            ReportStatus::Running
        } else {
            ReportStatus::Unexecuted
        };

        crate::BehaviorStatus {
            name: std::any::type_name_of_val(self).into(),
            status,
            children: vec![self.child.status(status, context)],
            current: if has_run { Some(0) } else { None },
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        composite::{
            tests::{test_with_context, Context},
            Sequence,
        },
        BehaviorRunner,
    };

    use super::*;
    use assert2::check;

    #[derive(Debug)]
    struct Push1;
    impl BehaviorNode<Context> for Push1 {
        fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
            context.stack.push(1);
            NodeResult::Success
        }
    }

    #[derive(Debug)]
    struct FibPush;
    impl BehaviorNode<Context> for FibPush {
        fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
            if context.stack.len() < 2 {
                context.stack.push(1);
            } else {
                let len = context.stack.len();
                let a = context.stack[len - 2];
                let b = context.stack[len - 1];
                context.stack.push(a + b);
            }
            NodeResult::Success
        }
    }

    #[derive(Debug)]
    struct IsCapped {
        cap: i32,
    }
    impl BehaviorNode<Context> for IsCapped {
        fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
            if context.stack.iter().any(|v| *v > self.cap) {
                NodeResult::Failure
            } else {
                NodeResult::Success
            }
        }
    }

    #[test]
    fn limited_repeat_repeats_to_limit() {
        let runner = BehaviorRunner::new(LimitedRepeated::new(3, Push1.arc()).arc());
        let (res, context) = test_with_context(|| Context { stack: Vec::new() }, runner, 3);
        check!(res == Some(true));
        check!(context.stack == vec![1, 1, 1]);
    }

    #[test]
    fn repeat_until_failure_stops_on_failure() {
        let runner = BehaviorRunner::new(
            RepeatedUntilFailure::new(
                vec![FibPush.arc(), IsCapped { cap: 100 }.arc()]
                    .into_iter()
                    .collect::<Sequence<_>>()
                    .arc(),
            )
            .arc(),
        );
        let (res, context) = test_with_context(|| Context { stack: Vec::new() }, runner, 11);
        check!(res == Some(true));
        check!(context.stack == vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]);
    }
}