Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.4.3: Add new decorators: ForceSuccess and ForceFailure. #15 #16

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Reference: <span id="ref"></span>
- [Timeout](#timeout)
- [Delay](#delay)
- [Retry](#retry)
- [ForceSuccess/ForceFailure](#force-success-and-force-failure)
- [Custom Decorator](#custom-decorator)
- [Sub Tree](#subtree)
- [Tick Context](#context)
Expand Down Expand Up @@ -498,6 +499,16 @@ Reference: <span id="ref"></span>
._().Action<Task>()
```

* `ForceSuccess` executes its child node and checks its status,
returns `RUNNING` if the decorated node is `RUNNING`, otherwise always returns `SUCCESS`. <span id="force-success-and-force-failure"></span> <a href="#ref">[↑]</a>

```cpp
.ForceSuccess()
._().Actino<Task>()
```

`ForceFailure` returns `RUNNING` if the decorated node is `RUNNING`, otherwise always returns `FAILURE`.

* **Custom Decorator** <span id="custom-decorator"></span> <a href="#ref">[↑]</a>

To implement a custom decorator, just inherits from `bt::DecoratorNode`:
Expand Down
9 changes: 9 additions & 0 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ while(...) {
- [Timeout 超时](#timeout)
- [Delay 延时](#delay)
- [Retry 重试](#retry)
- [ForceSuccess/ForceFailure 强制成功 和 强制失败](#force-success-and-force-failure)
- [自定义装饰器](#custom-decorator)
- [子树](#subtree)
- [Tick 上下文](#context)
Expand Down Expand Up @@ -486,6 +487,14 @@ while(...) {
._().Action<Task>()
```

* `ForceSuccess` 会执行它装饰的节点, 如果仍在执行, 则返回执行, 否则强制返回成功. <span id="force-success-and-force-failure"></span> <a href="#ref">[↑]</a>
`ForceFailure` 是类似的, 如果所装饰的节点仍在执行, 则返回执行, 否则强制返回失败.

```cpp
.ForceSuccess()
._().Actino<Task>()
```

* **自定义装饰器** <a href="#ref">[↑]</a>

要定义一个自定义的装饰器,可以继承 `bt::DecoratorNode`:
Expand Down
10 changes: 9 additions & 1 deletion bt.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2024 Chao Wang <[email protected]>.
// License: BSD, Version: 0.4.1. https://github.com/hit9/bt.cc
// License: BSD, Version: 0.4.3. https://github.com/hit9/bt.cc
// A lightweight behavior tree library that separates data and behavior.

#include "bt.h"
Expand Down Expand Up @@ -485,6 +485,14 @@ Status RetryNode::Update(const Context& ctx) {
}
}

Status ForceSuccessNode::Update(const Context& ctx) {
return (child->Update(ctx) == Status::RUNNING) ? Status::RUNNING : Status::SUCCESS;
}

Status ForceFailureNode::Update(const Context& ctx) {
return (child->Update(ctx) == Status::RUNNING) ? Status::RUNNING : Status::FAILURE;
}

//////////////////////////////////////////////////////////////
/// Node > SingleNode > RootNode
///////////////////////////////////////////////////////////////
Expand Down
26 changes: 25 additions & 1 deletion bt.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2024 Chao Wang <[email protected]>.
// License: BSD, Version: 0.4.2. https://github.com/hit9/bt.cc
// License: BSD, Version: 0.4.3. https://github.com/hit9/bt.cc
// A lightweight behavior tree library that separates data and behavior.

#ifndef HIT9_BT_H
Expand Down Expand Up @@ -720,6 +720,22 @@ class RetryNode : public DecoratorNode {
Status Update(const Context& ctx) override;
};

// ForceSuccessNode returns RUNNING if the decorated node is RUNNING, else always SUCCESS.
class ForceSuccessNode : public DecoratorNode {
public:
ForceSuccessNode(std::string_view name = "ForceSuccess", Ptr<Node> child = nullptr)
: DecoratorNode(name, std::move(child)) {}
Status Update(const Context& ctx) override;
};

// ForceFailureNode returns Failure if the decorated node is RUNNING, else always FAILURE.
class ForceFailureNode : public DecoratorNode {
public:
ForceFailureNode(std::string_view name = "ForceFailure", Ptr<Node> child = nullptr)
: DecoratorNode(name, std::move(child)) {}
Status Update(const Context& ctx) override;
};

//////////////////////////////////////////////////////////////
/// Node > SingleNode > RootNode
///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1037,6 +1053,14 @@ class Builder : public _InternalBuilderBase {
auto& RetryForever(std::chrono::milliseconds interval) {
return C<RetryNode>(-1, interval, "RetryForever");
}
// Forces a node to be success.
// It executes the decorated node and checks its status.
// Returns RUNNING if the decorated node is RUNNING, else always returns SUCCESS.
auto& ForceSuccess() { return C<ForceSuccessNode>("ForceSuccess"); }
// Forces a node to be failure.
// It executes the decorated node and checks its status.
// Returns RUNNING if the decorated node is RUNNING, else always returns FAILURE.
auto& ForceFailure() { return C<ForceFailureNode>("ForceFailure"); }
// If creates a ConditionalRunNode.
// It executes the decorated node only if the condition goes true.
// Code example::
Expand Down
5 changes: 5 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
0.4.3
-----

* Add two new decorators: `ForceSuccess` and `ForceFailure`. #15

0.4.2
-----

Expand Down
83 changes: 83 additions & 0 deletions tests/forece_success_and_failure_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <catch2/catch_test_macros.hpp>

#include "bt.h"
#include "types.h"

TEST_CASE("ForceSuccess", "[simple force success test]") {
bt::Tree root;
auto bb = std::make_shared<Blackboard>();
bt::Context ctx(bb);

// clang-format off
root
.ForceSuccess()
._().Action<A>()
.End()
;
// clang-format on

Entity e;
root.BindTreeBlob(e.blob);

// Tick#1
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 1);
REQUIRE(root.LastStatus() == bt::Status::RUNNING); // report running as is

// Tick#2: Makes A failed.
bb->shouldA = bt::Status::FAILURE;
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 2);
REQUIRE(bb->statusA == bt::Status::FAILURE);
REQUIRE(root.LastStatus() == bt::Status::SUCCESS); // still success.

// Tick#3: Makes A success.
bb->shouldA = bt::Status::SUCCESS;
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 3);
REQUIRE(bb->statusA == bt::Status::SUCCESS);
REQUIRE(root.LastStatus() == bt::Status::SUCCESS); // still success.
}

TEST_CASE("ForceFailure", "[simple force failure test]") {
bt::Tree root;
auto bb = std::make_shared<Blackboard>();
bt::Context ctx(bb);

// clang-format off
root
.ForceFailure()
._().Action<A>()
.End()
;
// clang-format on

Entity e;
root.BindTreeBlob(e.blob);

// Tick#1
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 1);
REQUIRE(root.LastStatus() == bt::Status::RUNNING); // report running as is

// Tick#2: Makes A failed.
bb->shouldA = bt::Status::FAILURE;
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 2);
REQUIRE(bb->statusA == bt::Status::FAILURE);
REQUIRE(root.LastStatus() == bt::Status::FAILURE); // should also failure.

// Tick#3: Makes A success.
bb->shouldA = bt::Status::SUCCESS;
++ctx.seq;
root.Tick(ctx);
REQUIRE(bb->counterA == 3);
REQUIRE(bb->statusA == bt::Status::SUCCESS);
REQUIRE(root.LastStatus() == bt::Status::FAILURE); // still failure.
}

Loading