1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
#include <iostream>
#include <list>
#include <vector>
#include <stack>
#include <initializer_list>
#include <string>
#include <cstdlib>
#include <ctime>
#include <algorithm>
class BehaviourTree { // Note: A proper copy constructor and assignment operator should be defined, since the implicit ones use shallow copies only.
public:
class Node { // This class represents each node in the behaviour tree.
public:
virtual bool run() = 0;
};
class CompositeNode : public Node { // This type of Node follows the Composite Pattern, containing a list of other Nodes.
private:
std::vector<Node*> children;
public:
const std::vector<Node*>& getChildren() const {return children;}
void addChild (Node* child) {children.emplace_back(child);}
void addChildren (std::initializer_list<Node*>&& newChildren) {for (Node* child : newChildren) addChild(child);}
template <typename CONTAINER>
void addChildren (const CONTAINER& newChildren) {for (Node* child : newChildren) addChild(child);}
protected:
std::vector<Node*> childrenShuffled() const {std::vector<Node*> temp = children; std::random_shuffle (temp.begin(), temp.end()); return temp;}
};
class Selector : public CompositeNode {
public:
virtual bool run() override {
for (Node* child : getChildren()) { // The generic Selector implementation
if (child->run()) // If one child succeeds, the entire operation run() succeeds. Failure only results if all children fail.
return true;
}
return false; // All children failed so the entire run() operation fails.
}
};
class RandomSelector : public CompositeNode { // RandomSelector operates as a Selector, but in a random order instead of from first child to last child.
public:
virtual bool run() override {
for (Node* child : childrenShuffled()) { // The order is shuffled
if (child->run())
return true;
}
return false;
}
};
class Sequence : public CompositeNode {
public:
virtual bool run() override {
for (Node* child : getChildren()) { // The generic Sequence implementation.
if (!child->run()) // If one child fails, then enter operation run() fails. Success only results if all children succeed.
return false;
}
return true; // All children suceeded, so the entire run() operation succeeds.
}
};
class Root : public Node {
private:
Node* child;
friend class BehaviourTree;
void setChild (Node* newChild) {child = newChild;}
virtual bool run() override {return child->run();}
};
private:
Root* root;
public:
BehaviourTree() : root(new Root) {}
void setRootChild (Node* rootChild) const {root->setChild (rootChild);}
bool run() const {return root->run();}
};
class Action : public BehaviourTree::Node {
private:
std::string name;
int probabilityOfSuccess;
public:
Action (const std::string newName, int prob) : name(newName), probabilityOfSuccess(prob) {}
private:
virtual bool run() override {
if (std::rand() % 100 < probabilityOfSuccess) {
std::cout << name << " succeeded." << std::endl;
return true;
}
std::cout << name << " failed." << std::endl;
return false;
}
};
int main() {
std::srand(std::time(nullptr));
BehaviourTree behaviorTree;
BehaviourTree::Selector selector[3];
BehaviourTree::Sequence sequence[4];
Action walkToDoor ("Walk to door", 99), openDoor1 ("Open door", 15), unlockDoor ("Unlock door", 25), openDoor2 ("Open door after unlocking it", 99), smashDoor ("Smash door", 60),
walkThroughDoor ("Walk through door", 60), closeDoor ("Close door", 100), walkToWindow ("Walk to Window", 99), openWindow1 ("Open window", 70), unlockWindow ("Unlock window", 65),
openWindow2 ("Open window after unlocking it", 85), smashWindow ("Smash window", 95), climbThroughWindow ("Climb through window", 85), closeWindow ("Close window", 100);
behaviorTree.setRootChild (&selector[0]);
selector[0].addChildren ({&sequence[0],&sequence[2]});
sequence[0].addChildren ({&walkToDoor, &selector[1], &walkThroughDoor, &closeDoor});
selector[1].addChildren ({&openDoor1, &sequence[1], &smashDoor});
sequence[1].addChildren ({&unlockDoor, &openDoor2});
sequence[2].addChildren ({&walkToWindow, &selector[2], &climbThroughWindow, &closeWindow});
const std::list<BehaviourTree::Node*> nodes = {&openWindow1, &sequence[3], &smashWindow};
selector[2].addChildren(nodes);
sequence[3].addChildren ({&unlockWindow, &openWindow2});
if (behaviorTree.run())
std::cout << "Congratulations! You made it out!" << std::endl;
else
std::cout << "Sorry. You are trapped here for life." << std::endl;
}
/*
Possible outcome:
Walk to door succeeded.
Open door failed.
Unlock door failed.
Smash door failed.
Walk to Window succeeded.
Open window failed.
Unlock window failed.
Smash window succeeded.
Climb through window succeeded.
Close window succeeded.
Congratulations! You made it out!
*/
|