diff --git a/README.md b/README.md index 2e826fe..c37e2a6 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Create your simple state machine: auto simple_bb = blackboard_type{}; auto simple_fsm = simple_machine(); -simple_fsm.set_state(std::make_shared(), 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); @@ -99,8 +99,8 @@ Or with a stack state machine: auto stack_bb = blackboard_type{}; auto stack_fsm = stack_machine{}; -stack_fsm.push_state(std::make_shared(), stack_bb); -stack_fsm.push_state(std::make_shared(), stack_bb); +stack_fsm.push_state(state_dummy{}, stack_bb); +stack_fsm.push_state(state_dummy{}, stack_bb); stack_fsm.update(stack_bb); @@ -129,16 +129,18 @@ struct blackboard_type { Then, create your tree: ```cpp -auto tree = seq::make({ - check::make([](const blackboard_type& bb) { - // check some condition - return true; - }), - task::make([](blackboard_type& bb) { - // perform some action - return execution_state::success; - }) -}); +auto tree = seq( + node_list( + check([](const blackboard_type& bb) { + // check some condition + return true; + }), + task([](blackboard_type& bb) { + // perform some action + return execution_state::success; + }) + ) +); ``` Finally, evaluate it: @@ -148,7 +150,7 @@ auto blackboard = blackboard_type{ // ... }; -auto state = tree->evaluate(blackboard); +auto state = tree.evaluate(blackboard); ``` For more informations, consult the @@ -226,12 +228,14 @@ class collect_gold final : public action { Finally, create an evaluator and run it: ```cpp -auto evaluator = evaluator{ - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared() -}; +auto evaluator = evaluator( + action_list( + collect_food{}, + collect_wood{}, + collect_stone{}, + collect_gold{} + ) +); auto blackboard = blackboard_type{}; evaluator.run(blackboard); @@ -295,17 +299,20 @@ class chop_tree final : public action { Finally, create a plan and run it: ```cpp -auto actions = std::vector>{ - std::make_shared(), - std::make_shared() -}; auto initial = blackboard_type{}; auto goal = blackboard_type{ .has_axe = true, .wood = 3 }; -auto p = planner(actions, initial, goal); +auto p = planner( + action_list( + get_axe{}, + chop_tree{} + ), + initial, + goal +); auto blackboard = initial; while (p) { diff --git a/include/aitoolkit/behtree.hpp b/include/aitoolkit/behtree.hpp index d90516a..389abfe 100644 --- a/include/aitoolkit/behtree.hpp +++ b/include/aitoolkit/behtree.hpp @@ -71,38 +71,44 @@ Next, create the tree: ```cpp using namespace aitoolkit::bt; -auto tree = sel::make({ - seq::make({ - check::make([](const blackboard_type& bb) { - auto distance = glm::distance(bb.agent_position, bb.enemy_position); - return distance <= bb.attack_range; - }), - task::make([](blackboard_type& bb) { - // Destroy enemy - return execution_state::success; - }) - }), - seq::make({ - check::make([](const blackboard_type& bb) { - auto distance = glm::distance(bb.agent_position, bb.enemy_position); - return distance <= bb.sight_range; - }), - task::make([](blackboard_type& bb) { - // Move towards enemy - return execution_state::success; - }) - }), - seq::make({ - task::make([](blackboard_type& bb) { - // Move towards waypoint - return execution_state::success; - }), - task::make([](blackboard_type& bb) { - // Select next waypoint - return execution_state::success; +auto tree = sel( + node_list( + seq( + node_list( + check([](const blackboard_type& bb) { + auto distance = glm::distance(bb.agent_position, bb.enemy_position); + return distance <= bb.attack_range; + }), + task([](blackboard_type& bb) { + // Destroy enemy + return execution_state::success; + }) + ) + ), + seq( + node_list( + check([](const blackboard_type& bb) { + auto distance = glm::distance(bb.agent_position, bb.enemy_position); + return distance <= bb.sight_range; + }), + task([](blackboard_type& bb) { + // Move towards enemy + return execution_state::success; + }) + ) + ), + seq::make({ + task([](blackboard_type& bb) { + // Move towards waypoint + return execution_state::success; + }), + task([](blackboard_type& bb) { + // Select next waypoint + return execution_state::success; + }) }) - }) -}); + ) +); ``` Finally, evaluate the tree: @@ -116,7 +122,7 @@ auto bb = blackboard_type{ }; while (true) { - auto state = tree->evaluate(bb); + auto state = tree.evaluate(bb); if (state == execution_state::success) { break; } @@ -124,11 +130,13 @@ while (true) { ``` */ -#include #include #include #include +#include +#include + namespace aitoolkit::bt { /** * @ingroup behtree @@ -149,10 +157,18 @@ namespace aitoolkit::bt { template 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>> m_children; + std::vector>> m_children; }; /** @@ -160,7 +176,22 @@ namespace aitoolkit::bt { * @brief Heap-allocated pointer to node */ template - using node_ptr = std::shared_ptr>; + using node_ptr = std::unique_ptr>; + + template + concept node_trait = std::derived_from>; + + /** + * @ingroup behtree + * @brief Helper function to create a list of nodes + */ + template ...Children> + std::vector> node_list(Children&&... children) { + auto nodes = std::vector>{}; + nodes.reserve(sizeof...(children)); + (nodes.push_back(std::make_unique(std::move(children))), ...); + return nodes; + } /** * @ingroup behtree @@ -170,19 +201,12 @@ namespace aitoolkit::bt { template class seq final : public node { public: - static node_ptr make(std::initializer_list> children) { - auto seq_node = std::make_shared>(); - seq_node->m_children.reserve(children.size()); - - for (auto& child : children) { - seq_node->m_children.push_back(child); - } - - return seq_node; + seq(std::vector> 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; @@ -201,19 +225,12 @@ namespace aitoolkit::bt { template class sel final : public node { public: - static node_ptr make(std::initializer_list> children) { - auto sel_node = std::make_shared>(); - sel_node->m_children.reserve(children.size()); - - for (auto child : children) { - sel_node->m_children.push_back(child); - } - - return sel_node; + sel(std::vector> 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; @@ -232,13 +249,10 @@ namespace aitoolkit::bt { template class neg final : public node { public: - static node_ptr make(node_ptr child) { - auto neg_node = std::make_shared>(); - - neg_node->m_children.reserve(1); - neg_node->m_children.push_back(child); - - return neg_node; + template N> + neg(N&& child) { + this->m_children.reserve(1); + this->m_children.push_back(std::make_unique(std::move(child))); } virtual execution_state evaluate(T& blackboard) const override { @@ -246,7 +260,8 @@ namespace aitoolkit::bt { 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) { @@ -268,10 +283,6 @@ namespace aitoolkit::bt { using callback_type = std::function; public: - static node_ptr make(callback_type fn) { - return std::make_shared>(fn); - } - check(callback_type fn) : m_fn(fn) {} virtual execution_state evaluate(T& blackboard) const override { @@ -297,10 +308,6 @@ namespace aitoolkit::bt { using callback_type = std::function; public: - static node_ptr make(callback_type fn) { - return std::make_shared>(fn); - } - task(callback_type fn) : m_fn(fn) {} virtual execution_state evaluate(T& blackboard) const override { diff --git a/include/aitoolkit/fsm.hpp b/include/aitoolkit/fsm.hpp index f7f04dc..368646a 100644 --- a/include/aitoolkit/fsm.hpp +++ b/include/aitoolkit/fsm.hpp @@ -71,7 +71,7 @@ auto machine = simple_machine{}; To transition to a new state, call `set_state()`: ```cpp -machine.set_state(std::make_shared(), blackboard); +machine.set_state(state_dummy{}, blackboard); ``` > **NB:** @@ -108,10 +108,10 @@ machine.update(blackboard); > - this will call the `update` method of the current state (if any) > - if the machine is paused, calling `update()` will do nothing -To clear any state, call `set_state()` with a `nullptr`: +To clear any state, call `clear_state()`: ```cpp -machine.set_state(nullptr, blackboard); +machine.clear_state(blackboard); ``` > **NB:** This will call the `exit` method of the current state (if any). @@ -129,7 +129,7 @@ auto machine = stack_machine{}; To push a new state onto the stack, call `push_state()`: ```cpp -machine.push_state(std::make_shared(), blackboard); +machine.push_state(state_dummy{}, blackboard); ``` > **NB:** This will call the `pause` method of the current state (if any) and @@ -156,6 +156,9 @@ machine.update(blackboard); #include #include +#include +#include + namespace aitoolkit::fsm { /** * @ingroup fsm @@ -181,7 +184,10 @@ namespace aitoolkit::fsm { * @brief Heap-allocated pointer to a state. */ template - using state_ptr = std::shared_ptr>; + using state_ptr = std::unique_ptr>; + + template + concept state_trait = std::derived_from>; /** * @ingroup fsm @@ -194,19 +200,27 @@ namespace aitoolkit::fsm { /** * @brief Enters in a new state, exiting the previous one (if any). */ - void set_state(state_ptr state, T& blackboard) { + template S> + void set_state(S state, T& blackboard) { if (m_current_state) { m_current_state->exit(blackboard); } - m_current_state = state; + m_current_state = std::make_unique(state); + m_current_state->enter(blackboard); - if (m_current_state) { - m_current_state->enter(blackboard); + if (m_paused) { + m_current_state->pause(blackboard); + } + } - if (m_paused) { - m_current_state->pause(blackboard); - } + /** + * @brief Clear the current state. + */ + void clear_state(T& blackboard) { + if (m_current_state) { + m_current_state->exit(blackboard); + m_current_state = nullptr; } } @@ -261,16 +275,15 @@ namespace aitoolkit::fsm { /** * @brief Enters in a new state, pausing the previous one (if any). */ - void push_state(state_ptr state, T& blackboard) { + template S> + void push_state(S state, T& blackboard) { if (!m_state_stack.empty()) { - auto current_state = m_state_stack.back(); + auto& current_state = m_state_stack.back(); current_state->pause(blackboard); } - if (state) { - state->enter(blackboard); - m_state_stack.push_back(state); - } + state.enter(blackboard); + m_state_stack.push_back(std::make_unique(state)); } /** @@ -278,13 +291,13 @@ namespace aitoolkit::fsm { */ void pop_state(T& blackboard) { if (!m_state_stack.empty()) { - auto current_state = m_state_stack.back(); + auto& current_state = m_state_stack.back(); current_state->exit(blackboard); m_state_stack.pop_back(); } if (!m_state_stack.empty()) { - auto current_state = m_state_stack.back(); + auto& current_state = m_state_stack.back(); current_state->resume(blackboard); } } @@ -294,7 +307,7 @@ namespace aitoolkit::fsm { */ void update(T& blackboard) { if (!m_state_stack.empty()) { - auto current_state = m_state_stack.back(); + auto& current_state = m_state_stack.back(); current_state->update(blackboard); } } diff --git a/include/aitoolkit/goap.hpp b/include/aitoolkit/goap.hpp index 10049bd..c322e4d 100644 --- a/include/aitoolkit/goap.hpp +++ b/include/aitoolkit/goap.hpp @@ -136,13 +136,6 @@ class mine_stone final : public action { Finally, create a plan and run it: ```cpp -auto actions = std::vector>{ - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared() -}; auto initial = blackboard_type{}; auto goal = blackboard_type{ .has_axe = true, @@ -152,7 +145,17 @@ auto goal = blackboard_type{ .stone = 1 }; -auto p = planner(actions, initial, goal); +auto p = planner( + action_list( + get_axe{}, + get_pickaxe{}, + chop_tree{}, + mine_gold{}, + mine_stone{} + ) + initial, + goal +); auto blackboard = initial; while (p) { @@ -164,6 +167,7 @@ while (p) { #include #include #include +#include #include #include #include @@ -218,7 +222,18 @@ namespace aitoolkit::goap { * @brief Heeap allocated pointer to an action. */ template - using action_ptr = std::shared_ptr>; + using action_ptr = std::unique_ptr>; + + template + concept action_trait = std::derived_from>; + + template ... Actions> + std::vector> action_list(Actions... actions) { + auto action_list = std::vector>{}; + action_list.reserve(sizeof...(Actions)); + (action_list.push_back(std::make_unique(std::move(actions))), ...); + return action_list; + } /** * @ingroup goap @@ -228,35 +243,45 @@ namespace aitoolkit::goap { template class plan { public: - plan(std::queue> actions) : m_actions(actions) {} + plan() = default; /** * @brief Get the number of actions in the plan. */ size_t size() const { - return m_actions.size(); + return m_plan.size(); } /** * @brief Check if the plan is empty. */ operator bool() const { - return !m_actions.empty(); + return !m_plan.empty(); } /** * @brief Execute the next planned action. */ void run_next(T& blackboard) { - if (!m_actions.empty()) { - auto action = m_actions.front(); - m_actions.pop(); + if (!m_plan.empty()) { + auto action_idx = m_plan.front(); + m_plan.pop(); + + auto& action = m_actions[action_idx]; action->apply_effects(blackboard, false); } } private: - std::queue> m_actions; + std::queue m_plan; + std::vector> m_actions; + + friend plan planner( + std::vector> actions, + T initital_blackboard, + T goal_blackboard, + size_t max_iterations + ); }; /** @@ -285,7 +310,7 @@ namespace aitoolkit::goap { T blackboard; float cost; - action_ptr action_taken; + std::optional action_taken_idx; std::shared_ptr parent; }; @@ -303,7 +328,7 @@ namespace aitoolkit::goap { open_set.push(std::make_shared(node_type{ .blackboard = initital_blackboard, .cost = 0.0f, - .action_taken = nullptr, + .action_taken_idx = std::nullopt, .parent = nullptr })); @@ -316,20 +341,24 @@ namespace aitoolkit::goap { open_set.pop(); if (current_node->blackboard == goal_blackboard) { - auto actions = std::queue>{}; + auto p = plan(); + p.m_actions = std::move(actions); while (current_node->parent != nullptr) { - actions.push(current_node->action_taken); + auto action_idx = current_node->action_taken_idx.value(); + p.m_plan.push(action_idx); current_node = current_node->parent; } - return plan(actions); + return p; } if (!closed_set.contains(current_node->blackboard)) { closed_set.insert(current_node->blackboard); - for (auto& action : actions) { + for (size_t action_idx = 0; action_idx < actions.size(); action_idx++) { + auto& action = actions[action_idx]; + if (action->check_preconditions(current_node->blackboard)) { auto next_blackboard = current_node->blackboard; action->apply_effects(next_blackboard, true); @@ -339,7 +368,7 @@ namespace aitoolkit::goap { open_set.push(std::make_shared(node_type{ .blackboard = next_blackboard, .cost = next_cost, - .action_taken = action, + .action_taken_idx = action_idx, .parent = current_node })); } @@ -348,6 +377,6 @@ namespace aitoolkit::goap { } } - return plan(std::queue>{}); + return plan(); } } diff --git a/include/aitoolkit/utility.hpp b/include/aitoolkit/utility.hpp index 90947f0..ded1d98 100644 --- a/include/aitoolkit/utility.hpp +++ b/include/aitoolkit/utility.hpp @@ -97,11 +97,13 @@ class collect_gold final : public action { Finally, create an evaluator and run it: ```cpp -auto evaluator = evaluator{ - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared() +auto evaluator = evaluator( + action_list( + collect_food{}, + collect_wood{}, + collect_stone{}, + collect_gol{} + ) }; auto blackboard = blackboard_type{}; @@ -109,11 +111,13 @@ evaluator.run(blackboard); ``` */ -#include #include #include #include +#include +#include + namespace aitoolkit::utility { /** * @ingroup utility @@ -141,7 +145,22 @@ namespace aitoolkit::utility { * @brief Heap allocated pointer to an action. */ template - using action_ptr = std::shared_ptr>; + using action_ptr = std::unique_ptr>; + + template + concept action_trait = std::derived_from>; + + /** + * @ingroup utility + * @brief Helper function to create a list of actions + */ + template ...Actions> + std::vector> action_list(Actions&&... actions) { + auto actions_list = std::vector>{}; + actions_list.reserve(sizeof...(Actions)); + (actions_list.push_back(std::make_unique(std::move(actions))), ...); + return actions_list; + } /** * @ingroup utility @@ -154,12 +173,7 @@ namespace aitoolkit::utility { /** * @brief Construct an evaluator from a list of actions */ - evaluator(std::initializer_list> actions) { - m_actions.reserve(actions.size()); - for (auto action : actions) { - m_actions.push_back(action); - } - } + evaluator(std::vector> actions) : m_actions(std::move(actions)) {} /** * @brief Find the best action and apply it to the blackboard @@ -170,13 +184,13 @@ namespace aitoolkit::utility { } auto best_score = std::numeric_limits::min(); - auto best_action = m_actions.front(); + auto best_action = m_actions.front().get(); for (auto& action : m_actions) { auto score = action->score(blackboard); if (score > best_score) { best_score = score; - best_action = action; + best_action = action.get(); } } diff --git a/tests/behtree.spec.cpp b/tests/behtree.spec.cpp index 13c6803..60636b3 100644 --- a/tests/behtree.spec.cpp +++ b/tests/behtree.spec.cpp @@ -11,11 +11,11 @@ TEST_CASE("behtree task node evaluation") { SUBCASE("task node returns success") { blackboard_type blackboard; - auto task_node = task::make([](auto& blackboard) { + auto task_node = task([](auto& blackboard) { return execution_state::success; }); - auto state = task_node->evaluate(blackboard); + auto state = task_node.evaluate(blackboard); CHECK(state == execution_state::success); } @@ -23,11 +23,11 @@ TEST_CASE("behtree task node evaluation") { SUBCASE("task node returns failure") { blackboard_type blackboard; - auto task_node = task::make([](auto& blackboard) { + auto task_node = task([](auto& blackboard) { return execution_state::failure; }); - auto state = task_node->evaluate(blackboard); + auto state = task_node.evaluate(blackboard); CHECK(state == execution_state::failure); } @@ -35,11 +35,11 @@ TEST_CASE("behtree task node evaluation") { SUBCASE("task node returns running") { blackboard_type blackboard; - auto task_node = task::make([](auto& blackboard) { + auto task_node = task([](auto& blackboard) { return execution_state::running; }); - auto state = task_node->evaluate(blackboard); + auto state = task_node.evaluate(blackboard); CHECK(state == execution_state::running); } @@ -49,11 +49,11 @@ TEST_CASE("behtree check node evaluation") { SUBCASE("check node returns success") { blackboard_type blackboard; - auto check_node = check::make([](auto& blackboard) { + auto check_node = check([](auto& blackboard) { return true; }); - auto state = check_node->evaluate(blackboard); + auto state = check_node.evaluate(blackboard); CHECK(state == execution_state::success); } @@ -61,11 +61,11 @@ TEST_CASE("behtree check node evaluation") { SUBCASE("check node returns failure") { blackboard_type blackboard; - auto check_node = check::make([](auto& blackboard) { + auto check_node = check([](auto& blackboard) { return false; }); - auto state = check_node->evaluate(blackboard); + auto state = check_node.evaluate(blackboard); CHECK(state == execution_state::failure); } @@ -75,13 +75,13 @@ TEST_CASE("behtree neg node evaluation") { SUBCASE("neg node returns success") { blackboard_type blackboard; - auto neg_node = neg::make( - task::make([](auto& blackboard) { + auto neg_node = neg( + task([](auto& blackboard) { return execution_state::failure; }) ); - auto state = neg_node->evaluate(blackboard); + auto state = neg_node.evaluate(blackboard); CHECK(state == execution_state::success); } @@ -89,13 +89,13 @@ TEST_CASE("behtree neg node evaluation") { SUBCASE("neg node returns failure") { blackboard_type blackboard; - auto neg_node = neg::make( - task::make([](auto& blackboard) { + auto neg_node = neg( + task([](auto& blackboard) { return execution_state::success; }) ); - auto state = neg_node->evaluate(blackboard); + auto state = neg_node.evaluate(blackboard); CHECK(state == execution_state::failure); } @@ -103,13 +103,13 @@ TEST_CASE("behtree neg node evaluation") { SUBCASE("neg node returns running") { blackboard_type blackboard; - auto neg_node = neg::make( - task::make([](auto& blackboard) { + auto neg_node = neg( + task([](auto& blackboard) { return execution_state::running; }) ); - auto state = neg_node->evaluate(blackboard); + auto state = neg_node.evaluate(blackboard); CHECK(state == execution_state::running); } @@ -119,18 +119,20 @@ TEST_CASE("behtree seq node evaluation") { SUBCASE("seq node returns success") { blackboard_type blackboard; - auto seq_node = seq::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }) - }); + auto seq_node = seq( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }) + ) + ); - auto state = seq_node->evaluate(blackboard); + auto state = seq_node.evaluate(blackboard); CHECK(state == execution_state::success); CHECK(blackboard.node_count == 2); @@ -139,18 +141,20 @@ TEST_CASE("behtree seq node evaluation") { SUBCASE("seq node returns failure") { blackboard_type blackboard; - auto seq_node = seq::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }) - }); + auto seq_node = seq( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }) + ) + ); - auto state = seq_node->evaluate(blackboard); + auto state = seq_node.evaluate(blackboard); CHECK(state == execution_state::failure); CHECK(blackboard.node_count == 2); @@ -159,18 +163,20 @@ TEST_CASE("behtree seq node evaluation") { SUBCASE("seq node returns failure early") { blackboard_type blackboard; - auto seq_node = seq::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }) - }); + auto seq_node = seq( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }) + ) + ); - auto state = seq_node->evaluate(blackboard); + auto state = seq_node.evaluate(blackboard); CHECK(state == execution_state::failure); CHECK(blackboard.node_count == 1); @@ -179,18 +185,20 @@ TEST_CASE("behtree seq node evaluation") { SUBCASE("seq node returns running") { blackboard_type blackboard; - auto seq_node = seq::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::running; - }) - }); + auto seq_node = seq( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::running; + }) + ) + ); - auto state = seq_node->evaluate(blackboard); + auto state = seq_node.evaluate(blackboard); CHECK(state == execution_state::running); CHECK(blackboard.node_count == 2); @@ -199,18 +207,20 @@ TEST_CASE("behtree seq node evaluation") { SUBCASE("seq node returns running early") { blackboard_type blackboard; - auto seq_node = seq::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::running; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }) - }); + auto seq_node = seq( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::running; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }) + ) + ); - auto state = seq_node->evaluate(blackboard); + auto state = seq_node.evaluate(blackboard); CHECK(state == execution_state::running); CHECK(blackboard.node_count == 1); @@ -221,18 +231,20 @@ TEST_CASE("behtree sel node evaluation") { SUBCASE("sel node returns success when one child succeeds") { blackboard_type blackboard; - auto sel_node = sel::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }) - }); + auto sel_node = sel( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }) + ) + ); - auto state = sel_node->evaluate(blackboard); + auto state = sel_node.evaluate(blackboard); CHECK(state == execution_state::success); CHECK(blackboard.node_count == 2); @@ -241,18 +253,20 @@ TEST_CASE("behtree sel node evaluation") { SUBCASE("sel node returns success when one child succeeds early") { blackboard_type blackboard; - auto sel_node = sel::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::success; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }) - }); + auto sel_node = sel( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::success; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }) + ) + ); - auto state = sel_node->evaluate(blackboard); + auto state = sel_node.evaluate(blackboard); CHECK(state == execution_state::success); CHECK(blackboard.node_count == 1); @@ -261,18 +275,20 @@ TEST_CASE("behtree sel node evaluation") { SUBCASE("sel node return failure when all children fail") { blackboard_type blackboard; - auto sel_node = sel::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }) - }); + auto sel_node = sel( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }) + ) + ); - auto state = sel_node->evaluate(blackboard); + auto state = sel_node.evaluate(blackboard); CHECK(state == execution_state::failure); CHECK(blackboard.node_count == 2); @@ -281,18 +297,20 @@ TEST_CASE("behtree sel node evaluation") { SUBCASE("sel node returns running when one child returns running") { blackboard_type blackboard; - auto sel_node = sel::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::running; - }) - }); + auto sel_node = sel( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::running; + }) + ) + ); - auto state = sel_node->evaluate(blackboard); + auto state = sel_node.evaluate(blackboard); CHECK(state == execution_state::running); CHECK(blackboard.node_count == 2); @@ -301,18 +319,20 @@ TEST_CASE("behtree sel node evaluation") { SUBCASE("sel node returns running when one child returns running early") { blackboard_type blackboard; - auto sel_node = sel::make({ - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::running; - }), - task::make([](auto& blackboard) { - blackboard.node_count++; - return execution_state::failure; - }) - }); + auto sel_node = sel( + node_list( + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::running; + }), + task([](auto& blackboard) { + blackboard.node_count++; + return execution_state::failure; + }) + ) + ); - auto state = sel_node->evaluate(blackboard); + auto state = sel_node.evaluate(blackboard); CHECK(state == execution_state::running); CHECK(blackboard.node_count == 1); diff --git a/tests/fsm.spec.cpp b/tests/fsm.spec.cpp index 6e9b663..25a477f 100644 --- a/tests/fsm.spec.cpp +++ b/tests/fsm.spec.cpp @@ -44,7 +44,7 @@ TEST_CASE("fsm simple machine") { auto blackboard = blackboard_type{}; auto fsm = simple_machine{}; - fsm.set_state(std::make_shared(1), blackboard); + fsm.set_state(state_dummy{1}, blackboard); CHECK(blackboard.enter == 1); fsm.pause(blackboard); @@ -56,15 +56,15 @@ TEST_CASE("fsm simple machine") { fsm.update(blackboard); CHECK(blackboard.update == 1); - fsm.set_state(std::make_shared(2), blackboard); + fsm.set_state(state_dummy{2}, blackboard); CHECK(blackboard.exit == 1); CHECK(blackboard.enter == 2); - fsm.set_state(nullptr, blackboard); + fsm.clear_state(blackboard); CHECK(blackboard.exit == 2); fsm.pause(blackboard); - fsm.set_state(std::make_shared(3), blackboard); + fsm.set_state(state_dummy{3}, blackboard); CHECK(blackboard.enter == 3); CHECK(blackboard.pause == 3); } @@ -73,10 +73,10 @@ TEST_CASE("fsm stack machine") { auto blackboard = blackboard_type{}; auto fsm = stack_machine{}; - fsm.push_state(std::make_shared(1), blackboard); + fsm.push_state(state_dummy{1}, blackboard); CHECK(blackboard.enter == 1); - fsm.push_state(std::make_shared(2), blackboard); + fsm.push_state(state_dummy{2}, blackboard); CHECK(blackboard.enter == 2); CHECK(blackboard.pause == 1); diff --git a/tests/goap.spec.cpp b/tests/goap.spec.cpp index b3a4e21..b2fb627 100644 --- a/tests/goap.spec.cpp +++ b/tests/goap.spec.cpp @@ -132,16 +132,17 @@ TEST_CASE("goap planning") { .gold = 2, .stone = 1 }; - - auto actions = std::vector>{ - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared() - }; - - auto p = planner(actions, initial, goal); + auto p = planner( + action_list( + chop_wood{}, + build_storage{}, + gather_food{}, + mine_gold{}, + mine_stone{} + ), + initial, + goal + ); CHECK(p); // 10 chop wood, 1 build storage, 3 gather food, 2 mine gold, 1 mine stone @@ -170,15 +171,18 @@ TEST_CASE("goap planning") { .stone = 1 }; - auto actions = std::vector>{ - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared(), - std::make_shared() - }; - - auto p = planner(actions, initial, goal, 1000); + auto p = planner( + action_list( + chop_wood{}, + build_storage{}, + gather_food{}, + mine_gold{}, + mine_stone{} + ), + initial, + goal, + 1000 + ); CHECK(!p); } } diff --git a/tests/utility.spec.cpp b/tests/utility.spec.cpp index 28a24a3..88ae6a0 100644 --- a/tests/utility.spec.cpp +++ b/tests/utility.spec.cpp @@ -14,13 +14,8 @@ struct blackboard_type { }; TEST_CASE("utility evaluator") { - class action_a final : public action { public: - static action_ptr make() { - return std::make_shared(); - } - virtual float score(const blackboard_type& blackboard) const override { return 1.0f; } @@ -32,10 +27,6 @@ TEST_CASE("utility evaluator") { class action_b final : public action { public: - static action_ptr make() { - return std::make_shared(); - } - virtual float score(const blackboard_type& blackboard) const override { return 2.0f; } @@ -47,10 +38,6 @@ TEST_CASE("utility evaluator") { class action_c final : public action { public: - static action_ptr make() { - return std::make_shared(); - } - virtual float score(const blackboard_type& blackboard) const override { return 3.0f; } @@ -62,11 +49,13 @@ TEST_CASE("utility evaluator") { SUBCASE("evaluator runs action with highest score") { blackboard_type blackboard; - auto machine = evaluator{ - action_a::make(), - action_b::make(), - action_c::make(), - }; + auto machine = evaluator( + action_list( + action_a{}, + action_b{}, + action_c{} + ) + ); machine.run(blackboard);