#pragma once #include <map> #include <vector> #include <string> #include <set> #include "GOAPAction.h" #include "GOAPState.h" #include "GOAPResources.h" namespace Game { /* Comparision function for states based on the resources of each state */ struct StateComp { bool operator()(GOAPState* a, GOAPState* b) { return a->Comp(*b); } }; /* Used to sort GOAP states in the state list based on the HValues */ struct GOAPComp { bool operator()(GOAPState* a, GOAPState* b) { if(a->GetScore() == b->GetScore()) { if(a->GetHVal() == b->GetHVal()) return a < b; else return a->GetHVal() < b->GetHVal(); } else return a->GetScore() < b->GetScore(); } }; class GOAPSolver { public: GOAPSolver(GOAPState& end, std::map<int, std::string>& idnames, std::vector<GOAPAction>& actions); ~GOAPSolver(); /* Sets the starting state for the solver, and the resources owned by the character */ void SetStart(GOAPState& startState, GOAPResources& presources); /* Searches all possibilities to find the best course of action (Limited to 10,000 attempts) */ void PerformAStar(std::vector<GOAPAction>& actionList); private: std::map<int, std::string> idtoname;//used for printing plans std::vector<GOAPAction> actionSet; std::set<GOAPState*, StateComp> generatedStates; std::set<GOAPState*, GOAPComp> open; GOAPState start; GOAPState end; GOAPState current; bool foundEnd; /* Checks the "neighboring" states to find the possible actions that can be performed (with their outcomes) and computes the HVal and GVal for those actions */ void PerformOneStep(std::vector<GOAPAction>& actionList); /* Returns the cheapest option from the open list */ GOAPState* GetCheapest(); /* Removes the cheapest option from the open list */ void RemoveOpen(); /* Adds the state to the open list */ void Open(GOAPState* state); /* Removes the state from the open list and adds it to the closed list */ void Close(GOAPState* state); /* Checks to see if the state satisfies all of the end conditions */ bool ReachedEnd(GOAPState* state); /* Creates the list of actions needed to get from the start state to the end state */ void GeneratePath(GOAPState* state, std::vector<GOAPAction>& list); /* Uses the number of differences between the current state and the given state as the heuristic (more differences are worse) */ float ComputeHeuristic(GOAPState* state); /* Checks to see if the given state can already be reached through another method (that is, already exists in the list) */ bool isInGenerated(GOAPState* s); /* Returns the state from the list of computed states */ GOAPState* GetGenerated(GOAPState* s); /* Adds the state to the list of computed states */ void AddToGenerated(GOAPState* s); /* Clear all computed states */ void ClearStates(); }; }
#include "GOAPSolver.h" #include "CMacros.h" #include "CWindow.h" #include "GOAPAction.h" using namespace std; using namespace Combustion; namespace Game { GOAPSolver::GOAPSolver(GOAPState& e, map<int, string>& idnames, vector<GOAPAction>& actions) : end(e), idtoname(idnames), actionSet(actions), foundEnd(false) {} GOAPSolver::~GOAPSolver() { ClearStates(); } void GOAPSolver::ClearStates() { for(auto it = generatedStates.begin(); it != generatedStates.end(); ++it) delete (*it); generatedStates.clear(); } void GOAPSolver::SetStart(GOAPState& startState, GOAPResources& resources) { foundEnd = false; start = GOAPState(startState); start.SetWorldState(resources); current = start; open.clear(); ClearStates(); Open(¤t); } GOAPState* GOAPSolver::GetCheapest() { GOAPState* state = *(open.begin()); return state; } void GOAPSolver::Open(GOAPState* state) { state->SetIsClosed(false); state->SetIsOpen(true); open.insert(state); } void GOAPSolver::Close(GOAPState* state) { state->SetIsClosed(true); state->SetIsOpen(false); } void GOAPSolver::RemoveOpen() { open.erase( open.begin() ); } bool GOAPSolver::ReachedEnd(GOAPState* state) { return state->IsSatisfied(); } void GOAPSolver::GeneratePath(GOAPState* state, std::vector<GOAPAction>& list) { if(state->GetAction() != 0) { list.push_back(*(state->GetAction())); } if(state->GetParent() != 0) GeneratePath(state->GetParent(), list); } void GOAPSolver::PerformOneStep(std::vector<GOAPAction>& actionList) { if(open.size() == 0) return; GOAPState* currstate = GetCheapest(); if( ReachedEnd(currstate) ) { foundEnd = true; GeneratePath(currstate, actionList); return; } //move current to closed set RemoveOpen(); Close(currstate); //find all actions that can be performed on the current state for(unsigned int i = 0; i < actionSet.size(); ++i) { if( actionSet[i].MeetsPrecondition(*currstate) ) { //Get state if this action is performed GOAPState* neighbor = actionSet[i].ApplyToState(*currstate); neighbor->SetAction(&(actionSet[i])); if(!isInGenerated(neighbor)) { neighbor->SetIsOpen(true); AddToGenerated(neighbor); } else { GOAPState* temp = GetGenerated(neighbor); delete neighbor; if(neighbor->GetIsClosed()) continue; neighbor = temp; } //if neighbor isOpen if(neighbor->GetIsOpen()) { neighbor->SetGVal( currstate->GetGVal() + actionSet[i].GetGCost() ); neighbor->SetHVal( ComputeHeuristic(neighbor) ); neighbor->SetParent(currstate); //check to see if the path to this one is better.... if(currstate->GetGVal() < neighbor->GetGVal()) { neighbor->SetParent(currstate); neighbor->SetGVal(currstate->GetGVal() + actionSet[i].GetGCost()); } Open(neighbor); } } } } void GOAPSolver::PerformAStar(std::vector<GOAPAction>& actionList) { int maxAttempts = 10000; while(!foundEnd && maxAttempts > 0) { PerformOneStep(actionList); maxAttempts--; } } float GOAPSolver::ComputeHeuristic(GOAPState* state) { return end.ComputeNumDifferences(*state); } bool GOAPSolver::isInGenerated(GOAPState* s) { return (generatedStates.find(s) != generatedStates.end()); } GOAPState* GOAPSolver::GetGenerated(GOAPState* s) { return *(generatedStates.find(s)); } void GOAPSolver::AddToGenerated(GOAPState* s) { generatedStates.insert(s); } }