Skip to main content

bonsai_bt/
when_all.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, Status, RUNNING};
5
6pub struct WhenAllArgs<'a, A, E, F, B, T> {
7    pub any: bool,
8    pub upd: Option<Float>,
9    pub cursors: &'a mut [Option<State<A>>],
10    pub e: &'a E,
11    pub blackboard: &'a mut B,
12    pub f: &'a mut F,
13    pub parent_id: usize,
14    pub metas: &'a [NodeMeta],
15    pub tracer: &'a mut T,
16}
17
18// `WhenAll` and `WhenAny` share same algorithm.
19//
20// `WhenAll` fails if any fails and succeeds when all succeeds.
21// `WhenAny` succeeds if any succeeds and fails when all fails.
22pub fn when_all<A, E, F, B, T>(args: WhenAllArgs<A, E, F, B, T>) -> (Status, Float)
23where
24    A: Clone,
25    E: UpdateEvent,
26    F: FnMut(ActionArgs<E, A>, &mut B) -> (Status, Float),
27    T: Tracer,
28{
29    let WhenAllArgs {
30        any,
31        upd,
32        cursors,
33        e,
34        blackboard,
35        f,
36        parent_id,
37        metas,
38        tracer,
39    } = args;
40    let (status, inv_status) = if any {
41        // `WhenAny`
42        (Status::Failure, Status::Success)
43    } else {
44        // `WhenAll`
45        (Status::Success, Status::Failure)
46    };
47    // Get the least delta time left over.
48    let mut min_dt = Float::MAX;
49    // Count number of terminated events.
50    let mut terminated = 0;
51    let mut child_id = first_child_id::<T>(parent_id);
52    for cur in cursors.iter_mut() {
53        let this_id = child_id;
54        child_id = next_sibling_id::<T>(metas, this_id);
55        match *cur {
56            None => {}
57            Some(ref mut cur) => {
58                match cur.tick(this_id, metas, e, blackboard, f, tracer) {
59                    (Running, _) => {
60                        continue;
61                    }
62                    (s, new_dt) if s == inv_status => {
63                        // Fail for `WhenAll`.
64                        // Succeed for `WhenAny`.
65                        return (inv_status, new_dt);
66                    }
67                    (s, new_dt) if s == status => {
68                        min_dt = min_dt.min(new_dt);
69                    }
70                    _ => unreachable!(),
71                }
72            }
73        }
74
75        terminated += 1;
76        *cur = None;
77    }
78    #[allow(clippy::manual_unwrap_or)]
79    match terminated {
80        // If there are no events, there is a whole 'dt' left.
81        0 if cursors.is_empty() => (
82            status,
83            match upd {
84                Some(dt) => dt,
85                // Other kind of events happen instantly.
86                _ => 0.0,
87            },
88        ),
89        // If all events terminated, the least delta time is left.
90        n if cursors.len() == n => (status, min_dt),
91        _ => RUNNING,
92    }
93}