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    async fn on_init(&mut self, ctx: &Context) -> anyhow::Result<()> {
117        for child in &mut self.children {
118            child.on_init(ctx).await?;
119        }
120        Ok(())
121    }
122
123    async fn on_terminate(&mut self, ctx: &Context) -> anyhow::Result<()> {
124        for child in &mut self.children {
125            child.on_terminate(ctx).await?;
126        }
127        Ok(())
128    }
129
130    fn name(&self) -> &str {
131        "sequence"
132    }
133}
134
135// ============================================================================
136// Selector Node (Also called Fallback or Priority)
137// ============================================================================
138
139/// Executes child behaviors until one succeeds or all fail.
140///
141/// **Behavior:**
142/// - Ticks children in order
143/// - Returns `Success` if any child succeeds
144/// - Returns `Failure` if all children fail
145/// - Returns `Running` while executing children
146///
147/// **Use cases:**
148/// - Fallback behaviors (try A, if it fails try B)
149/// - Alternative strategies
150/// - Error recovery chains
151///
152/// # Example
153///
154/// ```rust,no_run
155/// use mecha10_behavior_runtime::prelude::*;
156///
157/// // Assuming these behaviors are defined elsewhere
158/// # use mecha10_behavior_runtime::BoxedBehavior;
159/// # fn get_behaviors() -> Vec<BoxedBehavior> { vec![] }
160/// let selector = SelectOrNode::new(get_behaviors());
161/// ```
162#[derive(Debug)]
163pub struct SelectOrNode {
164    children: Vec<BoxedBehavior>,
165    current_index: usize,
166}
167
168impl SelectOrNode {
169    /// Create a new selector node with the given children.
170    pub fn new(children: Vec<BoxedBehavior>) -> Self {
171        Self {
172            children,
173            current_index: 0,
174        }
175    }
176
177    /// Add a child behavior to the selector.
178    pub fn add_child(&mut self, child: BoxedBehavior) {
179        self.children.push(child);
180    }
181
182    /// Get the number of children.
183    pub fn len(&self) -> usize {
184        self.children.len()
185    }
186
187    /// Check if the selector is empty.
188    pub fn is_empty(&self) -> bool {
189        self.children.is_empty()
190    }
191}
192
193#[async_trait]
194impl BehaviorNode for SelectOrNode {
195    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
196        // Empty selector fails immediately
197        if self.children.is_empty() {
198            return Ok(NodeStatus::Failure);
199        }
200
201        // Try children in order until one succeeds
202        let total_children = self.children.len();
203        while self.current_index < total_children {
204            debug!(
205                "Selector: ticking child {} of {}",
206                self.current_index + 1,
207                total_children
208            );
209
210            let child = &mut self.children[self.current_index];
211            match child.tick(ctx).await? {
212                NodeStatus::Success => {
213                    // Selector succeeds if any child succeeds
214                    debug!("Selector: child {} succeeded", self.current_index + 1);
215                    return Ok(NodeStatus::Success);
216                }
217                NodeStatus::Failure => {
218                    // Try next child
219                    warn!("Selector: child {} failed, trying next", self.current_index + 1);
220                    self.current_index += 1;
221                }
222                NodeStatus::Running => {
223                    // Child still running, selector continues next tick
224                    return Ok(NodeStatus::Running);
225                }
226            }
227        }
228
229        // All children failed
230        debug!("Selector: all children failed");
231        Ok(NodeStatus::Failure)
232    }
233
234    async fn reset(&mut self) -> anyhow::Result<()> {
235        self.current_index = 0;
236        for child in &mut self.children {
237            child.reset().await?;
238        }
239        Ok(())
240    }
241
242    async fn on_init(&mut self, ctx: &Context) -> anyhow::Result<()> {
243        for child in &mut self.children {
244            child.on_init(ctx).await?;
245        }
246        Ok(())
247    }
248
249    async fn on_terminate(&mut self, ctx: &Context) -> anyhow::Result<()> {
250        for child in &mut self.children {
251            child.on_terminate(ctx).await?;
252        }
253        Ok(())
254    }
255
256    fn name(&self) -> &str {
257        "selector"
258    }
259}
260
261// ============================================================================
262// Parallel Node
263// ============================================================================
264
265/// Policy for determining when a parallel node succeeds or fails.
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum ParallelPolicy {
268    /// Succeed when all children succeed, fail if any child fails.
269    RequireAll,
270
271    /// Succeed when any child succeeds, fail when all children fail.
272    RequireOne,
273
274    /// Succeed when at least N children succeed.
275    RequireN(usize),
276}
277
278/// Executes all child behaviors concurrently (in parallel).
279///
280/// **Behavior:**
281/// - Ticks all children every tick
282/// - Success/failure depends on the configured policy
283/// - Returns `Running` while any child is still running
284///
285/// **Use cases:**
286/// - Concurrent actions (move + look around + monitor obstacles)
287/// - Parallel sensor processing
288/// - Multi-objective optimization
289///
290/// # Example
291///
292/// ```rust,no_run
293/// use mecha10_behavior_runtime::prelude::*;
294///
295/// // Assuming these behaviors are defined elsewhere
296/// # use mecha10_behavior_runtime::BoxedBehavior;
297/// # fn get_behaviors() -> Vec<BoxedBehavior> { vec![] }
298/// // All must succeed
299/// let parallel = ParallelNode::new(
300///     get_behaviors(),
301///     ParallelPolicy::RequireAll,
302/// );
303/// ```
304#[derive(Debug)]
305pub struct ParallelNode {
306    children: Vec<BoxedBehavior>,
307    policy: ParallelPolicy,
308    child_statuses: Vec<NodeStatus>,
309}
310
311impl ParallelNode {
312    /// Create a new parallel node with the given children and policy.
313    pub fn new(children: Vec<BoxedBehavior>, policy: ParallelPolicy) -> Self {
314        let child_statuses = vec![NodeStatus::Running; children.len()];
315        Self {
316            children,
317            policy,
318            child_statuses,
319        }
320    }
321
322    /// Add a child behavior to the parallel node.
323    pub fn add_child(&mut self, child: BoxedBehavior) {
324        self.children.push(child);
325        self.child_statuses.push(NodeStatus::Running);
326    }
327
328    /// Get the number of children.
329    pub fn len(&self) -> usize {
330        self.children.len()
331    }
332
333    /// Check if the parallel node is empty.
334    pub fn is_empty(&self) -> bool {
335        self.children.is_empty()
336    }
337}
338
339#[async_trait]
340impl BehaviorNode for ParallelNode {
341    async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
342        // Empty parallel succeeds immediately
343        if self.children.is_empty() {
344            return Ok(NodeStatus::Success);
345        }
346
347        // Tick all children that are still running
348        for (i, child) in self.children.iter_mut().enumerate() {
349            if self.child_statuses[i].is_running() {
350                let status = child.tick(ctx).await?;
351                self.child_statuses[i] = status;
352                debug!("Parallel: child {} status: {}", i + 1, status);
353            }
354        }
355
356        // Check policy to determine overall status
357        let success_count = self.child_statuses.iter().filter(|s| s.is_success()).count();
358        let failure_count = self.child_statuses.iter().filter(|s| s.is_failure()).count();
359        let _running_count = self.child_statuses.iter().filter(|s| s.is_running()).count();
360
361        match self.policy {
362            ParallelPolicy::RequireAll => {
363                if failure_count > 0 {
364                    debug!("Parallel (RequireAll): at least one child failed");
365                    Ok(NodeStatus::Failure)
366                } else if success_count == self.children.len() {
367                    debug!("Parallel (RequireAll): all children succeeded");
368                    Ok(NodeStatus::Success)
369                } else {
370                    Ok(NodeStatus::Running)
371                }
372            }
373            ParallelPolicy::RequireOne => {
374                if success_count > 0 {
375                    debug!("Parallel (RequireOne): at least one child succeeded");
376                    Ok(NodeStatus::Success)
377                } else if failure_count == self.children.len() {
378                    debug!("Parallel (RequireOne): all children failed");
379                    Ok(NodeStatus::Failure)
380                } else {
381                    Ok(NodeStatus::Running)
382                }
383            }
384            ParallelPolicy::RequireN(n) => {
385                if success_count >= n {
386                    debug!("Parallel (RequireN({})): {} children succeeded", n, success_count);
387                    Ok(NodeStatus::Success)
388                } else if (self.children.len() - failure_count) < n {
389                    // Not enough children can possibly succeed
390                    debug!("Parallel (RequireN({})): not enough children can succeed", n);
391                    Ok(NodeStatus::Failure)
392                } else {
393                    Ok(NodeStatus::Running)
394                }
395            }
396        }
397    }
398
399    async fn reset(&mut self) -> anyhow::Result<()> {
400        for i in 0..self.children.len() {
401            self.child_statuses[i] = NodeStatus::Running;
402            self.children[i].reset().await?;
403        }
404        Ok(())
405    }
406
407    async fn on_init(&mut self, ctx: &Context) -> anyhow::Result<()> {
408        for child in &mut self.children {
409            child.on_init(ctx).await?;
410        }
411        Ok(())
412    }
413
414    async fn on_terminate(&mut self, ctx: &Context) -> anyhow::Result<()> {
415        for child in &mut self.children {
416            child.on_terminate(ctx).await?;
417        }
418        Ok(())
419    }
420
421    fn name(&self) -> &str {
422        "parallel"
423    }
424}