public BrachNode(BrachNode _parent, float _runningCost, HashSet <KeyValuePair <string, object> > _state, GOAPAction _action) { parent = _parent; runningCost = _runningCost; state = _state; action = _action; }
/// <summary> /// Returns true if at least one solution was found. /// The possible paths are stored in the leaves list. Each leaf has a /// 'runningCost' value where the lowest cost will be the best action /// sequence. /// </summary> /// <param name="parent"></param> /// <param name="leaves"></param> /// <param name="usableActions"></param> /// <param name="goal"></param> /// <returns></returns> private bool BuildGraph(BrachNode parent, List <BrachNode> leaves, HashSet <GOAPAction> usableActions, HashSet <KeyValuePair <string, object> > goal) { bool foundOne = false; // go through each action available at this BrachNode and see if we can use it here foreach (GOAPAction action in usableActions) { // if the parent state has the conditions for this action's preconditions, we can use it here if (InState(action.Preconditions, parent.state)) { // apply the action's effects to the parent state HashSet <KeyValuePair <string, object> > currentState = PopulateState(parent.state, action.Effects); //Debug.Log(GoapAgent.prettyPrint(currentState)); BrachNode BrachNode = new BrachNode(parent, parent.runningCost + action.cost, currentState, action); if (InState(goal, currentState)) { // we found a solution! leaves.Add(BrachNode); foundOne = true; } else { // not at a solution yet, so test all the remaining actions and branch out the tree HashSet <GOAPAction> subset = ActionSubset(usableActions, action); bool found = BuildGraph(BrachNode, leaves, subset, goal); if (found) { foundOne = true; } } } } return(foundOne); }
/// <summary> /// Plan what sequence of actions can fulfill the goal. /// Returns null if a plan could not be found, or a list of the actions /// that must be performed, in order, to fulfill the goal. /// </summary> /// <param name="agent"></param> /// <param name="availableActions"></param> /// <param name="worldState"></param> /// <param name="goal"></param> /// <returns></returns> public Queue <GOAPAction> Plan(GameObject agent, HashSet <GOAPAction> availableActions, HashSet <KeyValuePair <string, object> > worldState, HashSet <KeyValuePair <string, object> > goal) { // reset the actions so we can start fresh with them foreach (GOAPAction a in availableActions) { a.DoReset(); } // check what actions can run using their checkProceduralPrecondition HashSet <GOAPAction> usableActions = new HashSet <GOAPAction>(); foreach (GOAPAction a in availableActions) { if (a.CheckProcPreconditions(agent)) { usableActions.Add(a); } } // we now have all actions that can run, stored in usableActions // build up the tree and record the leaf BrachNodes that provide a solution to the goal. List <BrachNode> leaves = new List <BrachNode>(); // build graph BrachNode start = new BrachNode(null, 0, worldState, null); bool success = BuildGraph(start, leaves, usableActions, goal); if (!success) { // oh no, we didn't get a plan // Debug.LogWarning("NO plan was found for " + agent.name); return(null); } // get the cheapest leaf BrachNode cheapest = null; foreach (BrachNode leaf in leaves) { if (cheapest == null) { cheapest = leaf; } else { if (leaf.runningCost < cheapest.runningCost) { cheapest = leaf; } } } // get its BrachNode and work back through the parents List <GOAPAction> result = new List <GOAPAction>(); BrachNode n = cheapest; while (n != null) { if (n.action != null) { result.Insert(0, n.action); // insert the action in the front } n = n.parent; } // we now have this action list in correct order Queue <GOAPAction> queue = new Queue <GOAPAction>(); foreach (GOAPAction a in result) { queue.Enqueue(a); } // hooray we have a plan! return(queue); }