Skip to content

Commit

Permalink
🔀 Merge pull request #6 from linkdd/fix-memory-ownership
Browse files Browse the repository at this point in the history
♻️ Fix memory ownership
  • Loading branch information
linkdd authored Jan 14, 2024
2 parents 5df3bd9 + ee320fd commit 675fdd6
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 328 deletions.
57 changes: 32 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Create your simple state machine:
auto simple_bb = blackboard_type{};
auto simple_fsm = simple_machine<blackboard_type>();
simple_fsm.set_state(std::make_shared<state_dummy>(), simple_bb);
simple_fsm.set_state(state_dummy{}, simple_bb);
simple_fsm.pause(simple_bb);
simple_fsm.resume(simple_bb);
simple_fsm.update(simple_bb);
Expand All @@ -99,8 +99,8 @@ Or with a stack state machine:
auto stack_bb = blackboard_type{};
auto stack_fsm = stack_machine<blackboard_type>{};

stack_fsm.push_state(std::make_shared<state_dummy>(), stack_bb);
stack_fsm.push_state(std::make_shared<state_dummy>(), stack_bb);
stack_fsm.push_state(state_dummy{}, stack_bb);
stack_fsm.push_state(state_dummy{}, stack_bb);

stack_fsm.update(stack_bb);

Expand Down Expand Up @@ -129,16 +129,18 @@ struct blackboard_type {
Then, create your tree:

```cpp
auto tree = seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
// check some condition
return true;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// perform some action
return execution_state::success;
})
});
auto tree = seq<blackboard_type>(
node_list<blackboard_type>(
check<blackboard_type>([](const blackboard_type& bb) {
// check some condition
return true;
}),
task<blackboard_type>([](blackboard_type& bb) {
// perform some action
return execution_state::success;
})
)
);
```
Finally, evaluate it:
Expand All @@ -148,7 +150,7 @@ auto blackboard = blackboard_type{
// ...
};
auto state = tree->evaluate(blackboard);
auto state = tree.evaluate(blackboard);
```

For more informations, consult the
Expand Down Expand Up @@ -226,12 +228,14 @@ class collect_gold final : public action<blackboard_type> {
Finally, create an evaluator and run it:

```cpp
auto evaluator = evaluator<blackboard_type>{
std::make_shared<collect_food>(),
std::make_shared<collect_wood>(),
std::make_shared<collect_stone>(),
std::make_shared<collect_gold>()
};
auto evaluator = evaluator<blackboard_type>(
action_list<blackboard_type>(
collect_food{},
collect_wood{},
collect_stone{},
collect_gold{}
)
);

auto blackboard = blackboard_type{};
evaluator.run(blackboard);
Expand Down Expand Up @@ -295,17 +299,20 @@ class chop_tree final : public action<blackboard_type> {
Finally, create a plan and run it:
```cpp
auto actions = std::vector<action_ptr<blackboard_type>>{
std::make_shared<get_axe>(),
std::make_shared<chop_tree>()
};
auto initial = blackboard_type{};
auto goal = blackboard_type{
.has_axe = true,
.wood = 3
};
auto p = planner<blackboard_type>(actions, initial, goal);
auto p = planner<blackboard_type>(
action_list<blackboard_type>(
get_axe{},
chop_tree{}
),
initial,
goal
);
auto blackboard = initial;
while (p) {
Expand Down
149 changes: 78 additions & 71 deletions include/aitoolkit/behtree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,38 +71,44 @@ Next, create the tree:
```cpp
using namespace aitoolkit::bt;
auto tree = sel<blackboard_type>::make({
seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.attack_range;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Destroy enemy
return execution_state::success;
})
}),
seq<blackboard_type>::make({
check<blackboard_type>::make([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.sight_range;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Move towards enemy
return execution_state::success;
})
}),
seq<blackboard_type>::make({
task<blackboard_type>::make([](blackboard_type& bb) {
// Move towards waypoint
return execution_state::success;
}),
task<blackboard_type>::make([](blackboard_type& bb) {
// Select next waypoint
return execution_state::success;
auto tree = sel<blackboard_type>(
node_list<blackboard_type>(
seq<blackboard_type>(
node_list<blackboard_type>(
check<blackboard_type>([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.attack_range;
}),
task<blackboard_type>([](blackboard_type& bb) {
// Destroy enemy
return execution_state::success;
})
)
),
seq<blackboard_type>(
node_list<blackboard_type>(
check<blackboard_type>([](const blackboard_type& bb) {
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
return distance <= bb.sight_range;
}),
task<blackboard_type>([](blackboard_type& bb) {
// Move towards enemy
return execution_state::success;
})
)
),
seq<blackboard_type>::make({
task<blackboard_type>([](blackboard_type& bb) {
// Move towards waypoint
return execution_state::success;
}),
task<blackboard_type>([](blackboard_type& bb) {
// Select next waypoint
return execution_state::success;
})
})
})
});
)
);
```
Finally, evaluate the tree:
Expand All @@ -116,19 +122,21 @@ auto bb = blackboard_type{
};
while (true) {
auto state = tree->evaluate(bb);
auto state = tree.evaluate(bb);
if (state == execution_state::success) {
break;
}
}
```
*/

#include <initializer_list>
#include <functional>
#include <memory>
#include <vector>

#include <type_traits>
#include <concepts>

namespace aitoolkit::bt {
/**
* @ingroup behtree
Expand All @@ -149,18 +157,41 @@ namespace aitoolkit::bt {
template <class T>
class node {
public:
node() = default;
node(const node&) = delete;
node(node&& other) {
m_children = std::move(other.m_children);
}

virtual ~node() = default;

virtual execution_state evaluate(T& blackboard) const = 0;

protected:
std::vector<std::shared_ptr<node<T>>> m_children;
std::vector<std::unique_ptr<node<T>>> m_children;
};

/**
* @ingroup behtree
* @brief Heap-allocated pointer to node
*/
template <class T>
using node_ptr = std::shared_ptr<node<T>>;
using node_ptr = std::unique_ptr<node<T>>;

template <typename N, class T>
concept node_trait = std::derived_from<N, node<T>>;

/**
* @ingroup behtree
* @brief Helper function to create a list of nodes
*/
template <typename T, node_trait<T> ...Children>
std::vector<node_ptr<T>> node_list(Children&&... children) {
auto nodes = std::vector<node_ptr<T>>{};
nodes.reserve(sizeof...(children));
(nodes.push_back(std::make_unique<Children>(std::move(children))), ...);
return nodes;
}

/**
* @ingroup behtree
Expand All @@ -170,19 +201,12 @@ namespace aitoolkit::bt {
template <class T>
class seq final : public node<T> {
public:
static node_ptr<T> make(std::initializer_list<node_ptr<T>> children) {
auto seq_node = std::make_shared<seq<T>>();
seq_node->m_children.reserve(children.size());

for (auto& child : children) {
seq_node->m_children.push_back(child);
}

return seq_node;
seq(std::vector<node_ptr<T>> children) {
this->m_children = std::move(children);
}

virtual execution_state evaluate(T& blackboard) const override {
for (auto child : this->m_children) {
for (auto& child : this->m_children) {
auto state = child->evaluate(blackboard);
if (state != execution_state::success) {
return state;
Expand All @@ -201,19 +225,12 @@ namespace aitoolkit::bt {
template <class T>
class sel final : public node<T> {
public:
static node_ptr<T> make(std::initializer_list<node_ptr<T>> children) {
auto sel_node = std::make_shared<sel<T>>();
sel_node->m_children.reserve(children.size());

for (auto child : children) {
sel_node->m_children.push_back(child);
}

return sel_node;
sel(std::vector<node_ptr<T>> children) {
this->m_children = std::move(children);
}

virtual execution_state evaluate(T& blackboard) const override {
for (auto child : this->m_children) {
for (auto& child : this->m_children) {
auto state = child->evaluate(blackboard);
if (state != execution_state::failure) {
return state;
Expand All @@ -232,21 +249,19 @@ namespace aitoolkit::bt {
template <class T>
class neg final : public node<T> {
public:
static node_ptr<T> make(node_ptr<T> child) {
auto neg_node = std::make_shared<neg<T>>();

neg_node->m_children.reserve(1);
neg_node->m_children.push_back(child);

return neg_node;
template <node_trait<T> N>
neg(N&& child) {
this->m_children.reserve(1);
this->m_children.push_back(std::make_unique<N>(std::move(child)));
}

virtual execution_state evaluate(T& blackboard) const override {
if (this->m_children.size() != 1) {
return execution_state::failure;
}

auto state = this->m_children.front()->evaluate(blackboard);
auto& child = this->m_children.front();
auto state = child->evaluate(blackboard);
if (state == execution_state::success) {
return execution_state::failure;
} else if (state == execution_state::failure) {
Expand All @@ -268,10 +283,6 @@ namespace aitoolkit::bt {
using callback_type = std::function<bool(const T&)>;

public:
static node_ptr<T> make(callback_type fn) {
return std::make_shared<check<T>>(fn);
}

check(callback_type fn) : m_fn(fn) {}

virtual execution_state evaluate(T& blackboard) const override {
Expand All @@ -297,10 +308,6 @@ namespace aitoolkit::bt {
using callback_type = std::function<execution_state(T&)>;

public:
static node_ptr<T> make(callback_type fn) {
return std::make_shared<task<T>>(fn);
}

task(callback_type fn) : m_fn(fn) {}

virtual execution_state evaluate(T& blackboard) const override {
Expand Down
Loading

0 comments on commit 675fdd6

Please sign in to comment.