From dfe6349c76fa46218c3a5544218ed40fb7108d3e Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 25 Mar 2024 21:58:48 +0100 Subject: [PATCH] feat: outlined planner, started implementation --- src/planner.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ src/planner.hpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 src/planner.cpp create mode 100644 src/planner.hpp diff --git a/src/planner.cpp b/src/planner.cpp new file mode 100644 index 0000000..3ad233c --- /dev/null +++ b/src/planner.cpp @@ -0,0 +1,95 @@ +#include "planner.hpp" +#include "action.hpp" +#include "global_world_state.hpp" +#include "godot_cpp/templates/pair.hpp" +#include "utils/godot_macros.h" +#include +#include +#include + +namespace godot::goap { +void Planner::_bind_methods() { +#define CLASSNAME Planner + GDPROPERTY_HINTED(actions, Variant::OBJECT, PROPERTY_HINT_ARRAY_TYPE, "Action"); +} + +void Planner::_ready() { + this->global_world_state = GlobalWorldState::get_singleton(); +} + +void Planner::_process(double delta_time) { + this->cached_world_state.clear(); +} + +Vector Planner::make_plan(Goal *goal) { + HashMap open{}; + open.insert(PlannerNode{{}, goal->goal_state, nullptr}, 0); + HashMap, PlannerNodeHasher> from{}; + HashMap score_to_start{}; + HashMap heuristic_score{}; + + PlannerNode current; + while(!open.is_empty()) { + int current_weight = 0; + for(KeyValue &kvp : open) { + if(kvp.value > current_weight) { + current = kvp.key; + current_weight = kvp.value; + } + } + } +} + +Variant Planner::get_world_property(StringName prop_key) { + if(world_state_override.has(prop_key)) + return world_state_override.get(prop_key); + if(prop_key.begins_with("g_")) + return this->global_world_state->get_world_property(prop_key); + if(this->cached_world_state.has(prop_key)) + return this->cached_world_state[prop_key]; + if(this->has_method("get_" + prop_key)) { + Variant val = this->call(prop_key); + this->cached_world_state[prop_key] = val; + return val; + } + return nullptr; +} + +bool Planner::can_do(Ref action) { + for(WorldProperty &prop : action->context_prerequisites) { + if(this->get_world_property(prop.key) != prop.value) + return false; + } + return true; +} + +Vector> Planner::find_actions_satisfying(WorldState requirements) { + Vector> found_actions{}; + for(Ref &act : this->actions) { + for(WorldProperty &prop : requirements) { + if(act->effects.has(prop.key) + && act->effects.get(prop.key) == prop.value + && this->can_do(act)) + found_actions.push_back(act); + } + } + return found_actions; +} + +void Planner::set_actions(Array value) { + this->actions.clear(); + for(size_t i{0}; i < value.size(); ++i) { + Ref act = value[i]; + if(act.is_valid()) + this->actions.push_back(value[i]); + } +} + +Array Planner::get_actions() const { + Array array{}; + for(Ref const &act : this->actions) { + array.push_back(act); + } + return array; +} +} diff --git a/src/planner.hpp b/src/planner.hpp new file mode 100644 index 0000000..13b6c23 --- /dev/null +++ b/src/planner.hpp @@ -0,0 +1,93 @@ +#ifndef GOAP_PLANNER_HPP +#define GOAP_PLANNER_HPP + +#include "action.hpp" +#include "godot_cpp/classes/object.hpp" +#include "godot_cpp/variant/variant.hpp" +#include +#include +#include +#include + +namespace godot { +class CharacterActor; + +namespace goap { +class GlobalWorldState; + +struct State { + static State new_move_to(Vector3 location); + static State new_animate(StringName animation); + static State new_activate(Node *node); + enum Type { + STATE_MOVE_TO, + STATE_ANIMATE, + STATE_ACTIVATE + }; + State::Type type; + union { + Vector3 move_to; + StringName animate; + Node *activate; + }; +}; + +class Goal : public Resource { + GDCLASS(Goal, Resource); + static void _bind_methods(); +public: + WorldState goal_state{}; +}; + +class Planner : public Node { + GDCLASS(Planner, Node); + static void _bind_methods(); +public: + virtual void _ready() override; + virtual void _process(double delta_time) override; + + Vector make_plan(Goal *goal); + + Variant get_world_property(StringName prop_key); + + bool can_do(Ref action); + Vector> find_actions_satisfying(WorldState requirements); + + void set_actions(Array actions); + Array get_actions() const; +private: + CharacterActor *actor{nullptr}; + WorldState world_state_override{}; + WorldState cached_world_state{}; + GlobalWorldState *global_world_state{nullptr}; + + Vector> actions{}; +}; + +struct PlannerNode { + WorldState state; + WorldState open_requirements; + Ref last_edge; + + int heuristic_score(Ref action) { + int score{0}; + return score; + } +}; + +struct PlannerNodeHasher { + static _FORCE_INLINE_ uint32_t hash(godot::goap::PlannerNode const &node) { + VariantHasher variant_hasher{}; + Variant a{1}; + variant_hasher.hash(a); + } +}; + +static _FORCE_INLINE_ bool operator==(PlannerNode const& lhs, PlannerNode const& rhs) { + return true; +} +} +} + + +#endif // !GOAP_PLANNER_HPP