commit 0ef55377b2f137f71efb2428c5e0ef7f8076a922 Author: Warren Ulrich Date: Sun Jun 11 22:56:19 2023 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5069d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3b75c35 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.20) + +project(bt) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED true) + +add_library(bt INTERFACE) + +target_include_directories(bt INTERFACE include) \ No newline at end of file diff --git a/include/bt.hpp b/include/bt.hpp new file mode 100644 index 0000000..a9ae959 --- /dev/null +++ b/include/bt.hpp @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +enum class task_result { + failure, + success +}; + +class node_task +{ +public: + class promise_type; + + using handle_type = std::coroutine_handle; + + class promise_type { + public: + std::optional result; + handle_type get_return_object() noexcept { + return handle_type::from_promise(*this); + } + + std::suspend_always initial_suspend() noexcept { return {}; } + + std::suspend_always final_suspend() noexcept { return {}; } + + void return_value(task_result r) noexcept { + result = r; + } + + void unhandled_exception() noexcept { + std::terminate(); // TODO: handle exception + } + }; + + class awaiter { + public: + awaiter(handle_type handle) noexcept : _handle(handle) {} + + bool await_ready() const noexcept { + return false; + } + + auto await_suspend(std::coroutine_handle<>) noexcept { + return _handle; + } + + task_result await_resume() noexcept { + return *_handle.promise().result; + } + + private: + handle_type _handle; + }; + + node_task(handle_type handle) noexcept : _handle(handle) {} + + node_task(node_task&& other) noexcept : _handle(other._handle) { + other._handle = nullptr; + } + + ~node_task() { + if (_handle) { + _handle.destroy(); + } + } + + node_task& operator=(node_task&& other) noexcept { + if (&other != this) { + if (_handle) { + _handle.destroy(); + } + _handle = other._handle; + other._handle = nullptr; + } + return *this; + } + + awaiter operator co_await() noexcept { + return awaiter{_handle}; + } + + void resume() noexcept { + _handle.resume(); + } + + bool done() const noexcept { + return _handle.done(); + } + + task_result result() noexcept { + return *_handle.promise().result; + } + + std::optional try_result() noexcept { + return _handle.promise().result; + } + +private: + handle_type _handle; +}; + +using yield = std::suspend_always; + +template +class behavior_node { +public: + virtual node_task tick(Context& ctx) noexcept = 0; + + virtual ~behavior_node() = default; +}; + +template +class sequence_node : public behavior_node { +public: + void add_child(std::unique_ptr>&& child) { + _children.push_back(std::move(child)); + } + + node_task tick(Context& ctx) noexcept override { + std::size_t idx = 0; + while (idx < _children.size()) { + auto task = _children[idx]->tick(ctx); + while (!task.done()) { + task.resume(); + + if (idx < _children.size() - 1) + co_await yield{}; + } + + if (task.result() == task_result::failure) { + co_return task_result::failure; + } + + ++idx; + } + + co_return task_result::success; + } + +private: + std::vector>> _children; +}; + +template +auto sequence() { + return std::make_unique>(); +} + +template +class selector_node : public behavior_node { +public: + void add_child(std::unique_ptr>&& child) { + _children.push_back(std::move(child)); + } + + node_task tick(Context& ctx) noexcept override { + for (std::size_t idx = 0; idx < _children.size(); ++idx) { + auto task = _children[idx]->tick(ctx); + while (!task.done()) { + task.resume(); + co_await yield{}; + } + + if (task.result() == task_result::success) { + co_return task_result::success; + } + } + co_return task_result::failure; + } + +private: + std::vector>> _children; +}; + +template +auto selector() { + return std::make_unique>(); +} + +template +class action_node : public behavior_node { +public: + action_node(TickFn tick) noexcept : _tick(tick) {} + + node_task tick(Context& ctx) noexcept override { + return _tick(ctx); + } + +private: + TickFn _tick; +}; + +template +auto action(TickFn tick) { + return std::make_unique>(tick); +} + +template +class conditional_node : public behavior_node { +public: + conditional_node(ConditionFn condition) noexcept : _condition(condition) {} + + node_task tick(Context& ctx) noexcept override { + if (_condition(ctx)) { + co_return task_result::success; + } + co_return task_result::failure; + } + +private: + ConditionFn _condition; +}; + +template +auto conditional(ConditionFn condition) { + return std::make_unique>(condition); +} \ No newline at end of file