From 2235363e0741ecd747e5c94dd92bdf32c76fb114 Mon Sep 17 00:00:00 2001 From: David Delassus Date: Sun, 14 Jan 2024 04:34:49 +0100 Subject: [PATCH] :recycle: use std::unique_ptr for goap --- README.md | 13 ++++--- include/aitoolkit/goap.hpp | 77 ++++++++++++++++++++++++++------------ tests/goap.spec.cpp | 42 +++++++++++---------- 3 files changed, 84 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 55b8d1c..c37e2a6 100644 --- a/README.md +++ b/README.md @@ -299,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/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/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); } }