simple_bt 1.0.1

minimal(-ish) behavior tree implementation
Documentation
//! Create a simple behavior tree implementation

pub mod composite;

use std::sync::Arc;

#[derive(Debug)]
pub enum NodeResult<B> {
    /// The node is still running
    ///
    /// This contains the node to be ticked
    Running(BehaviorArc<B>),
    /// The node succeeded
    Success,
    /// The node failed
    Failure,
}

pub type BehaviorArc<B> = Arc<dyn BehaviorNode<B>>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ReportStatus {
    #[default]
    Unexecuted,
    Running,
    Success,
    Failure,
}

impl ReportStatus {
    /// If self is Unexecuted, return Unexecuted.
    /// Otherwise, return the other status.
    pub fn unexecuted_or(self, other: ReportStatus) -> ReportStatus {
        if self == ReportStatus::Unexecuted {
            ReportStatus::Unexecuted
        } else {
            other
        }
    }

    /// If self is Unexecuted, return Unexecuted.
    /// Otherwise, call the function and return the evaluated status.
    pub fn unexecuted_or_else(self, other: impl FnOnce() -> ReportStatus) -> ReportStatus {
        if self == ReportStatus::Unexecuted {
            ReportStatus::Unexecuted
        } else {
            (other)()
        }
    }
}

/// Simple way to report current status
#[derive(Debug)]
pub struct BehaviorStatus {
    pub name: Box<str>,
    pub status: ReportStatus,
    pub children: Vec<BehaviorStatus>,
    /// If not `None`, this behavior is currently processing the given child (from the child statuses)
    pub current: Option<usize>,
}

// This is our main "behavior tree" trait.
// all nodes implement this trait.

pub trait BehaviorNode<B>: std::fmt::Debug + Send + Sync {
    fn tick(self: Arc<Self>, context: &mut B) -> NodeResult<B>;

    fn arc(self) -> BehaviorArc<B>
    where
        Self: Sized + Send + Sync + 'static,
    {
        Arc::new(self)
    }

    fn status(&self, parent: ReportStatus, context: &B) -> BehaviorStatus {
        let _ = context;
        BehaviorStatus {
            name: std::any::type_name_of_val(self).into(),
            status: parent,
            children: vec![],
            current: None,
        }
    }
}

#[derive(Debug)]
/// Takes care of executing a behavior tree
pub struct BehaviorRunner<B> {
    tree: BehaviorArc<B>,
    current_tick: Option<BehaviorArc<B>>,
}

impl<B> BehaviorRunner<B> {
    pub fn new(tree: BehaviorArc<B>) -> Self {
        Self {
            tree,
            current_tick: None,
        }
    }

    pub fn from_node<N>(node: N) -> Self
    where
        N: BehaviorNode<B> + 'static,
    {
        Self {
            tree: Arc::new(node),
            current_tick: None,
        }
    }

    pub fn into_inner(self) -> BehaviorArc<B> {
        self.current_tick.unwrap_or(self.tree)
    }

    pub fn is_running(&self) -> bool {
        self.current_tick.is_some()
    }

    /// Reset to its initial state
    pub fn reset(&mut self) {
        self.current_tick.take();
    }

    /// Forward status call to underlying tree
    pub fn status(&self, context: &B) -> BehaviorStatus {
        if let Some(bp) = self.current_tick.as_ref() {
            bp.status(ReportStatus::Running, context)
        } else {
            self.tree.status(ReportStatus::Unexecuted, context)
        }
    }

    fn tick_node(&mut self, node: Arc<dyn BehaviorNode<B>>, context: &mut B) -> Option<bool> {
        match node.tick(context) {
            NodeResult::Running(nbp) => {
                self.current_tick = Some(nbp);
                None
            }
            NodeResult::Success => Some(true),
            NodeResult::Failure => Some(false),
        }
    }

    // returns None -> still running
    // return Some(p) -> p true success, p false failure
    pub fn proceed(&mut self, context: &mut B) -> Option<bool> {
        if let Some(bp) = self.current_tick.take() {
            self.tick_node(bp, context)
        } else {
            let node = self.tree.clone();
            self.tick_node(node, context)
        }
    }
}