Skip to main content

bonsai_bt/
sequence.rs

1use crate::status::Status::*;
2use crate::tracer::{first_child_id, next_sibling_id, NodeMeta, Tracer};
3use crate::Float;
4use crate::{event::UpdateEvent, state::State, ActionArgs, Behavior, Status, RUNNING};
5
6pub struct SequenceArgs<'a, A, E, F, B, T> {
7    pub select: bool,
8    pub upd: Option<Float>,
9    pub seq: &'a [Behavior<A>],
10    pub i: &'a mut usize,
11    pub cursor: &'a mut Box<State<A>>,
12    pub e: &'a E,
13    pub blackboard: &'a mut B,
14    pub f: &'a mut F,
15    pub parent_id: usize,
16    pub metas: &'a [NodeMeta],
17    pub tracer: &'a mut T,
18}
19
20// `Sequence` and `Select` share same algorithm.
21//
22// `Sequence` fails if any fails and succeeds when all succeeds.
23// `Select` succeeds if any succeeds and fails when all fails.
24pub fn sequence<A, E, F, B, T>(args: SequenceArgs<A, E, F, B, T>) -> (Status, Float)
25where
26    A: Clone,
27    E: UpdateEvent,
28    F: FnMut(ActionArgs<E, A>, &mut B) -> (Status, Float),
29    T: Tracer,
30{
31    let SequenceArgs {
32        select,
33        upd,
34        seq,
35        i,
36        cursor,
37        e,
38        blackboard,
39        f,
40        parent_id,
41        metas,
42        tracer,
43    } = args;
44
45    let (status, inv_status) = if select {
46        // `Select`
47        (Status::Failure, Status::Success)
48    } else {
49        // `Sequence`
50        (Status::Success, Status::Failure)
51    };
52    let mut child_id = first_child_id::<T>(parent_id);
53    if T::IS_RECORDING {
54        for _ in 0..*i {
55            child_id = next_sibling_id::<T>(metas, child_id);
56        }
57    }
58    let mut remaining_dt = upd.unwrap_or(0.0);
59    let mut remaining_e;
60    while *i < seq.len() {
61        match cursor.tick(
62            child_id,
63            metas,
64            match upd {
65                Some(_) => {
66                    remaining_e = UpdateEvent::from_dt(remaining_dt, e).unwrap();
67                    &remaining_e
68                }
69                _ => e,
70            },
71            blackboard,
72            f,
73            tracer,
74        ) {
75            (Running, _) => {
76                break;
77            }
78            (s, new_dt) if s == inv_status => {
79                return (inv_status, new_dt);
80            }
81            (s, new_dt) if s == status => {
82                remaining_dt = match upd {
83                    // Change update event with remaining delta time.
84                    Some(_) => new_dt,
85                    // Other events are 'consumed' and not passed to next.
86                    // If this is the last event, then the sequence succeeded.
87                    _ => {
88                        if *i == seq.len() - 1 {
89                            return (status, new_dt);
90                        } else {
91                            *i += 1;
92                            // Create a new cursor for next event.
93                            // Use the same pointer to avoid allocation.
94                            **cursor = State::new(seq[*i].clone());
95                            return RUNNING;
96                        }
97                    }
98                }
99            }
100            _ => unreachable!(),
101        };
102        *i += 1;
103        if T::IS_RECORDING {
104            child_id = next_sibling_id::<T>(metas, child_id);
105        }
106        // If end of sequence,
107        // return the 'dt' that is left.
108        if *i >= seq.len() {
109            return (status, remaining_dt);
110        }
111        // Create a new cursor for next event.
112        // Use the same pointer to avoid allocation.
113        **cursor = State::new(seq[*i].clone());
114    }
115    RUNNING
116}
117
118pub struct MemorylessSequenceArgs<'a, A, E, F, B, T> {
119    pub select: bool,
120    pub upd: Option<Float>,
121    pub seq: &'a [Behavior<A>],
122    pub cursor: &'a mut Box<State<A>>,
123    pub e: &'a E,
124    pub blackboard: &'a mut B,
125    pub f: &'a mut F,
126    pub parent_id: usize,
127    pub metas: &'a [NodeMeta],
128    pub tracer: &'a mut T,
129}
130
131/// Shared driver for memoryless `Sequence` and `Select` (`memory = false`).
132///
133/// Walks `seq` from index 0 each call, overwriting `*cursor` before each child
134/// so the previous tick's running state is discarded.
135///
136/// `select` flips the short-circuit polarity:
137/// - `false` → Sequence: `Failure` short-circuits; all-`Success` → `Success`.
138/// - `true`  → Select:   `Success` short-circuits; all-`Failure` → `Failure`.
139///
140/// Reuses the caller's cursor `Box`; the only per-child allocation is
141/// `child.clone()`, which is free for `Copy` action types.
142#[inline]
143pub fn memoryless_sequence<A, E, F, B, T>(args: MemorylessSequenceArgs<A, E, F, B, T>) -> (Status, Float)
144where
145    A: Clone,
146    E: UpdateEvent,
147    F: FnMut(ActionArgs<E, A>, &mut B) -> (Status, Float),
148    T: Tracer,
149{
150    let MemorylessSequenceArgs {
151        select,
152        upd,
153        seq,
154        cursor,
155        e,
156        blackboard,
157        f,
158        parent_id,
159        metas,
160        tracer,
161    } = args;
162
163    let initial_dt = upd.unwrap_or(0.0);
164
165    if seq.is_empty() {
166        return if select {
167            (Status::Failure, initial_dt)
168        } else {
169            (Status::Success, initial_dt)
170        };
171    }
172
173    let (terminal_status, short_circuit_status) = if select {
174        (Status::Failure, Status::Success)
175    } else {
176        (Status::Success, Status::Failure)
177    };
178
179    let mut child_id = first_child_id::<T>(parent_id);
180    let mut remaining_dt = initial_dt;
181    let mut remaining_e;
182
183    for child in seq {
184        // Reset in place: reuses the Box, no new allocation.
185        **cursor = State::new(child.clone());
186
187        let ev = match upd {
188            Some(_) => {
189                remaining_e = UpdateEvent::from_dt(remaining_dt, e).unwrap();
190                &remaining_e
191            }
192            None => e,
193        };
194
195        match cursor.tick(child_id, metas, ev, blackboard, f, tracer) {
196            (Running, _) => return RUNNING,
197            (s, dt) if s == short_circuit_status => return (s, dt),
198            (s, dt) if s == terminal_status => {
199                if upd.is_some() {
200                    remaining_dt = dt;
201                }
202            }
203            _ => unreachable!(),
204        }
205
206        if T::IS_RECORDING {
207            child_id = next_sibling_id::<T>(metas, child_id);
208        }
209    }
210
211    (terminal_status, remaining_dt)
212}