Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ 💥 Fix memory ownership #6

Merged
merged 4 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading