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!
*/ |