metro-rts/src/goap/planner.hpp

81 lines
3 KiB
C++

#ifndef GOAP_PLANNER_HPP
#define GOAP_PLANNER_HPP
#include "action.hpp"
#include "actor_world_state.hpp"
#include "goal.hpp"
#include <cstdint>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/templates/vector.hpp>
#include <godot_cpp/variant/packed_int32_array.hpp>
namespace gd = godot;
namespace goap {
typedef gd::Vector<Action const *> Plan;
/*! Wrapper for WorldState to be used to represent nodes in the A* implementation.
* Mainly required to let a HashMap of HashMaps work.
*/
struct WorldStateNode {
WorldStateNode() = default;
//! Root node constructor
WorldStateNode(WorldState const &goal, ActorWorldState *context);
//! Path node constructor
WorldStateNode(WorldStateNode const &last_state, Action const *last_action);
~WorldStateNode() = default;
int requirements_unmet() const;
WorldState state{};
WorldState open_requirements{};
Action const *last_action{nullptr};
ActorWorldState *context;
};
struct WorldStateNodeHasher {
static uint32_t hash(WorldStateNode const &state);
};
extern bool operator==(WorldStateNode const &lhs, WorldStateNode const &rhs);
extern bool operator!=(WorldStateNode const &lhs, WorldStateNode const &rhs);
extern bool operator<(WorldStateNode const &lhs, WorldStateNode const &rhs);
extern bool operator<=(WorldStateNode const &lhs, WorldStateNode const &rhs);
extern bool operator>(WorldStateNode const &lhs, WorldStateNode const &rhs);
extern bool operator>=(WorldStateNode const &lhs, WorldStateNode const &rhs);
typedef gd::HashMap<WorldStateNode, WorldStateNode, WorldStateNodeHasher> NodeMap;
typedef gd::HashMap<WorldStateNode, float, WorldStateNodeHasher> NodeScoreMap;
/*! Uses A* to compute `Action` sequences for an actor to achieve `Goal`s.
*
* Set up available actions from the editor and call plan_for_goal with a desired goal.
* Requires a sibling node inheriting from ActorWorldState called "ActorWorldState".
*/
class Planner : public gd::Node {
GDCLASS(Planner, gd::Node);
static void _bind_methods();
public:
virtual void _enter_tree() override;
//! Compute a plan to achieve the passed `Goal`.
Plan plan_for_goal(gd::Ref<Goal> goal);
void set_actions(gd::Array array);
gd::Array get_actions() const;
void set_test_goal(gd::Ref<Goal> goal);
gd::Ref<Goal> get_test_goal() const;
private:
//! \returns A vector of all actions that satisfy any of the requirements in `unsatisfied`
gd::Vector<Action const *> get_neighbours(WorldStateNode const &from) const;
//! \returns True if the passed action contributes to any of the open requirements in `node`.
bool does_action_contribute(Action const *action, WorldStateNode const &node) const;
//! \returns A plan starting with `start_node` traced backwards through the `from` map.
Plan unroll_plan(WorldStateNode const &start_node, NodeMap const &from);
private:
gd::Ref<Goal> test_goal{};
ActorWorldState *world_state{nullptr};
gd::Vector<Action const *> actions;
};
}
#endif // !GOAP_PLANNER_HPP