Skip to content

Commit

Permalink
refactor adding WhileAll behavior (#36)
Browse files Browse the repository at this point in the history
**In this MR**

* Rename behavior  `RepeatSequence` to `WhileAll`
* Improve readme slightly for While and WhileAll behavior
* Bump rust version
* Bump lib version
  • Loading branch information
Sollimann authored Oct 8, 2024
1 parent 9fb29e6 commit fbe0776
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 43 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

.idea/
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<!-- [![version](https://img.shields.io/badge/version-1.0.0-blue)](https://GitHub.com/Sollimann/CleanIt/releases/) -->
[![Build Status](https://github.com/Sollimann/bonsai/actions/workflows/rust-ci.yml/badge.svg)](https://github.com/Sollimann/bonsai/actions)
[![Bonsai crate](https://img.shields.io/crates/v/bonsai-bt.svg)](https://crates.io/crates/bonsai-bt)
[![minimum rustc 1.56](https://img.shields.io/badge/rustc-1.56+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![minimum rustc 1.72](https://img.shields.io/badge/rustc-1.56+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![Docs](https://docs.rs/bonsai-bt/badge.svg)](https://docs.rs/bonsai-bt)
[![codecov](https://codecov.io/gh/Sollimann/bonsai/branch/main/graph/badge.svg?token=JX8JBPWORV)](https://codecov.io/gh/Sollimann/bonsai)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Sollimann/bonsai/graphs/commit-activity)
Expand Down Expand Up @@ -47,12 +47,12 @@ A Behavior Tree forms a tree structure where each node represents a process. Whe
For example, if you have a state `A` and a state `B`:

- Move from state `A` to state `B` if `A` succeeds: `Sequence([A, B])`
- Move from state `A` to sequence of states `[B]` if `A` is running. If all states in the sequence `[B]` succeed in order, check if `A` is still running and repeat. Stop if `A` succeeds or any of the states fail: `RepeatSequence(A, [B])`
- Try `A` first and then try `B` if `A` fails: `Select([A, B])`
- If `condition` succeedes do `A`, else do `B` : `If(condition, A, B)`
- If `A` succeeds, return failure (and vice-versa): `Invert(A)`
- Do `B` repeatedly while `A` runs: `While(A, [B])`
- Do `A`, `B` repeatedly while `LoopCondition` runs: `While(LoopCondition, [A, B])`. Checks condition node between nodes `A`, `B`.
- Do `A`, `B` forever: `While(WaitForever, [A, B])`
- Do `A`, `B` repeatedly while `LoopCondition` runs: `WhileAll(LoopCondition, [A, B])`. After *All* nodes `A`, `B` are completed successfully, check the condition node.
- Run `A` and `B` in parallell and wait for both to succeed: `WhenAll([A, B])`
- Run `A` and `B` in parallell and wait for any to succeed: `WhenAny([A, B])`
- Run `A` and `B` in parallell, but `A` has to succeed before `B`: `After([A, B])`
Expand Down
4 changes: 2 additions & 2 deletions bonsai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ license = "MIT"
name = "bonsai-bt"
readme = "../README.md"
repository = "https://github.com/sollimann/bonsai.git"
rust-version = "1.60.0"
version = "0.6.3"
rust-version = "1.80.0"
version = "0.7.0"

[lib]
name = "bonsai_bt"
Expand Down
11 changes: 8 additions & 3 deletions bonsai/src/behavior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ pub enum Behavior<A> {
/// Succeeds if the conditional behavior succeeds.
/// Fails if the conditional behavior fails,
/// or if any behavior in the loop body fails.
///
/// # Panics
///
/// Panics if the given behavior sequence is empty.
While(Box<Behavior<A>>, Vec<Behavior<A>>),

/// Runs a sequence on repeat as long as a conditional behavior
/// that precedes the sequence is running.
///
/// Conditional behavior is **only** checked before the sequence runs and
/// not during the sequence.
///
Expand All @@ -61,14 +66,14 @@ pub enum Behavior<A> {
///
/// ```
///
///use bonsai_bt::{BT, Running, Failure, Success, Action, UpdateArgs, Behavior::RepeatSequence, ActionArgs};
///use bonsai_bt::{BT, Running, Failure, Success, Action, UpdateArgs, Behavior::WhileAll, ActionArgs};
///use bonsai_bt::Event;
///
///#[derive(Clone, Debug)]
///
///enum Ex { A, B, C }
///
///let rs = RepeatSequence(
///let rs = WhileAll(
/// Box::new(Action(Ex::A)),
/// vec![Action(Ex::B), Action(Ex::C)],
///);
Expand Down Expand Up @@ -101,7 +106,7 @@ pub enum Behavior<A> {
///});
///assert!(i == 4);
/// ```
RepeatSequence(Box<Behavior<A>>, Vec<Behavior<A>>),
WhileAll(Box<Behavior<A>>, Vec<Behavior<A>>),
/// Runs all behaviors in parallel until all succeeded.
///
/// Succeeds if all behaviors succeed.
Expand Down
1 change: 1 addition & 0 deletions bonsai/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@

pub use behavior::Behavior::{
self, Action, After, AlwaysSucceed, If, Invert, Select, Sequence, Wait, WaitForever, WhenAll, WhenAny, While,
WhileAll,
};

pub use bt::BT;
Expand Down
12 changes: 6 additions & 6 deletions bonsai/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ pub enum State<A> {
SequenceState(Vec<Behavior<A>>, usize, Box<State<A>>),
/// Keeps track of a `While` behavior.
WhileState(Box<State<A>>, Vec<Behavior<A>>, usize, Box<State<A>>),
/// Keeps track of a `RepeatSequence` behavior.
RepeatSequenceState(Box<State<A>>, Vec<Behavior<A>>, usize, bool, Box<State<A>>),
/// Keeps track of a `WhileAll` behavior.
WhileAllState(Box<State<A>>, Vec<Behavior<A>>, usize, bool, Box<State<A>>),
/// Keeps track of a `WhenAll` behavior.
WhenAllState(Vec<Option<State<A>>>),
/// Keeps track of a `WhenAny` behavior.
Expand Down Expand Up @@ -97,13 +97,13 @@ impl<A: Clone> State<A> {
Behavior::WhenAll(all) => State::WhenAllState(all.into_iter().map(|ev| Some(State::new(ev))).collect()),
Behavior::WhenAny(any) => State::WhenAnyState(any.into_iter().map(|ev| Some(State::new(ev))).collect()),
Behavior::After(after_all) => State::AfterState(0, after_all.into_iter().map(State::new).collect()),
Behavior::RepeatSequence(ev, rep) => {
Behavior::WhileAll(ev, rep) => {
let state = State::new(
rep.first()
.expect("RepeatSequence's sequence of behaviors to run cannot be empty!")
.expect("WhileAll's sequence of behaviors to run cannot be empty!")
.clone(),
);
State::RepeatSequenceState(Box::new(State::new(*ev)), rep, 0, true, Box::new(state))
State::WhileAllState(Box::new(State::new(*ev)), rep, 0, true, Box::new(state))
}
}
}
Expand Down Expand Up @@ -322,7 +322,7 @@ impl<A: Clone> State<A> {
}
(
_,
&mut RepeatSequenceState(
&mut WhileAllState(
ref mut condition_behavior,
ref all_sequence_behaviors,
ref mut cur_seq_idx,
Expand Down
6 changes: 3 additions & 3 deletions bonsai/src/visualizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) enum NodeType<A> {
Select,
If,
Sequence,
RepeatSequence,
WhileAll,
While,
WhenAll,
WhenAny,
Expand Down Expand Up @@ -92,8 +92,8 @@ impl<A: Clone + Debug, K: Debug> BT<A, K> {
let right = Sequence(seq);
Self::dfs_recursive(graph, right, node_id)
}
Behavior::RepeatSequence(ev, seq) => {
let node_id = graph.add_node(NodeType::RepeatSequence);
Behavior::WhileAll(ev, seq) => {
let node_id = graph.add_node(NodeType::WhileAll);
graph.add_edge(parent_node, node_id, 1);

// left
Expand Down
26 changes: 11 additions & 15 deletions bonsai/tests/behavior_tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use crate::behavior_tests::TestActions::{Dec, Inc, LessThan, LessThanRunningSuccess};
use bonsai_bt::Behavior::RepeatSequence;
use bonsai_bt::{
Action, ActionArgs,
Behavior::{After, AlwaysSucceed, If, Invert, Select},
Event, Failure, Sequence, State,
Status::Running,
Success, UpdateArgs, Wait, WaitForever, WhenAll, While,
Action, ActionArgs, After, AlwaysSucceed, Event, Failure, If, Invert, Select, Sequence, State, Status::Running,
Success, UpdateArgs, Wait, WaitForever, WhenAll, While, WhileAll,
};

/// Some test actions.
Expand Down Expand Up @@ -437,7 +433,7 @@ fn test_after_all_succeed_out_of_order() {
fn test_repeat_sequence() {
{
let a: i32 = 0;
let after = RepeatSequence(Box::new(Action(LessThanRunningSuccess(5))), vec![Action(Inc)]);
let after = WhileAll(Box::new(Action(LessThanRunningSuccess(5))), vec![Action(Inc)]);

let mut state = State::new(after);

Expand All @@ -454,7 +450,7 @@ fn test_repeat_sequence() {
/// running initially, then the condition behavior cannot run more than once until the whole
/// sequence has succeeded.
fn test_repeat_sequence_double_running() {
let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(5))), // running...
vec![
Action(LessThanRunningSuccess(5)), // running... until current value is 5
Expand Down Expand Up @@ -484,7 +480,7 @@ fn test_repeat_sequence_double_running() {

#[test]
fn test_repeat_sequence2() {
let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(5))), // running...
vec![
Action(LessThanRunningSuccess(10)), // running... until current value is 5
Expand Down Expand Up @@ -516,7 +512,7 @@ fn test_repeat_sequence2() {

#[test]
fn test_repeat_sequence3() {
let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(2))),
vec![
Action(LessThanRunningSuccess(10)),
Expand Down Expand Up @@ -553,9 +549,9 @@ fn test_repeat_sequence_nested() {
let dec2 = Sequence(vec![Action(Dec), Action(Dec)]);
let inc1 = Sequence(vec![Action(Inc)]);

let nested = RepeatSequence(Box::new(Action(LessThanRunningSuccess(5))), vec![Action(Inc), inc1]);
let nested = WhileAll(Box::new(Action(LessThanRunningSuccess(5))), vec![Action(Inc), inc1]);

let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(1))),
vec![
nested, // inc to 6
Expand Down Expand Up @@ -586,7 +582,7 @@ fn test_repeat_sequence_nested() {
fn test_repeat_sequence_fail() {
{
let a: i32 = 4;
let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(5))),
vec![Action(Dec), Action(LessThan(0))],
);
Expand All @@ -603,7 +599,7 @@ fn test_repeat_sequence_timed() {
let a: i32 = 0;
let time_step = 0.1;
let steps = 5;
let after = RepeatSequence(
let after = WhileAll(
Box::new(Action(LessThanRunningSuccess(steps))),
vec![Wait(time_step), Action(Inc)],
);
Expand All @@ -624,7 +620,7 @@ fn test_repeat_sequence_timed() {
#[test]
#[should_panic]
fn test_repeat_sequence_empty() {
let after = RepeatSequence(Box::new(Action(LessThanRunningSuccess(0))), vec![]);
let after = WhileAll(Box::new(Action(LessThanRunningSuccess(0))), vec![]);
// panics because no behaviors...
let _state = State::new(after);
}
4 changes: 3 additions & 1 deletion examples/src/3d/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ fn write_to_screen(txt: String, w: &mut Window) {
&Point3::new(1.0, 0.0, 1.0),
);
}

#[allow(clippy::doc_lazy_continuation)]
/// This method ticks the behavior tree for a given duration 'dt' to move the
/// behavior tree forward in time. Note that a tick - basically a depth-first traversal
/// - of the tree is intended to return instantly, so it is important that the action
/// callbacks return instantly. Long-running tasks/actions might take many ticks to complete
/// , where you update and monitor the task on a tick-basis.
/// where you update and monitor the task on a tick-basis.
///
/// The ticks to execute for as long as the specified time 'dt'.
fn game_tick(
Expand Down
9 changes: 5 additions & 4 deletions examples/src/async_drone/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use tokio::time::{sleep, Duration};

struct PrintOnDrop(&'static str);

#[derive(Clone, Debug)]
#[allow(dead_code)]
#[derive(Debug)]
pub struct Point {
pub x: f32,
pub y: f32,
pub z: f32,
x: f32,
y: f32,
z: f32,
}

impl Point {
Expand Down
12 changes: 6 additions & 6 deletions examples/src/simple_npc_ai/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bonsai_bt::Behavior::RepeatSequence;
use bonsai_bt::Behavior::WhileAll;
use bonsai_bt::{Behavior::Action, Event, Failure, Running, Status, Success, UpdateArgs, BT};

#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -103,11 +103,11 @@ impl EnemyNPCState {
}
}

/// Demonstrates a usage of [RepeatSequence] behavior with
/// Demonstrates a usage of [WhileAll] behavior with
/// a simple NPC simulation.
///
/// The NPC AI first enters a higher [RepeatSequence] that
/// checks if the NPC is dead, then it succeeds to inner [RepeatSequence]
/// The NPC AI first enters a higher [WhileAll] that
/// checks if the NPC is dead, then it succeeds to inner [WhileAll]
/// where the NPC performs actions until it is determined that
/// no action points are left to consume. Then the AI control flow returns
/// to the previous higher sequence where the executions continues and the NPC rests
Expand Down Expand Up @@ -138,11 +138,11 @@ impl EnemyNPCState {
///
///
fn main() {
let run_and_shoot_ai = RepeatSequence(
let run_and_shoot_ai = WhileAll(
Box::new(Action(EnemyNPC::HasActionPointsLeft)),
vec![Action(EnemyNPC::Run), Action(EnemyNPC::Shoot)],
);
let top_ai = RepeatSequence(
let top_ai = WhileAll(
Box::new(Action(EnemyNPC::IsDead)),
vec![run_and_shoot_ai.clone(), Action(EnemyNPC::Rest), Action(EnemyNPC::Die)],
);
Expand Down

0 comments on commit fbe0776

Please sign in to comment.