Skip to main content

mecha10_behavior_runtime/
composition.rs

1//! Composition primitives for building behavior trees
2//!
3//! This module provides the fundamental composition nodes: Sequence, Selector, and Parallel.
4
5use crate::{BehaviorNode, BoxedBehavior, NodeStatus};
6use async_trait::async_trait;
7use mecha10_core::Context;
8use tracing::{debug, warn};
9
10// ============================================================================
11// Sequence Node
12// ============================================================================
13
14/// Executes child behaviors in sequence until one fails or all succeed.
15///
16/// **Behavior:**
17/// - Ticks children in order
18/// - Returns `Success` if all children succeed
19/// - Returns `Failure` if any child fails
20/// - Returns `Running` while executing children
21///
22/// **Use cases:**
23/// - Multi-step tasks that must complete in order
24/// - Pipeline processing
25/// - Sequential actions (approach → grasp → lift)
26///
27/// # Example
28///
29/// ```rust,no_run
30/// use mecha10_behavior_runtime::prelude::*;
31///
32/// // Assuming these behaviors are defined elsewhere
33/// # use mecha10_behavior_runtime::BoxedBehavior;
34/// # fn get_behaviors() -> Vec<BoxedBehavior> { vec![] }
35/// let sequence = SequenceNode::new(get_behaviors());
36/// ```
37#[derive(Debug)]
38pub struct SequenceNode {
39    children: Vec<BoxedBehavior>,
40    current_index: usize,
41}
42
43impl SequenceNode {
44    /// Create a new sequence node with the given children.
45    pub fn new(children: Vec<BoxedBehavior>) -> Self {
46        Self {
47            children,
48            current_index: 0,
49        }
50    }
51
52    /// Add a child behavior to the sequence.
53    pub fn add_child(&mut self, child: BoxedBehavior) {
54        self.children.push(child);
55    }
56
57    /// Get the number of children.
58    pub fn len(&self) -> usize {
59        self.children.len()
60    }
61
62    /// Check if the sequence is empty.
63    pub fn is_empty(&self) -> bool {
64        self.children.is_empty()
65    }
66}
67
68#[async_trait]
69impl BehaviorNode for SequenceNode {
70    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
71        // Empty sequence succeeds immediately
72        if self.children.is_empty() {
73            return Ok(NodeStatus::Success);
74        }
75
76        // Execute children in sequence
77        let total_children = self.children.len();
78        while self.current_index < total_children {
79            debug!(
80                "Sequence: ticking child {} of {}",
81                self.current_index + 1,
82                total_children
83            );
84
85            let child = &mut self.children[self.current_index];
86            match child.tick(ctx).await? {
87                NodeStatus::Success => {
88                    // Move to next child
89                    self.current_index += 1;
90                }
91                NodeStatus::Failure => {
92                    // Sequence fails if any child fails
93                    debug!("Sequence: child {} failed", self.current_index + 1);
94                    return Ok(NodeStatus::Failure);
95                }
96                NodeStatus::Running => {
97                    // Child still running, sequence continues next tick
98                    return Ok(NodeStatus::Running);
99                }
100            }
101        }
102
103        // All children succeeded
104        debug!("Sequence: all children succeeded");
105        Ok(NodeStatus::Success)
106    }
107
108    async fn reset(&mut self) -> anyhow::Result<()> {
109        self.current_index = 0;
110        for child in &mut self.children {
111            child.reset().await?;
112        }
113        Ok(())
114    }
115
116    fn name(&self) -> &str {
117        "sequence"
118    }
119
120    fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
121        self.children
122            .iter_mut()
123            .map(|c| c.as_mut() as &mut dyn BehaviorNode)
124            .collect()
125    }
126}
127
128// ============================================================================
129// Selector Node (Also called Fallback or Priority)
130// ============================================================================
131
132/// Executes child behaviors until one succeeds or all fail.
133///
134/// **Behavior:**
135/// - Ticks children in order
136/// - Returns `Success` if any child succeeds
137/// - Returns `Failure` if all children fail
138/// - Returns `Running` while executing children
139///
140/// **Use cases:**
141/// - Fallback behaviors (try A, if it fails try B)
142/// - Alternative strategies
143/// - Error recovery chains
144///
145/// # Example
146///
147/// ```rust,no_run
148/// use mecha10_behavior_runtime::prelude::*;
149///
150/// // Assuming these behaviors are defined elsewhere
151/// # use mecha10_behavior_runtime::BoxedBehavior;
152/// # fn get_behaviors() -> Vec<BoxedBehavior> { vec![] }
153/// let selector = SelectOrNode::new(get_behaviors());
154/// ```
155#[derive(Debug)]
156pub struct SelectOrNode {
157    children: Vec<BoxedBehavior>,
158    current_index: usize,
159}
160
161impl SelectOrNode {
162    /// Create a new selector node with the given children.
163    pub fn new(children: Vec<BoxedBehavior>) -> Self {
164        Self {
165            children,
166            current_index: 0,
167        }
168    }
169
170    /// Add a child behavior to the selector.
171    pub fn add_child(&mut self, child: BoxedBehavior) {
172        self.children.push(child);
173    }
174
175    /// Get the number of children.
176    pub fn len(&self) -> usize {
177        self.children.len()
178    }
179
180    /// Check if the selector is empty.
181    pub fn is_empty(&self) -> bool {
182        self.children.is_empty()
183    }
184}
185
186#[async_trait]
187impl BehaviorNode for SelectOrNode {
188    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
189        // Empty selector fails immediately
190        if self.children.is_empty() {
191            return Ok(NodeStatus::Failure);
192        }
193
194        // Try children in order until one succeeds
195        let total_children = self.children.len();
196        while self.current_index < total_children {
197            debug!(
198                "Selector: ticking child {} of {}",
199                self.current_index + 1,
200                total_children
201            );
202
203            let child = &mut self.children[self.current_index];
204            match child.tick(ctx).await? {
205                NodeStatus::Success => {
206                    // Selector succeeds if any child succeeds
207                    debug!("Selector: child {} succeeded", self.current_index + 1);
208                    return Ok(NodeStatus::Success);
209                }
210                NodeStatus::Failure => {
211                    // Try next child
212                    warn!("Selector: child {} failed, trying next", self.current_index + 1);
213                    self.current_index += 1;
214                }
215                NodeStatus::Running => {
216                    // Child still running, selector continues next tick
217                    return Ok(NodeStatus::Running);
218                }
219            }
220        }
221
222        // All children failed
223        debug!("Selector: all children failed");
224        Ok(NodeStatus::Failure)
225    }
226
227    async fn reset(&mut self) -> anyhow::Result<()> {
228        self.current_index = 0;
229        for child in &mut self.children {
230            child.reset().await?;
231        }
232        Ok(())
233    }
234
235    fn name(&self) -> &str {
236        "selector"
237    }
238
239    fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
240        self.children
241            .iter_mut()
242            .map(|c| c.as_mut() as &mut dyn BehaviorNode)
243            .collect()
244    }
245}
246
247// ============================================================================
248// Parallel Node
249// ============================================================================
250
251/// Policy for determining when a parallel node succeeds or fails.
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum ParallelPolicy {
254    /// Succeed when all children succeed, fail if any child fails.
255    RequireAll,
256
257    /// Succeed when any child succeeds, fail when all children fail.
258    RequireOne,
259
260    /// Succeed when at least N children succeed.
261    RequireN(usize),
262}
263
264/// Executes all child behaviors concurrently (in parallel).
265///
266/// **Behavior:**
267/// - Ticks all children every tick
268/// - Success/failure depends on the configured policy
269/// - Returns `Running` while any child is still running
270///
271/// **Use cases:**
272/// - Concurrent actions (move + look around + monitor obstacles)
273/// - Parallel sensor processing
274/// - Multi-objective optimization
275///
276/// # Example
277///
278/// ```rust,no_run
279/// use mecha10_behavior_runtime::prelude::*;
280///
281/// // Assuming these behaviors are defined elsewhere
282/// # use mecha10_behavior_runtime::BoxedBehavior;
283/// # fn get_behaviors() -> Vec<BoxedBehavior> { vec![] }
284/// // All must succeed
285/// let parallel = ParallelNode::new(
286///     get_behaviors(),
287///     ParallelPolicy::RequireAll,
288/// );
289/// ```
290#[derive(Debug)]
291pub struct ParallelNode {
292    children: Vec<BoxedBehavior>,
293    policy: ParallelPolicy,
294    child_statuses: Vec<NodeStatus>,
295}
296
297impl ParallelNode {
298    /// Create a new parallel node with the given children and policy.
299    pub fn new(children: Vec<BoxedBehavior>, policy: ParallelPolicy) -> Self {
300        let child_statuses = vec![NodeStatus::Running; children.len()];
301        Self {
302            children,
303            policy,
304            child_statuses,
305        }
306    }
307
308    /// Add a child behavior to the parallel node.
309    pub fn add_child(&mut self, child: BoxedBehavior) {
310        self.children.push(child);
311        self.child_statuses.push(NodeStatus::Running);
312    }
313
314    /// Get the number of children.
315    pub fn len(&self) -> usize {
316        self.children.len()
317    }
318
319    /// Check if the parallel node is empty.
320    pub fn is_empty(&self) -> bool {
321        self.children.is_empty()
322    }
323}
324
325#[async_trait]
326impl BehaviorNode for ParallelNode {
327    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
328        // Empty parallel succeeds immediately
329        if self.children.is_empty() {
330            return Ok(NodeStatus::Success);
331        }
332
333        // Tick all children that are still running
334        for (i, child) in self.children.iter_mut().enumerate() {
335            if self.child_statuses[i].is_running() {
336                let status = child.tick(ctx).await?;
337                self.child_statuses[i] = status;
338                debug!("Parallel: child {} status: {}", i + 1, status);
339            }
340        }
341
342        // Check policy to determine overall status
343        let success_count = self.child_statuses.iter().filter(|s| s.is_success()).count();
344        let failure_count = self.child_statuses.iter().filter(|s| s.is_failure()).count();
345        let _running_count = self.child_statuses.iter().filter(|s| s.is_running()).count();
346
347        match self.policy {
348            ParallelPolicy::RequireAll => {
349                if failure_count > 0 {
350                    debug!("Parallel (RequireAll): at least one child failed");
351                    Ok(NodeStatus::Failure)
352                } else if success_count == self.children.len() {
353                    debug!("Parallel (RequireAll): all children succeeded");
354                    Ok(NodeStatus::Success)
355                } else {
356                    Ok(NodeStatus::Running)
357                }
358            }
359            ParallelPolicy::RequireOne => {
360                if success_count > 0 {
361                    debug!("Parallel (RequireOne): at least one child succeeded");
362                    Ok(NodeStatus::Success)
363                } else if failure_count == self.children.len() {
364                    debug!("Parallel (RequireOne): all children failed");
365                    Ok(NodeStatus::Failure)
366                } else {
367                    Ok(NodeStatus::Running)
368                }
369            }
370            ParallelPolicy::RequireN(n) => {
371                if success_count >= n {
372                    debug!("Parallel (RequireN({})): {} children succeeded", n, success_count);
373                    Ok(NodeStatus::Success)
374                } else if (self.children.len() - failure_count) < n {
375                    // Not enough children can possibly succeed
376                    debug!("Parallel (RequireN({})): not enough children can succeed", n);
377                    Ok(NodeStatus::Failure)
378                } else {
379                    Ok(NodeStatus::Running)
380                }
381            }
382        }
383    }
384
385    async fn reset(&mut self) -> anyhow::Result<()> {
386        for i in 0..self.children.len() {
387            self.child_statuses[i] = NodeStatus::Running;
388            self.children[i].reset().await?;
389        }
390        Ok(())
391    }
392
393    fn name(&self) -> &str {
394        "parallel"
395    }
396
397    fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
398        self.children
399            .iter_mut()
400            .map(|c| c.as_mut() as &mut dyn BehaviorNode)
401            .collect()
402    }
403}