Add new behavior trees library
This commit is contained in:
parent
f0c63bac7a
commit
90c0061825
13 changed files with 582 additions and 0 deletions
6
libs/bt/CMakeLists.txt
Normal file
6
libs/bt/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_BT_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_BT_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
psemek_add_library(psemek-bt ${PSEMEK_BT_HEADERS} ${PSEMEK_BT_SOURCES})
|
||||
target_include_directories(psemek-bt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-bt PUBLIC psemek-util)
|
||||
28
libs/bt/include/psemek/bt/condition.hpp
Normal file
28
libs/bt/include/psemek/bt/condition.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct condition;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct condition<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
status update(Time, Args ... args) override
|
||||
{
|
||||
return finished{check(args...)};
|
||||
}
|
||||
|
||||
virtual bool check(Args ... args) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
52
libs/bt/include/psemek/bt/failure.hpp
Normal file
52
libs/bt/include/psemek/bt/failure.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct failure_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct failure_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
failure_node(node_ptr<tree_type> child)
|
||||
: child_(std::move(child))
|
||||
{}
|
||||
|
||||
void start(Args ... args) override
|
||||
{
|
||||
child_->start(args...);
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
auto result = child_->update(dt, args...);
|
||||
if (std::get_if<finished>(&result))
|
||||
return finished{false};
|
||||
return result;
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
return child_->event(event, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
node_ptr<tree<Time, Event, Args...>> child_;
|
||||
};
|
||||
|
||||
template <typename Tree>
|
||||
node_ptr<Tree> failure(node_ptr<Tree> child)
|
||||
{
|
||||
return std::make_unique<failure_node<Tree>>(std::move(child));
|
||||
}
|
||||
|
||||
}
|
||||
59
libs/bt/include/psemek/bt/negate.hpp
Normal file
59
libs/bt/include/psemek/bt/negate.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct negate_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct negate_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
negate_node(node_ptr<tree_type> child)
|
||||
: child_(std::move(child))
|
||||
{}
|
||||
|
||||
void start(Args ... args) override
|
||||
{
|
||||
child_->start(args...);
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
auto result = child_->update(dt, args...);
|
||||
if (auto f = std::get_if<finished>(&result))
|
||||
{
|
||||
if (f->result)
|
||||
return finished{false};
|
||||
else
|
||||
return finished{true};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
return child_->event(event, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
node_ptr<tree<Time, Event, Args...>> child_;
|
||||
};
|
||||
|
||||
template <typename Tree>
|
||||
node_ptr<Tree> negate(node_ptr<Tree> child)
|
||||
{
|
||||
return std::make_unique<negate_node<Tree>>(std::move(child));
|
||||
}
|
||||
|
||||
}
|
||||
44
libs/bt/include/psemek/bt/node.hpp
Normal file
44
libs/bt/include/psemek/bt/node.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/tree.hpp>
|
||||
|
||||
#include <variant>
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
|
||||
struct running
|
||||
{};
|
||||
|
||||
struct finished
|
||||
{
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct suspended
|
||||
{
|
||||
Time duration;
|
||||
};
|
||||
|
||||
using status = std::variant<running, finished, suspended>;
|
||||
|
||||
virtual void start(Args ...) {}
|
||||
virtual status update(Time dt, Args ... args) = 0;
|
||||
virtual bool event(Event const &, Args ...) { return false; }
|
||||
|
||||
virtual ~node() {}
|
||||
};
|
||||
|
||||
template <typename Tree>
|
||||
using node_ptr = std::unique_ptr<node<Tree>>;
|
||||
|
||||
}
|
||||
77
libs/bt/include/psemek/bt/repeat.hpp
Normal file
77
libs/bt/include/psemek/bt/repeat.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct repeat_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct repeat_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::running;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
repeat_node(node_ptr<tree_type> child, std::optional<std::size_t> max_count)
|
||||
: child_(std::move(child))
|
||||
, max_count_(max_count)
|
||||
{}
|
||||
|
||||
void start(Args ... args) override
|
||||
{
|
||||
child_->start(args...);
|
||||
child_started_ = true;
|
||||
repeat_count_ = 0;
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
if (!child_started_)
|
||||
{
|
||||
child_->start(args...);
|
||||
child_started_ = true;
|
||||
}
|
||||
|
||||
auto result = child_->update(dt, args...);
|
||||
if (auto f = std::get_if<finished>(&result))
|
||||
{
|
||||
if (!(f->result))
|
||||
return finished{true};
|
||||
else
|
||||
{
|
||||
child_started_ = false;
|
||||
if (++repeat_count_ == max_count_)
|
||||
return finished{false};
|
||||
return running{};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
return child_->event(event, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
node_ptr<tree<Time, Event, Args...>> child_;
|
||||
bool child_started_ = false;
|
||||
std::optional<std::size_t> max_count_;
|
||||
std::size_t repeat_count_ = 0;
|
||||
};
|
||||
|
||||
template <typename Tree>
|
||||
node_ptr<Tree> repeat(node_ptr<Tree> child, std::optional<std::size_t> max_count = std::nullopt)
|
||||
{
|
||||
return std::make_unique<repeat_node<Tree>>(std::move(child), max_count);
|
||||
}
|
||||
|
||||
}
|
||||
15
libs/bt/include/psemek/bt/retry.hpp
Normal file
15
libs/bt/include/psemek/bt/retry.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/repeat.hpp>
|
||||
#include <psemek/bt/negate.hpp>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
node_ptr<Tree> retry(node_ptr<Tree> child, std::optional<std::size_t> max_count = std::nullopt)
|
||||
{
|
||||
return repeat<Tree>(negate<Tree>(std::move(child)), max_count);
|
||||
}
|
||||
|
||||
}
|
||||
80
libs/bt/include/psemek/bt/selector.hpp
Normal file
80
libs/bt/include/psemek/bt/selector.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct selector_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct selector_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::running;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
selector_node(std::vector<node_ptr<tree_type>> children)
|
||||
: children_(std::move(children))
|
||||
{}
|
||||
|
||||
void start(Args ...) override
|
||||
{
|
||||
current_index_ = 0;
|
||||
current_started_ = false;
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
if (current_index_ < children_.size())
|
||||
{
|
||||
if (!current_started_)
|
||||
{
|
||||
children_[current_index_]->start(args...);
|
||||
current_started_ = true;
|
||||
}
|
||||
|
||||
auto result = children_[current_index_]->update(dt, args...);
|
||||
if (auto f = std::get_if<finished>(&result))
|
||||
{
|
||||
if (f->result)
|
||||
return finished{true};
|
||||
++current_index_;
|
||||
current_started_ = false;
|
||||
return running{};
|
||||
}
|
||||
else
|
||||
return result;
|
||||
}
|
||||
return finished{false};
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
if (current_index_ < children_.size())
|
||||
return children_[current_index_]->event(event, args...);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<node_ptr<tree<Time, Event, Args...>>> children_;
|
||||
std::size_t current_index_ = 0;
|
||||
bool current_started_ = false;
|
||||
};
|
||||
|
||||
template <typename Tree, typename ... Children>
|
||||
node_ptr<Tree> selector(node_ptr<Tree> child, Children ... other_children)
|
||||
{
|
||||
std::vector<node_ptr<Tree>> children;
|
||||
children.push_back(std::move(child));
|
||||
(children.push_back(std::move(other_children)), ...);
|
||||
return std::make_unique<selector_node<Tree>>(std::move(children));
|
||||
}
|
||||
|
||||
}
|
||||
80
libs/bt/include/psemek/bt/sequence.hpp
Normal file
80
libs/bt/include/psemek/bt/sequence.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct sequence_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct sequence_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::running;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
sequence_node(std::vector<node_ptr<tree_type>> children)
|
||||
: children_(std::move(children))
|
||||
{}
|
||||
|
||||
void start(Args ...) override
|
||||
{
|
||||
current_index_ = 0;
|
||||
current_started_ = false;
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
if (current_index_ < children_.size())
|
||||
{
|
||||
if (!current_started_)
|
||||
{
|
||||
children_[current_index_]->start(args...);
|
||||
current_started_ = true;
|
||||
}
|
||||
|
||||
auto result = children_[current_index_]->update(dt, args...);
|
||||
if (auto f = std::get_if<finished>(&result))
|
||||
{
|
||||
if (!f->result)
|
||||
return finished{false};
|
||||
++current_index_;
|
||||
current_started_ = false;
|
||||
return running{};
|
||||
}
|
||||
else
|
||||
return result;
|
||||
}
|
||||
return finished{true};
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
if (current_index_ < children_.size())
|
||||
return children_[current_index_]->event(event, args...);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<node_ptr<tree<Time, Event, Args...>>> children_;
|
||||
std::size_t current_index_ = 0;
|
||||
bool current_started_ = false;
|
||||
};
|
||||
|
||||
template <typename Tree, typename ... Children>
|
||||
node_ptr<Tree> sequence(node_ptr<Tree> child, Children ... other_children)
|
||||
{
|
||||
std::vector<node_ptr<Tree>> children;
|
||||
children.push_back(std::move(child));
|
||||
(children.push_back(std::move(other_children)), ...);
|
||||
return std::make_unique<sequence_node<Tree>>(std::move(children));
|
||||
}
|
||||
|
||||
}
|
||||
52
libs/bt/include/psemek/bt/success.hpp
Normal file
52
libs/bt/include/psemek/bt/success.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree>
|
||||
struct success_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct success_node<tree<Time, Event, Args...>>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::status;
|
||||
|
||||
success_node(node_ptr<tree_type> child)
|
||||
: child_(std::move(child))
|
||||
{}
|
||||
|
||||
void start(Args ... args) override
|
||||
{
|
||||
child_->start(args...);
|
||||
}
|
||||
|
||||
status update(Time dt, Args ... args) override
|
||||
{
|
||||
auto result = child_->update(dt, args...);
|
||||
if (std::get_if<finished>(&result))
|
||||
return finished{true};
|
||||
return result;
|
||||
}
|
||||
|
||||
bool event(Event const & event, Args ... args) override
|
||||
{
|
||||
return child_->event(event, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
node_ptr<tree<Time, Event, Args...>> child_;
|
||||
};
|
||||
|
||||
template <typename Tree>
|
||||
node_ptr<Tree> success(node_ptr<Tree> child)
|
||||
{
|
||||
return std::make_unique<success_node<Tree>>(std::move(child));
|
||||
}
|
||||
|
||||
}
|
||||
16
libs/bt/include/psemek/bt/tree.hpp
Normal file
16
libs/bt/include/psemek/bt/tree.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct tree
|
||||
{
|
||||
using time_type = Time;
|
||||
using event_type = Event;
|
||||
using args_type = std::tuple<Args...>;
|
||||
};
|
||||
|
||||
}
|
||||
0
libs/bt/include/psemek/bt/updater.hpp
Normal file
0
libs/bt/include/psemek/bt/updater.hpp
Normal file
73
libs/bt/include/psemek/bt/wait.hpp
Normal file
73
libs/bt/include/psemek/bt/wait.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/bt/node.hpp>
|
||||
|
||||
namespace psemek::bt
|
||||
{
|
||||
|
||||
template <typename Tree, typename DurationFn>
|
||||
struct wait_node;
|
||||
|
||||
template <typename Time, typename Event, typename ... Args, typename DurationFn>
|
||||
struct wait_node<tree<Time, Event, Args...>, DurationFn>
|
||||
: node<tree<Time, Event, Args...>>
|
||||
{
|
||||
using tree_type = tree<Time, Event, Args...>;
|
||||
using node_type = node<tree_type>;
|
||||
using typename node_type::finished;
|
||||
using typename node_type::suspended;
|
||||
using typename node_type::status;
|
||||
|
||||
wait_node(DurationFn && duration_fn)
|
||||
: duration_fn_(std::move(duration_fn))
|
||||
{}
|
||||
|
||||
void start(Args ... args) override
|
||||
{
|
||||
remaining_time_ = duration_fn_(args...);
|
||||
}
|
||||
|
||||
status update(Time dt, Args ...) override
|
||||
{
|
||||
remaining_time_ -= dt;
|
||||
if (remaining_time_ <= 0)
|
||||
return finished{true};
|
||||
return suspended{remaining_time_};
|
||||
}
|
||||
|
||||
private:
|
||||
DurationFn duration_fn_;
|
||||
Time remaining_time_ = Time{};
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Tree, typename DurationFn>
|
||||
struct wait_helper
|
||||
{
|
||||
static node_ptr<Tree> make(DurationFn && duration_fn)
|
||||
{
|
||||
return std::make_unique<wait_node<Tree, DurationFn>>(std::move(duration_fn));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Time, typename Event, typename ... Args>
|
||||
struct wait_helper<tree<Time, Event, Args...>, Time>
|
||||
{
|
||||
static node_ptr<tree<Time, Event, Args...>> make(Time duration)
|
||||
{
|
||||
auto duration_fn = [duration](Args ...){ return duration; };
|
||||
return std::make_unique<wait_node<tree<Time, Event, Args...>, decltype(duration_fn)>>(std::move(duration_fn));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename Tree, typename DurationFn>
|
||||
node_ptr<Tree> wait(DurationFn && duration_fn)
|
||||
{
|
||||
return detail::wait_helper<Tree, DurationFn>::make(std::move(duration_fn));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue