diff --git a/libs/bt/CMakeLists.txt b/libs/bt/CMakeLists.txt new file mode 100644 index 00000000..51cf68b0 --- /dev/null +++ b/libs/bt/CMakeLists.txt @@ -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) diff --git a/libs/bt/include/psemek/bt/condition.hpp b/libs/bt/include/psemek/bt/condition.hpp new file mode 100644 index 00000000..8e01d211 --- /dev/null +++ b/libs/bt/include/psemek/bt/condition.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace psemek::bt +{ + + template + struct condition; + + template + struct condition> + : node> + { + using tree_type = tree; + using node_type = node; + 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; + }; + +} diff --git a/libs/bt/include/psemek/bt/failure.hpp b/libs/bt/include/psemek/bt/failure.hpp new file mode 100644 index 00000000..f95dfc97 --- /dev/null +++ b/libs/bt/include/psemek/bt/failure.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +namespace psemek::bt +{ + + template + struct failure_node; + + template + struct failure_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::finished; + using typename node_type::status; + + failure_node(node_ptr 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(&result)) + return finished{false}; + return result; + } + + bool event(Event const & event, Args ... args) override + { + return child_->event(event, args...); + } + + private: + node_ptr> child_; + }; + + template + node_ptr failure(node_ptr child) + { + return std::make_unique>(std::move(child)); + } + +} diff --git a/libs/bt/include/psemek/bt/negate.hpp b/libs/bt/include/psemek/bt/negate.hpp new file mode 100644 index 00000000..d99fb2d8 --- /dev/null +++ b/libs/bt/include/psemek/bt/negate.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include + +namespace psemek::bt +{ + + template + struct negate_node; + + template + struct negate_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::finished; + using typename node_type::status; + + negate_node(node_ptr 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(&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> child_; + }; + + template + node_ptr negate(node_ptr child) + { + return std::make_unique>(std::move(child)); + } + +} diff --git a/libs/bt/include/psemek/bt/node.hpp b/libs/bt/include/psemek/bt/node.hpp new file mode 100644 index 00000000..bceab9f3 --- /dev/null +++ b/libs/bt/include/psemek/bt/node.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include + +namespace psemek::bt +{ + + template + struct node; + + template + struct node> + { + using tree_type = tree; + + struct running + {}; + + struct finished + { + bool result; + }; + + struct suspended + { + Time duration; + }; + + using status = std::variant; + + virtual void start(Args ...) {} + virtual status update(Time dt, Args ... args) = 0; + virtual bool event(Event const &, Args ...) { return false; } + + virtual ~node() {} + }; + + template + using node_ptr = std::unique_ptr>; + +} diff --git a/libs/bt/include/psemek/bt/repeat.hpp b/libs/bt/include/psemek/bt/repeat.hpp new file mode 100644 index 00000000..4deaa52e --- /dev/null +++ b/libs/bt/include/psemek/bt/repeat.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include + +#include + +namespace psemek::bt +{ + + template + struct repeat_node; + + template + struct repeat_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::running; + using typename node_type::finished; + using typename node_type::status; + + repeat_node(node_ptr child, std::optional 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(&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> child_; + bool child_started_ = false; + std::optional max_count_; + std::size_t repeat_count_ = 0; + }; + + template + node_ptr repeat(node_ptr child, std::optional max_count = std::nullopt) + { + return std::make_unique>(std::move(child), max_count); + } + +} diff --git a/libs/bt/include/psemek/bt/retry.hpp b/libs/bt/include/psemek/bt/retry.hpp new file mode 100644 index 00000000..79fade32 --- /dev/null +++ b/libs/bt/include/psemek/bt/retry.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace psemek::bt +{ + + template + node_ptr retry(node_ptr child, std::optional max_count = std::nullopt) + { + return repeat(negate(std::move(child)), max_count); + } + +} diff --git a/libs/bt/include/psemek/bt/selector.hpp b/libs/bt/include/psemek/bt/selector.hpp new file mode 100644 index 00000000..9acdb188 --- /dev/null +++ b/libs/bt/include/psemek/bt/selector.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include + +namespace psemek::bt +{ + + template + struct selector_node; + + template + struct selector_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::running; + using typename node_type::finished; + using typename node_type::status; + + selector_node(std::vector> 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(&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>> children_; + std::size_t current_index_ = 0; + bool current_started_ = false; + }; + + template + node_ptr selector(node_ptr child, Children ... other_children) + { + std::vector> children; + children.push_back(std::move(child)); + (children.push_back(std::move(other_children)), ...); + return std::make_unique>(std::move(children)); + } + +} diff --git a/libs/bt/include/psemek/bt/sequence.hpp b/libs/bt/include/psemek/bt/sequence.hpp new file mode 100644 index 00000000..49395087 --- /dev/null +++ b/libs/bt/include/psemek/bt/sequence.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include + +namespace psemek::bt +{ + + template + struct sequence_node; + + template + struct sequence_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::running; + using typename node_type::finished; + using typename node_type::status; + + sequence_node(std::vector> 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(&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>> children_; + std::size_t current_index_ = 0; + bool current_started_ = false; + }; + + template + node_ptr sequence(node_ptr child, Children ... other_children) + { + std::vector> children; + children.push_back(std::move(child)); + (children.push_back(std::move(other_children)), ...); + return std::make_unique>(std::move(children)); + } + +} diff --git a/libs/bt/include/psemek/bt/success.hpp b/libs/bt/include/psemek/bt/success.hpp new file mode 100644 index 00000000..bcf894ce --- /dev/null +++ b/libs/bt/include/psemek/bt/success.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +namespace psemek::bt +{ + + template + struct success_node; + + template + struct success_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::finished; + using typename node_type::status; + + success_node(node_ptr 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(&result)) + return finished{true}; + return result; + } + + bool event(Event const & event, Args ... args) override + { + return child_->event(event, args...); + } + + private: + node_ptr> child_; + }; + + template + node_ptr success(node_ptr child) + { + return std::make_unique>(std::move(child)); + } + +} diff --git a/libs/bt/include/psemek/bt/tree.hpp b/libs/bt/include/psemek/bt/tree.hpp new file mode 100644 index 00000000..35eeeaf6 --- /dev/null +++ b/libs/bt/include/psemek/bt/tree.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace psemek::bt +{ + + template + struct tree + { + using time_type = Time; + using event_type = Event; + using args_type = std::tuple; + }; + +} diff --git a/libs/bt/include/psemek/bt/updater.hpp b/libs/bt/include/psemek/bt/updater.hpp new file mode 100644 index 00000000..e69de29b diff --git a/libs/bt/include/psemek/bt/wait.hpp b/libs/bt/include/psemek/bt/wait.hpp new file mode 100644 index 00000000..30046862 --- /dev/null +++ b/libs/bt/include/psemek/bt/wait.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include + +namespace psemek::bt +{ + + template + struct wait_node; + + template + struct wait_node, DurationFn> + : node> + { + using tree_type = tree; + using node_type = node; + 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 + struct wait_helper + { + static node_ptr make(DurationFn && duration_fn) + { + return std::make_unique>(std::move(duration_fn)); + } + }; + + template + struct wait_helper, Time> + { + static node_ptr> make(Time duration) + { + auto duration_fn = [duration](Args ...){ return duration; }; + return std::make_unique, decltype(duration_fn)>>(std::move(duration_fn)); + } + }; + + } + + template + node_ptr wait(DurationFn && duration_fn) + { + return detail::wait_helper::make(std::move(duration_fn)); + } + +}