use crate::simple_bt::{BehaviorArc, BehaviorNode, NodeResult};
use std::fmt::Debug;
use std::sync::Arc;
pub struct Repeated<B> {
resume: Option<BehaviorArc<B>>,
child: BehaviorArc<B>,
}
impl<B> Debug for Repeated<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Repeated")
.field("child", &self.child)
.finish()
}
}
impl<B> Repeated<B> {
pub fn new(child: BehaviorArc<B>) -> Self {
Self {
child,
resume: None,
}
}
}
impl<B: 'static> BehaviorNode<B> for Repeated<B> {
fn tick(self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
if let Some(resume) = self.resume.as_ref() {
if let NodeResult::Running(resume) = resume.clone().tick(blackboard) {
return NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
}
.arc(),
);
}
}
if let NodeResult::Running(resume) = self.child.clone().tick(blackboard) {
return NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
}
.arc(),
);
}
NodeResult::Running(Arc::new(Self {
child: self.child.clone(),
resume: None,
}))
}
}
pub struct LimitedRepeated<B> {
child: BehaviorArc<B>,
limit: usize,
completed: usize,
resume: Option<BehaviorArc<B>>,
}
impl<B> LimitedRepeated<B> {
pub fn new(limit: usize, child: BehaviorArc<B>) -> Self {
Self {
child,
limit,
completed: 0,
resume: None,
}
}
}
impl<B> Debug for LimitedRepeated<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LimitedRepeated")
.field("child", &self.child)
.field("limit", &self.limit)
.field("completed", &self.completed)
.finish()
}
}
impl<B: 'static> BehaviorNode<B> for LimitedRepeated<B> {
fn tick(self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
let mut completed = self.completed;
if completed >= self.limit {
return NodeResult::Success;
}
if let Some(resume) = self.resume.as_ref() {
match resume.clone().tick(blackboard) {
NodeResult::Running(resume) => {
return NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
limit: self.limit,
completed: self.completed,
}
.arc(),
)
}
_ => {
completed += 1;
}
}
}
match self.child.clone().tick(blackboard) {
NodeResult::Running(resume) => {
return NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
limit: self.limit,
completed,
}
.arc(),
)
}
_ => {
completed += 1;
}
}
NodeResult::Running(Arc::new(Self {
child: self.child.clone(),
resume: None,
limit: self.limit,
completed,
}))
}
}
pub struct RepeatedUntilFailure<B> {
resume: Option<BehaviorArc<B>>,
child: BehaviorArc<B>,
}
impl<B> RepeatedUntilFailure<B> {
pub fn new(child: BehaviorArc<B>) -> Self {
Self {
child,
resume: None,
}
}
}
impl<B> Debug for RepeatedUntilFailure<B> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RepeatedUntilFailure")
.field("child", &self.child)
.finish()
}
}
impl<B: 'static> BehaviorNode<B> for RepeatedUntilFailure<B> {
fn tick(self: Arc<Self>, blackboard: &mut B) -> NodeResult<B> {
if let Some(resume) = self.resume.as_ref() {
match resume.clone().tick(blackboard) {
NodeResult::Running(resume) => {
return NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
}
.arc(),
);
}
NodeResult::Failure => return NodeResult::Success,
_ => (),
}
}
match self.child.clone().tick(blackboard) {
NodeResult::Running(resume) => NodeResult::Running(
Self {
resume: Some(resume),
child: self.child.clone(),
}
.arc(),
),
NodeResult::Success => {
NodeResult::Running(Arc::new(Self {
child: self.child.clone(),
resume: None,
}))
}
NodeResult::Failure => {
NodeResult::Success
}
}
}
}
#[cfg(test)]
mod tests {
use crate::simple_bt::{
composite::{
tests::{test_with_context, Context},
Sequence,
},
BehaviorRunner,
};
use super::*;
use assert2::check;
#[derive(Debug)]
struct Push1;
impl BehaviorNode<Context> for Push1 {
fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
context.stack.push(1);
NodeResult::Success
}
}
#[derive(Debug)]
struct FibPush;
impl BehaviorNode<Context> for FibPush {
fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
if context.stack.len() < 2 {
context.stack.push(1);
} else {
let len = context.stack.len();
let a = context.stack[len - 2];
let b = context.stack[len - 1];
context.stack.push(a + b);
}
NodeResult::Success
}
}
#[derive(Debug)]
struct IsCapped {
cap: i32,
}
impl BehaviorNode<Context> for IsCapped {
fn tick(self: Arc<Self>, context: &mut Context) -> NodeResult<Context> {
if context.stack.iter().any(|v| *v > self.cap) {
NodeResult::Failure
} else {
NodeResult::Success
}
}
}
#[test]
fn limited_repeat_repeats_to_limit() {
let runner = BehaviorRunner::new(LimitedRepeated::new(3, Push1.arc()).arc());
let (res, context) = test_with_context(|| Context { stack: Vec::new() }, runner, 3);
check!(res == Some(true));
check!(context.stack == vec![1, 1, 1]);
}
#[test]
fn repeat_until_failure_stops_on_failure() {
let runner = BehaviorRunner::new(
RepeatedUntilFailure::new(
vec![FibPush.arc(), IsCapped { cap: 100 }.arc()]
.into_iter()
.collect::<Sequence<_>>()
.arc(),
)
.arc(),
);
let (res, context) = test_with_context(|| Context { stack: Vec::new() }, runner, 11);
check!(res == Some(true));
check!(context.stack == vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]);
}
}