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 #include #include #include #include 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; protected: std::unordered_map bools; std::unordered_map ints; std::unordered_map floats; std::unordered_map doubles; std::unordered_map 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; 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 children; std::vector::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(); } 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 DecoratorBuilder; template class CompositeBuilder { public: CompositeBuilder(Parent* parent, Composite* node) : parent(parent), node(node) {} template CompositeBuilder leaf(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->addChild(child); return *this; } template CompositeBuilder> composite(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->addChild(child); return CompositeBuilder>(this, (CompositeType*)child.get()); } template DecoratorBuilder> decorator(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->addChild(child); return DecoratorBuilder>(this, (DecoratorType*)child.get()); } Parent& end() { return *parent; } private: Parent * parent; Composite* node; }; template class DecoratorBuilder { public: DecoratorBuilder(Parent* parent, Decorator* node) : parent(parent), node(node) {} template DecoratorBuilder leaf(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->setChild(child); return *this; } template CompositeBuilder> composite(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->setChild(child); return CompositeBuilder>(this, (CompositeType*)child.get()); } template DecoratorBuilder> decorator(Args... args) { auto child = std::make_shared((args)...); child->setBlackboard(node->getBlackboard()); node->setChild(child); return DecoratorBuilder>(this, (DecoratorType*)child.get()); } Parent& end() { return *parent; } private: Parent * parent; Decorator* node; }; class Builder { public: Builder() { tree = std::make_shared(); } template Builder leaf(Args... args) { root = std::make_shared((args)...); root->setBlackboard(tree->getBlackboard()); return *this; } template CompositeBuilder composite(Args... args) { root = std::make_shared((args)...); root->setBlackboard(tree->getBlackboard()); return CompositeBuilder(this, (CompositeType*)root.get()); } template DecoratorBuilder decorator(Args... args) { root = std::make_shared((args)...); root->setBlackboard(tree->getBlackboard()); return DecoratorBuilder(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 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 #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(); auto sayHello = std::make_shared(); auto sayHelloAgain = std::make_shared(); sequence->addChild(sayHello); sequence->addChild(sayHelloAgain); tree.setRoot(sequence); tree.update(); } void CreatingBehaviorTreeUsingBuilders() { auto tree = BrainTree::Builder() .composite() .leaf() .leaf() .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()` - `composite()` - `decorator()` 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() .composite() .leaf("Foo") .leaf("Bar") .end() .end() .build(); ``` ## License MIT (c) Pär Arvidsson 2015-2018 ================================================ FILE: example/Example01_Simple.cpp ================================================ #include #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(5); repeater->setChild(std::make_shared()); 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 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() .leaf(1) .leaf() .leaf(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() .leaf(1) .leaf() .leaf(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() .leaf() .leaf(1) .leaf(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() .leaf() .leaf(1) .leaf(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() .decorator() .leaf() .end() .decorator() .decorator() .leaf(1) .end() .end() .leaf(2) .end() .build(); tree->update(); } int main() { CreatingBehaviorTreeUsingBuilders(); return 0; }