// Create possible previous goal (before this actionwas made) public WorldGoal reverseApplyToWorldGoal(WorldGoal goal) { WorldGoal newGoal = new WorldGoal(goal); //this is deep copied, see c'tor foreach (KeyValuePair <IStateful, Goal> agentGoal in goal) { GoapAgent currAgent = (GoapAgent)agentGoal.Key; Goal currGoal = agentGoal.Value; Goal updatedGoal = this.effects.reverseApplyEffectsToGoal(currGoal); List <string> keys = new List <string> (updatedGoal.Keys); foreach (string k in keys) { if (updatedGoal [k] == null) { updatedGoal.Remove(k); } } if (newGoal.ContainsKey(currAgent)) { newGoal [currAgent] = updatedGoal; } else { newGoal.Add(currAgent, updatedGoal); } // Add target preconditions to new goal foreach (KeyValuePair <string, Condition> kvp in this.preconditions) { if (!newGoal [currAgent].ContainsKey(kvp.Key)) { newGoal [currAgent].Add(kvp.Key, new Condition(kvp.Value.comparison, kvp.Value.value)); } } } return(newGoal); }
public bool isGoalCloser(WorldGoal currentGoal, WorldGoal possibleGoal) { bool res = false; foreach (KeyValuePair <IStateful, Goal> agentGoal in possibleGoal) { Goal possible = agentGoal.Value; // TODO: what if possible goal doesnt contain this person if (!currentGoal.ContainsKey(agentGoal.Key)) { continue; } else { Goal current = currentGoal [agentGoal.Key]; //go through parent node's conditions foreach (KeyValuePair <string, Condition> kvp in current) { // If initial state without this parameter, irrelevant and continue if (this.ContainsKey(kvp.Key)) { // If child node doesnt contain this goal, it is satisfied! if (!possible.ContainsKey(kvp.Key)) { res = true; } // Else - check if child goal is closer to inital state else { StateValue sv = this [kvp.Key]; Condition currCond = kvp.Value; Condition possCond = possible [kvp.Key]; //if int check if new closer to wanted if (sv.value.GetType() == typeof(int)) { if (sv.CheckCondition(currCond) && sv.CheckCondition(possCond)) { continue; } if (sv.CheckCondition(currCond) && !sv.CheckCondition(possCond)) { return(false); } if (!sv.CheckCondition(currCond) && sv.CheckCondition(possCond)) { res = true; continue; } //now both conditions dont hold, check if possCond improves //assume both goals have same compare type switch (currCond.comparison) { case CompareType.Equal: res = (Mathf.Abs((int)sv.value - (int)currCond.value)) < (Mathf.Abs((int)sv.value - (int)possCond.value)); break; case CompareType.LessThan: res = ((int)possCond.value - (int)sv.value) > ((int)currCond.value - (int)sv.value); break; case CompareType.LessThanOrEqual: res = ((int)possCond.value - (int)sv.value) > ((int)currCond.value - (int)sv.value); break; case CompareType.MoreThan: res = ((int)possCond.value - (int)sv.value) < ((int)currCond.value - (int)sv.value); break; case CompareType.MoreThanOrEqual: res = ((int)possCond.value - (int)sv.value) < ((int)currCond.value - (int)sv.value); break; case CompareType.NotEqual: res = (Mathf.Abs((int)sv.value - (int)currCond.value)) > (Mathf.Abs((int)sv.value - (int)possCond.value)); break; } //if bool check if new matches while old doesnt } else if (sv.value.GetType() == typeof(bool)) { //assuming booleans will only get ModificationType.Set res = (sv.value != currCond.value && sv.value == possCond.value); } //if new cond worse - return false if (res == false) { //Debug.Log ("this Goal isnt any better! abort!"); return(false); } } } } } } if (res == false) { //Debug.Log ("this Goal isnt any better! abort!"); } else { //Debug.Log ("this Goal is better!"); } return(res); }
/// <summary> /// A* forward search for a plan that satisfies the given goal. /// </summary> /// <returns>Returns null if a plan could not be found, or a list of the /// actions that must be performed, in order.</returns> public static Queue <GoapAction.WithContext> Plan( GoapAgent agent, List <GoapAction> availableActions, WorldGoal goal) { var exploredNodes = new Dictionary <WorldState, Node> (WorldStateComparer.instance); var closedSet = new HashSet <WorldState> (WorldStateComparer.instance); var openSet = new FastPriorityQueue <Node> (MAX_FRINGE_NODES); var currentNode = Node.pool.Borrow(); currentNode.Init(null, 0f, goal, null, null); // currentNode.position = Vector2.zero; currentNode.position = (agent as Component).transform.position; openSet.Enqueue(currentNode, 0f); int iterations = 0; while (openSet.Count > 0 && iterations < MAX_FRINGE_NODES - 10) { iterations++; currentNode = openSet.Dequeue(); //TODO: delete print //Debug.Log ("current world goal: " + GoapAgent.PrettyPrint(currentNode.goal)); if (DoConditionsApply(currentNode.goal[agent], agent.GetState())) { DebugUtils.Log("Selected plan with cost: " + currentNode.Score); var plan = UnwrapPlan(currentNode); //DebugUtils.Log("the plan: " + GoapAgent.PrettyPrint(plan)); // Return all nodes. Node.pool.ReturnAll(); // Check for leaks in the pools: //DebugUtils.LogError("Nodes: " + Node.pool.Count); //DebugUtils.LogError("WithContext: " + GoapAction.WithContext.pool.Count); //DebugUtils.LogError("WorldState: " + WorldState.pool.Count); return(plan); } foreach (GoapAction action in availableActions) { //TODO: delete print //Debug.Log("considering " + action.name); WorldGoal possibleChildGoal = action.reverseApplyToWorldGoal(currentNode.goal); //Debug.Log ("new goal will be: " + GoapAgent.PrettyPrint(possibleChildGoal)); if (agent.GetState().isGoalCloser(currentNode.goal, possibleChildGoal)) { List <IStateful> targets = action.GetAllTargets(agent); // No targets, move to next action if (targets.Count == 0) { continue; } float minCost = 99999f; float tempCost = 0f; IStateful closestTarget = null; Vector2 newPosition = currentNode.position; foreach (var target in targets) { //DebugUtils.Log ("targets..."); //TODO: check target preconds, make sure this works if (goal.ContainsKey(target)) { if (!DoConditionsApply(goal [target], target.GetPerceivedState())) { continue; } } if (action.RequiresInRange()) { tempCost = action.CalculateCost(currentNode.position, target); if (tempCost < minCost) { minCost = tempCost; closestTarget = target; newPosition = (target as Component).transform.position; DebugUtils.Log("minCost: " + minCost); DebugUtils.Log("closestTarget: " + closestTarget); } //DebugUtils.Log ("calculating tempCost"); } else { closestTarget = target; tempCost = 9999; DebugUtils.Log("+++ closestTarget: " + closestTarget); break; } } float cost = currentNode.runningCost + action.workDuration + tempCost; Node newChiledNode = Node.pool.Borrow(); DebugUtils.Log("*** closestTarget: " + closestTarget); newChiledNode.Init(currentNode, cost, possibleChildGoal, action, closestTarget); newChiledNode.position = newPosition; openSet.Enqueue(newChiledNode, newChiledNode.runningCost); } //TODO: delete 'else' scope else { //DebugUtils.Log (action.name + " doesnt improve goal"); } } } //TODO: return plan failed #yoel return(null); }