Repository: arvidsson/BrainTree
Branch: master
Commit: ff3bc1f0c882
Files: 5
Total size: 23.6 KB
Directory structure:
gitextract_17is0q7h/
├── BrainTree.h
├── LICENSE
├── README.md
└── example/
├── Example01_Simple.cpp
└── Example02_Simple.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: BrainTree.h
================================================
// BrainTree - A C++ behavior tree single header library.
// Copyright 2015-2018 Par Arvidsson. All rights reserved.
// Licensed under the MIT license (https://github.com/arvidsson/BrainTree/blob/master/LICENSE).
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <unordered_map>
#include <cassert>
namespace BrainTree
{
class Blackboard
{
public:
void setBool(std::string key, bool value) { bools[key] = value; }
bool getBool(std::string key)
{
if (bools.find(key) == bools.end()) {
bools[key] = false;
}
return bools[key];
}
bool hasBool(std::string key) const { return bools.find(key) != bools.end(); }
void setInt(std::string key, int value) { ints[key] = value; }
int getInt(std::string key)
{
if (ints.find(key) == ints.end()) {
ints[key] = 0;
}
return ints[key];
}
bool hasInt(std::string key) const { return ints.find(key) != ints.end(); }
void setFloat(std::string key, float value) { floats[key] = value; }
float getFloat(std::string key)
{
if (floats.find(key) == floats.end()) {
floats[key] = 0.0f;
}
return floats[key];
}
bool hasFloat(std::string key) const { return floats.find(key) != floats.end(); }
void setDouble(std::string key, double value) { doubles[key] = value; }
double getDouble(std::string key)
{
if (doubles.find(key) == doubles.end()) {
doubles[key] = 0.0f;
}
return doubles[key];
}
bool hasDouble(std::string key) const { return doubles.find(key) != doubles.end(); }
void setString(std::string key, std::string value) { strings[key] = value; }
std::string getString(std::string key)
{
if (strings.find(key) == strings.end()) {
strings[key] = "";
}
return strings[key];
}
bool hasString(std::string key) const { return strings.find(key) != strings.end(); }
using Ptr = std::shared_ptr<Blackboard>;
protected:
std::unordered_map<std::string, bool> bools;
std::unordered_map<std::string, int> ints;
std::unordered_map<std::string, float> floats;
std::unordered_map<std::string, double> doubles;
std::unordered_map<std::string, std::string> strings;
};
class Node
{
public:
enum class Status
{
Invalid,
Success,
Failure,
Running,
};
virtual ~Node() {}
void setBlackboard(Blackboard::Ptr board) {
blackboard = board;
}
Blackboard::Ptr getBlackboard() const { return blackboard; }
virtual Status update() = 0;
virtual void initialize() {}
virtual void terminate(Status s) {}
Status tick()
{
if (status != Status::Running) {
initialize();
}
status = update();
if (status != Status::Running) {
terminate(status);
}
return status;
}
bool isSuccess() const { return status == Status::Success; }
bool isFailure() const { return status == Status::Failure; }
bool isRunning() const { return status == Status::Running; }
bool isTerminated() const { return isSuccess() || isFailure(); }
void reset() { status = Status::Invalid; }
using Ptr = std::shared_ptr<Node>;
protected:
Status status = Status::Invalid;
Blackboard::Ptr blackboard = nullptr;
};
class Composite : public Node
{
public:
virtual ~Composite() {}
void addChild(Node::Ptr child) { children.push_back(child); it=children.begin(); }
bool hasChildren() const { return !children.empty(); }
protected:
std::vector<Node::Ptr> children;
std::vector<Node::Ptr>::iterator it;
};
class Decorator : public Node
{
public:
virtual ~Decorator() {}
void setChild(Node::Ptr node) { child = node; }
bool hasChild() const { return child != nullptr; }
protected:
Node::Ptr child = nullptr;
};
class Leaf : public Node
{
public:
Leaf() {}
virtual ~Leaf() {}
Leaf(Blackboard::Ptr blackboard) : blackboard(blackboard) {}
virtual Status update() = 0;
protected:
Blackboard::Ptr blackboard;
};
class BehaviorTree : public Node
{
public:
BehaviorTree() {
blackboard = std::make_shared<Blackboard>();
}
BehaviorTree(const Node::Ptr &rootNode) : BehaviorTree() { root = rootNode; }
Status update() { return root->tick(); }
void setRoot(const Node::Ptr &node) { root = node; }
private:
Node::Ptr root = nullptr;
};
template <class Parent>
class DecoratorBuilder;
template <class Parent>
class CompositeBuilder
{
public:
CompositeBuilder(Parent* parent, Composite* node) : parent(parent), node(node) {}
template <class NodeType, typename... Args>
CompositeBuilder<Parent> leaf(Args... args)
{
auto child = std::make_shared<NodeType>((args)...);
child->setBlackboard(node->getBlackboard());
node->addChild(child);
return *this;
}
template <class CompositeType, typename... Args>
CompositeBuilder<CompositeBuilder<Parent>> composite(Args... args)
{
auto child = std::make_shared<CompositeType>((args)...);
child->setBlackboard(node->getBlackboard());
node->addChild(child);
return CompositeBuilder<CompositeBuilder<Parent>>(this, (CompositeType*)child.get());
}
template <class DecoratorType, typename... Args>
DecoratorBuilder<CompositeBuilder<Parent>> decorator(Args... args)
{
auto child = std::make_shared<DecoratorType>((args)...);
child->setBlackboard(node->getBlackboard());
node->addChild(child);
return DecoratorBuilder<CompositeBuilder<Parent>>(this, (DecoratorType*)child.get());
}
Parent& end()
{
return *parent;
}
private:
Parent * parent;
Composite* node;
};
template <class Parent>
class DecoratorBuilder
{
public:
DecoratorBuilder(Parent* parent, Decorator* node) : parent(parent), node(node) {}
template <class NodeType, typename... Args>
DecoratorBuilder<Parent> leaf(Args... args)
{
auto child = std::make_shared<NodeType>((args)...);
child->setBlackboard(node->getBlackboard());
node->setChild(child);
return *this;
}
template <class CompositeType, typename... Args>
CompositeBuilder<DecoratorBuilder<Parent>> composite(Args... args)
{
auto child = std::make_shared<CompositeType>((args)...);
child->setBlackboard(node->getBlackboard());
node->setChild(child);
return CompositeBuilder<DecoratorBuilder<Parent>>(this, (CompositeType*)child.get());
}
template <class DecoratorType, typename... Args>
DecoratorBuilder<DecoratorBuilder<Parent>> decorator(Args... args)
{
auto child = std::make_shared<DecoratorType>((args)...);
child->setBlackboard(node->getBlackboard());
node->setChild(child);
return DecoratorBuilder<DecoratorBuilder<Parent>>(this, (DecoratorType*)child.get());
}
Parent& end()
{
return *parent;
}
private:
Parent * parent;
Decorator* node;
};
class Builder
{
public:
Builder() {
tree = std::make_shared<BehaviorTree>();
}
template <class NodeType, typename... Args>
Builder leaf(Args... args)
{
root = std::make_shared<NodeType>((args)...);
root->setBlackboard(tree->getBlackboard());
return *this;
}
template <class CompositeType, typename... Args>
CompositeBuilder<Builder> composite(Args... args)
{
root = std::make_shared<CompositeType>((args)...);
root->setBlackboard(tree->getBlackboard());
return CompositeBuilder<Builder>(this, (CompositeType*)root.get());
}
template <class DecoratorType, typename... Args>
DecoratorBuilder<Builder> decorator(Args... args)
{
root = std::make_shared<DecoratorType>((args)...);
root->setBlackboard(tree->getBlackboard());
return DecoratorBuilder<Builder>(this, (DecoratorType*)root.get());
}
Node::Ptr build()
{
assert(root != nullptr && "The Behavior Tree is empty!");
tree->setRoot(root);
return tree;
}
private:
Node::Ptr root;
std::shared_ptr<BehaviorTree> tree;
};
// The Selector composite ticks each child node in order.
// If a child succeeds or runs, the selector returns the same status.
// In the next tick, it will try to run each child in order again.
// If all children fails, only then does the selector fail.
class Selector : public Composite
{
public:
void initialize() override
{
it = children.begin();
}
Status update() override
{
assert(hasChildren() && "Composite has no children");
while (it != children.end()) {
auto status = (*it)->tick();
if (status != Status::Failure) {
return status;
}
it++;
}
return Status::Failure;
}
};
// The Sequence composite ticks each child node in order.
// If a child fails or runs, the sequence returns the same status.
// In the next tick, it will try to run each child in order again.
// If all children succeeds, only then does the sequence succeed.
class Sequence : public Composite
{
public:
void initialize() override
{
it = children.begin();
}
Status update() override
{
assert(hasChildren() && "Composite has no children");
while (it != children.end()) {
auto status = (*it)->tick();
if (status != Status::Success) {
return status;
}
it++;
}
return Status::Success;
}
};
// The StatefulSelector composite ticks each child node in order, and remembers what child it prevously tried to tick.
// If a child succeeds or runs, the stateful selector returns the same status.
// In the next tick, it will try to run the next child or start from the beginning again.
// If all children fails, only then does the stateful selector fail.
class StatefulSelector : public Composite
{
public:
Status update() override
{
assert(hasChildren() && "Composite has no children");
while (it != children.end()) {
auto status = (*it)->tick();
if (status != Status::Failure) {
return status;
}
it++;
}
it = children.begin();
return Status::Failure;
}
};
// The StatefulSequence composite ticks each child node in order, and remembers what child it prevously tried to tick.
// If a child fails or runs, the stateful sequence returns the same status.
// In the next tick, it will try to run the next child or start from the beginning again.
// If all children succeeds, only then does the stateful sequence succeed.
class StatefulSequence : public Composite
{
public:
Status update() override
{
assert(hasChildren() && "Composite has no children");
while (it != children.end()) {
auto status = (*it)->tick();
if (status != Status::Success) {
return status;
}
it++;
}
it = children.begin();
return Status::Success;
}
};
class ParallelSequence : public Composite
{
public:
ParallelSequence(bool successOnAll = true, bool failOnAll = true) : useSuccessFailPolicy(true), successOnAll(successOnAll), failOnAll(failOnAll) {}
ParallelSequence(int minSuccess, int minFail) : minSuccess(minSuccess), minFail(minFail) {}
Status update() override
{
assert(hasChildren() && "Composite has no children");
int minimumSuccess = minSuccess;
int minimumFail = minFail;
if (useSuccessFailPolicy) {
if (successOnAll) {
minimumSuccess = children.size();
}
else {
minimumSuccess = 1;
}
if (failOnAll) {
minimumFail = children.size();
}
else {
minimumFail = 1;
}
}
int total_success = 0;
int total_fail = 0;
for (auto &child : children) {
auto status = child->tick();
if (status == Status::Success) {
total_success++;
}
if (status == Status::Failure) {
total_fail++;
}
}
if (total_success >= minimumSuccess) {
return Status::Success;
}
if (total_fail >= minimumFail) {
return Status::Failure;
}
return Status::Running;
}
private:
bool useSuccessFailPolicy = false;
bool successOnAll = true;
bool failOnAll = true;
int minSuccess = 0;
int minFail = 0;
};
// The Succeeder decorator returns success, regardless of what happens to the child.
class Succeeder : public Decorator
{
public:
Status update() override
{
child->tick();
return Status::Success;
}
};
// The Failer decorator returns failure, regardless of what happens to the child.
class Failer : public Decorator
{
public:
Status update() override
{
child->tick();
return Status::Failure;
}
};
// The Inverter decorator inverts the child node's status, i.e. failure becomes success and success becomes failure.
// If the child runs, the Inverter returns the status that it is running too.
class Inverter : public Decorator
{
public:
Status update() override
{
auto s = child->tick();
if (s == Status::Success) {
return Status::Failure;
}
else if (s == Status::Failure) {
return Status::Success;
}
return s;
}
};
// The Repeater decorator repeats infinitely or to a limit until the child returns success.
class Repeater : public Decorator
{
public:
Repeater(int limit = 0) : limit(limit) {}
void initialize() override
{
counter = 0;
}
Status update() override
{
child->tick();
if (limit > 0 && ++counter == limit) {
return Status::Success;
}
return Status::Running;
}
protected:
int limit;
int counter = 0;
};
// The UntilSuccess decorator repeats until the child returns success and then returns success.
class UntilSuccess : public Decorator
{
public:
Status update() override
{
while (1) {
auto status = child->tick();
if (status == Status::Success) {
return Status::Success;
}
}
}
};
// The UntilFailure decorator repeats until the child returns fail and then returns success.
class UntilFailure : public Decorator
{
public:
Status update() override
{
while (1) {
auto status = child->tick();
if (status == Status::Failure) {
return Status::Success;
}
}
}
};
} // namespace BrainTree
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) Pr Arvidsson 2015-2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# BrainTree
A C++ [behavior tree](http://gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php) single header library.
## Features
- behavior tree implementation
- predefined composites
- predefined decorators
- (optional) rudimentary blackboard
- (optional) behavior tree builders
## Install
Include `BrainTree.h` in your project.
## Example
```c++
// this example should print out "Hello, World!", four times
#include <iostream>
#include "BrainTree.h"
class Action : public BrainTree::Node
{
public:
Status update() override
{
std::cout << "Hello, World!" << std::endl;
return Node::Status::Success;
}
};
void CreatingBehaviorTreeManually()
{
BrainTree::BehaviorTree tree;
auto sequence = std::make_shared<BrainTree::Sequence>();
auto sayHello = std::make_shared<Action>();
auto sayHelloAgain = std::make_shared<Action>();
sequence->addChild(sayHello);
sequence->addChild(sayHelloAgain);
tree.setRoot(sequence);
tree.update();
}
void CreatingBehaviorTreeUsingBuilders()
{
auto tree = BrainTree::Builder()
.composite<BrainTree::Sequence>()
.leaf<Action>()
.leaf<Action>()
.end()
.build();
tree->update();
}
int main()
{
CreatingBehaviorTreeManually();
CreatingBehaviorTreeUsingBuilders();
return 0;
}
```
## Composites
| Composite | Behaviour | Success | Running | Failure |
| ---------------- | ------------------------------------------------------------------------------- | ----------------------- | --------------------- | -------------------- |
| Selector | Ticks each child node in order, starting from the beginning each tick | If a child succeeds | If a child is running | If all children fail |
| Sequence | Ticks each child node in order, starting from the beginning each tick | If all children succeed | If a child is running | If a child fails |
| StatefulSelector | Ticks each child node in order, starting from the child ticked in previous tick | If a child succeeds | If a child is running | If all children fail |
| StatefulSequence | Ticks each child node in order, starting from the child ticked in previous tick | If all children succeed | If a child is running | If a child fails |
## Decorators
| Decorator | Behaviour |
| ------------ | --------------------------------------------------------- |
| Succeeder | Always returns success |
| Failer | Always returns failure |
| Inverter | Inverts the result of the child node |
| Repeater | Repeats until child node succeeds (infinitely or limited) |
| UntilSuccess | Repeats until child node succeeds |
| UntilFailure | Repeats until child node fails |
## Builder
The Builder class simplifies the process of creating a behavior tree. You use three methods to build your tree:
- `leaf<NodeType>()`
- `composite<CompositeType>()`
- `decorator<DecoratorType>()`
Both `composite()` and `decorator()` require a corresponding call to `end()`, this marks where you are done adding children to a composite or a child to a decorator. At the very end you call `build()` which will then give you the finished behavior tree.
```
auto tree = Builder()
.decorator<Repeater>()
.composite<Sequence>()
.leaf<SayHello>("Foo")
.leaf<SayHello>("Bar")
.end()
.end()
.build();
```
## License
MIT (c) Pär Arvidsson 2015-2018
================================================
FILE: example/Example01_Simple.cpp
================================================
#include <iostream>
#include "BrainTree.h"
namespace bt = BrainTree;
class SayHello : public bt::Node
{
Status update() override
{
std::cout << "Hello, World!" << std::endl;
return Node::Status::Success;
}
};
int main()
{
bt::BehaviorTree tree;
auto repeater = std::make_shared<bt::Repeater>(5);
repeater->setChild(std::make_shared<SayHello>());
tree.setRoot(repeater);
// simulate 5 frames
for (int i = 0; i < 5; i++)
tree.update();
return 0;
}
================================================
FILE: example/Example02_Simple.cpp
================================================
// this example demonstrates the beviorial difference between
// Sequence, MemSequence, Selector, StatefulSelector
//
// g++ -std=c++11 Example02_Simple.cpp && ./a.out
#include <iostream>
using namespace std;
#include "../BrainTree.h"
#define STR(var) #var
enum BoardItem {
MSG,
};
class SuccessAction : public BrainTree::Node {
public:
SuccessAction(int i) {
id = i;
}
Status update() override {
cout << " success" << id << " action" << endl;
sprintf(buffer, " message from success%d", id);
assert(blackboard != nullptr && "The Blackboard is empty!");
blackboard->setString(STR(MSG), buffer);
return Status::Success;
}
private:
char buffer[128];
int id;
};
class FailAction : public BrainTree::Node {
public:
Status update() override {
cout << " fail action" << endl;
assert(blackboard != nullptr && "The Blackboard is empty!");
cout << blackboard->getString(STR(MSG)) << endl;
return Status::Failure;
}
};
void CreatingBehaviorTreeUsingBuilders() {
cout << "\n*** Creating BehaviorTree Using Builders" << endl;
cout << "\n* Sequence: success1 -> fail -> success2, three times" << endl;
auto tree = BrainTree::Builder()
.composite<BrainTree::Sequence>()
.leaf<SuccessAction>(1)
.leaf<FailAction>()
.leaf<SuccessAction>(2)
.end()
.build();
cout << " first update()" << endl;
tree->update();
cout << " second update()" << endl;
tree->update();
cout << " third update()" << endl;
tree->update();
cout << "\n* MemSequence: success1 -> fail -> success2, three times" << endl;
tree = BrainTree::Builder()
.composite<BrainTree::MemSequence>()
.leaf<SuccessAction>(1)
.leaf<FailAction>()
.leaf<SuccessAction>(2)
.end()
.build();
cout << " first update()" << endl;
tree->update();
cout << " second update()" << endl;
tree->update();
cout << " third update()" << endl;
tree->update();
cout << "\n* Selector: fail -> success1 -> success2, three times" << endl;
tree = BrainTree::Builder()
.composite<BrainTree::Selector>()
.leaf<FailAction>()
.leaf<SuccessAction>(1)
.leaf<SuccessAction>(2)
.end()
.build();
cout << " first update()" << endl;
tree->update();
cout << " second update()" << endl;
tree->update();
cout << " third update()" << endl;
tree->update();
cout << "\n* StatefulSelector: fail -> success1 -> success2, three times" << endl;
tree = BrainTree::Builder()
.composite<BrainTree::StatefulSelector>()
.leaf<FailAction>()
.leaf<SuccessAction>(1)
.leaf<SuccessAction>(2)
.end()
.build();
cout << " first update()" << endl;
tree->update();
cout << " second update()" << endl;
tree->update();
cout << " third update()" << endl;
tree->update();
cout << "\n* Sequence: Succeeder w/ fail child" << endl;
cout << "-> Inverted Failer w/ success1 child" << endl;
cout << "-> success2" << endl;
tree = BrainTree::Builder()
.composite<BrainTree::Sequence>()
.decorator<BrainTree::Succeeder>()
.leaf<FailAction>()
.end()
.decorator<BrainTree::Inverter>()
.decorator<BrainTree::Failer>()
.leaf<SuccessAction>(1)
.end()
.end()
.leaf<SuccessAction>(2)
.end()
.build();
tree->update();
}
int main() {
CreatingBehaviorTreeUsingBuilders();
return 0;
}
gitextract_17is0q7h/
├── BrainTree.h
├── LICENSE
├── README.md
└── example/
├── Example01_Simple.cpp
└── Example02_Simple.cpp
SYMBOL INDEX (23 symbols across 3 files)
FILE: BrainTree.h
function namespace (line 13) | namespace BrainTree
function class (line 79) | class Node
function class (line 129) | class Composite : public Node
function class (line 142) | class Decorator : public Node
function class (line 154) | class Leaf : public Node
function class (line 167) | class BehaviorTree : public Node
function class (line 272) | class Builder
function class (line 318) | class Selector : public Composite
function class (line 348) | class Sequence : public Composite
function class (line 378) | class StatefulSelector : public Composite
function class (line 404) | class StatefulSequence : public Composite
function class (line 426) | class ParallelSequence : public Composite
FILE: example/Example01_Simple.cpp
class SayHello (line 6) | class SayHello : public bt::Node
method Status (line 8) | Status update() override
function main (line 15) | int main()
FILE: example/Example02_Simple.cpp
type BoardItem (line 11) | enum BoardItem {
class SuccessAction (line 15) | class SuccessAction : public BrainTree::Node {
method SuccessAction (line 17) | SuccessAction(int i) {
method Status (line 20) | Status update() override {
class FailAction (line 32) | class FailAction : public BrainTree::Node {
method Status (line 34) | Status update() override {
function CreatingBehaviorTreeUsingBuilders (line 42) | void CreatingBehaviorTreeUsingBuilders() {
function main (line 123) | int main() {
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (25K chars).
[
{
"path": "BrainTree.h",
"chars": 15074,
"preview": "// BrainTree - A C++ behavior tree single header library.\n// Copyright 2015-2018 Par Arvidsson. All rights reserved.\n// "
},
{
"path": "LICENSE",
"chars": 1085,
"preview": "The MIT License (MIT)\n\nCopyright (c) Pr Arvidsson 2015-2018\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 3792,
"preview": "# BrainTree\n\nA C++ [behavior tree](http://gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_the"
},
{
"path": "example/Example01_Simple.cpp",
"chars": 516,
"preview": "#include <iostream>\n#include \"BrainTree.h\"\n\nnamespace bt = BrainTree;\n\nclass SayHello : public bt::Node\n{\n Status upd"
},
{
"path": "example/Example02_Simple.cpp",
"chars": 3721,
"preview": "// this example demonstrates the beviorial difference between\n// Sequence, MemSequence, Selector, StatefulSelector\n//\n//"
}
]
About this extraction
This page contains the full source code of the arvidsson/BrainTree GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (23.6 KB), approximately 5.6k tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.