public PlannerNode(PlannerNode copyOf) { Parent = copyOf.Parent; RunningCost = copyOf.RunningCost; RunningData = copyOf.RunningData; Action = copyOf.Action; }
public void Execute() { Leaves = new List <PlannerNode>(); PlannerNode startNode = new PlannerNode(null, ApplyBeatToWorldState(State, StartBeat), StartBeat); if (Beats.Contains(StartBeat)) { Beats = BeatSubset(Beats, StartBeat); } if (BuildGraph(startNode, Beats)) { PlannerNode bestPlan = Leaves[0]; foreach (PlannerNode leaf in Leaves) { if (leaf.Cost < bestPlan.Cost) { bestPlan = leaf; } } NarrativePlan = new Plan(bestPlan, TargetBeat, Archetype); } }
public PlannerNode(PlannerNode parent, float runningCost, HashSet <KeyValuePair <string, object> > state, GOAPAction action) { Parent = parent; RunningCost = runningCost; _state = state; Action = action; }
private Path GeneratePath() { PlannerNode node = m_nodes[m_goal.Id]; if (node == null) { return(null); } Path path = new Path(node.G); List <INode> list = new List <INode>() { node.OriginNode }; while (node.Parent != null) { node = node.Parent; list.Add(node.OriginNode); if (node.OriginNode.Id == m_start.Id) { break; } } list.Reverse(); path.Nodes = list; return(path); }
private bool BuildGraph(PlannerNode parent, List <Beat> availableActions) { bool foundSolution = false; foreach (Beat beat in availableActions) { if (parent.State.IsInState(beat.Preconditions)) { PlannerNode node = new PlannerNode(parent, ApplyBeatToWorldState(parent.State, beat), beat); if (node.State.IsInState(TargetBeat.Preconditions)) { Leaves.Add(node); foundSolution = true; } else { if (BuildGraph(node, BeatSubset(availableActions, beat))) { foundSolution = true; } } } } return(foundSolution); }
private bool BuildGraph(PlannerNode parent, HashSet <PlannerNode> leaves, HashSet <GOAPAction> usableActions, HashSet <KeyValuePair <string, object> > goal) { bool foundOne = false; // go through each action available at this node and see if we can use it here foreach (GOAPAction action in usableActions) { // if the parent state has conditions for this action's preconditions, use it here if (InState(action.Preconditions, parent._state)) { // apply action's effects to parent state HashSet <KeyValuePair <string, object> > currentState = PopulateState(parent._state, action.Effects); PlannerNode node = new PlannerNode(parent, parent.RunningCost + action.Cost, currentState, action); if (InState(goal, currentState)) { // found solution! leaves.Add(node); foundOne = true; } else { // not at a solution yet, so test all remaining actions and branch out the tree HashSet <GOAPAction> subset = ActionSubset(usableActions, action); bool found = BuildGraph(node, leaves, subset, goal); if (found) { foundOne = true; } } } } return(foundOne); }
public static Stack <BaseAction> ConstructActionPlan(Actor actor, Goal goal, List <BaseAction> performableActions) { List <PlannerNode> treeGraph = new List <PlannerNode>(); PlannerNode start = new PlannerNode() { RunningData = actor.Data }; bool success = BuildGraph(start, treeGraph, goal, performableActions); if (!success) { // Plan was not created -- sad. UnityEngine.Debug.Log("Plan failed"); return(new Stack <BaseAction>()); } PlannerNode cheapest = null; foreach (var rootNode in treeGraph) { if (cheapest == null) { cheapest = rootNode; } else { if (rootNode.RunningCost < cheapest.RunningCost) { cheapest = rootNode; } } } // Get the cheapest node and work back through parents to construct action plan var actionPlan = new Stack <BaseAction>(); PlannerNode n = cheapest; while (n != null) { if (n.Action != null) { actionPlan.Push(n.Action); } n = n.Parent; } //Debug var debugString = "I made a plan! "; foreach (var action in actionPlan) { debugString += action.name + " - "; } UnityEngine.Debug.Log(debugString); return(actionPlan); }
IEnumerator ProcessList(SearchNode _start, SearchNode _goal) { // Prioritize the tile with the lowest cost int lowestIndex = getLowestFinal(stepOpen); PlannerNode current = stepOpen[lowestIndex]; // Remove it from the stepOpen list stepOpen.Remove(stepOpen[lowestIndex]); // If this is where we want to be if (current.vertex == _goal) { while (current.parent != null) { // Add every node along this path to the list path.Add(current.vertex.t); yield return(new WaitForSeconds(1)); current.vertex.t.setTemporaryColor(Color.yellow); current = current.parent; } // Return the list yield return("success"); } // If this isn't the goal, get its neighbors foreach (Edge e in current.vertex.edges) { SearchNode successor = e.node; // Calculate cost float tempGiven = current.givenCost + e.cost; // If we've been here before if (stepVisited.ContainsKey(successor)) { // If this previously stepVisited tile is now a better choice based on our current path if (tempGiven < stepVisited[successor].givenCost) { // Remove the old reference, update costs, and return to the list with new priority. PlannerNode successorNode = new PlannerNode(successor); stepOpen.Remove(successorNode); successorNode.givenCost = tempGiven; successorNode.finalCost = successorNode.givenCost + successorNode.heuristicCost * heuristicWeight; successorNode.parent = current; stepOpen.Add(successorNode); } } else { // If we haven't been here before, add it to the stepVisited list PlannerNode successorNode = new PlannerNode(successor); successorNode.givenCost = tempGiven; successorNode.heuristicCost = calculateCost(successor, _goal); successorNode.finalCost = successorNode.givenCost + successorNode.heuristicCost * heuristicWeight; successorNode.parent = current; stepVisited[successor] = successorNode; stepOpen.Add(successorNode); } } }
public Path Search(INode start, INode goal) { m_start = start; m_goal = goal; PlannerNode startNode = new PlannerNode(start, 0, m_map.CalcHeuristic(start, goal), NodeStatus.Open); m_nodes[start.Id] = startNode; m_open.Enqueue(startNode, startNode.F); while (m_open.Count > 0) { PlannerNode curtNode = m_open.Dequeue(); if (curtNode.OriginNode.Id == m_goal.Id) { break; } curtNode.Status = NodeStatus.Close; List <INode> successors = m_map.GetSuccessors(curtNode.OriginNode); for (int i = 0; i < successors.Count; i++) { INode successor = successors[i]; if (m_limitRect != null && !m_limitRect.Value.Contains(successor.Pos)) { continue; } PlannerNode node = m_nodes[successor.Id]; if (node != null) { if (node.Status != NodeStatus.Close) { float oldG = node.G; float newG = curtNode.G + m_map.CalcHeuristic(curtNode.OriginNode, successor); if (newG < node.G) { node.G = newG; node.Parent = curtNode; } } } else { float g = curtNode.G + m_map.CalcHeuristic(curtNode.OriginNode, successor); float h = m_map.CalcHeuristic(successor, m_goal); node = new PlannerNode(successor, g, h, NodeStatus.Open); node.Parent = curtNode; m_nodes[successor.Id] = node; m_open.Enqueue(node, node.F); } } } return(GeneratePath()); }
public List <PlannerNode <TKey, TValue> > GetNeighbours(PlannerNode <TKey, TValue> _baseNode) { m_NeighbourNodes.Clear(); WorldStateModifier <TKey, TValue> basePreconditions = _baseNode.Action?.GetPreconditions(); WorldState <TKey, TValue> worldStateAtBaseNode = _baseNode.GetWorldStateAtNode(); Goal <TKey, TValue> goalWorldStateAtBaseNode = _baseNode.GetGoalWorldStateAtNode(); WorldState <TKey, TValue> remainingGoals = goalWorldStateAtBaseNode - worldStateAtBaseNode; // We implement regressive A* for GOAP by chaining action preconditions to action effects until all preconditions are fulfilled. // For this planner, neighbours are nodes that fulfil any of the remaining goals of the base node without conflicting with any of its preconditions. foreach (IAction <TKey, TValue> possibleAction in m_CurrentAgent.GetActionSet()) { var effects = possibleAction.GetEffects(); if (!effects.ContainsAny(remainingGoals)) { continue; } if (effects.ConflictsAny(basePreconditions)) { continue; } PlannerNode <TKey, TValue> plannerNode = GetAvailablePlannerNode(); plannerNode.Init(possibleAction); plannerNode.CalculateWorldStateAtNode(worldStateAtBaseNode); plannerNode.CalculateGoalWorldStateAtNode(goalWorldStateAtBaseNode); bool foundInUsed = false; foreach (PlannerNode <TKey, TValue> usedNode in m_UsedNodes) { if (usedNode.Equals(plannerNode)) { plannerNode = usedNode; foundInUsed = true; break; } } m_NeighbourNodes.Add(plannerNode); if (!foundInUsed) { m_UsedNodes.Add(plannerNode); } } return(m_NeighbourNodes); }
public GoalPlanPair <TKey, TValue> GenerateGoalPlanPairForAgent(IAgent <TKey, TValue> _agent) { m_CurrentAgent = _agent; Stack <PlannerNode <TKey, TValue> > foundPath = null; bool newGoalBetterThanActiveGoal = false; m_GoalPlanPair.GoalInstance = null; m_GoalPlanPair.PlanInstance = null; for (var goalQueue = m_CurrentAgent.CalculateSortedGoals(); goalQueue.Count > 0;) { SortableGoal <TKey, TValue> goal = goalQueue.Dequeue(); PlannerNode <TKey, TValue> agentNode = new PlannerNode <TKey, TValue>(); agentNode.CalculateWorldStateAtNode(_agent.GetWorkingMemory()); agentNode.CalculateGoalWorldStateAtNode(goal); foundPath = m_Pathfinder.FindPath(agentNode, null); if (foundPath != null && foundPath.Count > 0) { newGoalBetterThanActiveGoal = _agent.IsGoalBetterThanActiveGoal(goal); if (newGoalBetterThanActiveGoal) { m_GoalPlanPair.GoalInstance = goal; #if UNITY_EDITOR _agent.CreatePlanHierarchy(foundPath.Last(), m_Pathfinder.GetOpenNodes(), m_Pathfinder.GetClosedNodes()); #endif } break; } } m_GoalPlanPair.PlanInstance = new Plan <TKey, TValue>(); if (newGoalBetterThanActiveGoal && foundPath != null) { foreach (PlannerNode <TKey, TValue> pathNode in foundPath) { if (pathNode.Action != null) { m_GoalPlanPair.PlanInstance.Push(pathNode.Action); } } } m_UsedNodes.Clear(); RecycleUsedNodes(); return(m_GoalPlanPair); }
public static void CreateNodePairForLastOpenNode <TKey, TValue>(ref PlannerNode <TKey, TValue> _lastPlannerNode, ref List <PlannerHierarchyNodePair <TKey, TValue> > _nodePairs) { var lastHierarchyNode = new HierarchyNode <TKey, TValue>( _isPartOfFoundPath: true, _executableAction: _lastPlannerNode.Action as ExecutableAction <TKey, TValue>, _goalWorldStateAtNode: _lastPlannerNode.GetGoalWorldStateAtNode(), _worldStateAtNode: _lastPlannerNode.GetWorldStateAtNode(), _isFinalNode: true, _isClosed: false); _nodePairs.Add(new PlannerHierarchyNodePair <TKey, TValue>() { PlannerNodeInstance = _lastPlannerNode, HierarchyNodeInstance = lastHierarchyNode }); }
public PlannerNode(PlannerNode parent, WorldState state, Beat beat) { Parent = parent; if (parent != null) { Cost = parent.Cost + beat.Cost; } else { Cost = beat.Cost; } State = state; NodeBeat = beat; }
public Plan(PlannerNode plan, Beat TargetBeat, PlayerArchetype archetype) { Archetype = archetype; Beats = new List <Beat>(); while (plan != null) { Beats.Add(plan.NodeBeat); plan = plan.Parent; } //Doesn't get added during the planning process, so add it here Beats.Add(TargetBeat); CalculateScore(); }
public void CreatePlanHierarchy(PlannerNode <TKey, TValue> _finalPathNode, IEnumerable <PlannerNode <TKey, TValue> > _openNodes, IEnumerable <PlannerNode <TKey, TValue> > _closedNodes) { List <PlannerHierarchyNodePair <TKey, TValue> > nodePairs = new List <PlannerHierarchyNodePair <TKey, TValue> >(); PuppeteerRuntimeHelper.CreateNodePairsForClosedNodes(_closedNodes, ref nodePairs); var goalNodePair = nodePairs.Find(_entry => _entry.PlannerNodeInstance.GetParent() == null); m_RootHierarchyNode = goalNodePair?.HierarchyNodeInstance; PuppeteerRuntimeHelper.CreateNodePairsForOpenNodes(_openNodes, ref nodePairs); PuppeteerRuntimeHelper.CreateNodePairForLastOpenNode(ref _finalPathNode, ref nodePairs); var lastHierarchyNode = nodePairs.Last().HierarchyNodeInstance; PuppeteerRuntimeHelper.ConstructHierarchy(ref nodePairs); PuppeteerRuntimeHelper.SetPartOfFoundPathRecursive(lastHierarchyNode); }
/// <summary> /// Based on the given parent, we construct a graph branching from it. /// Returns true if ANY plan was found. /// </summary> /// <param name="parent"></param> /// <param name="graphRef"></param> /// <param name="goal"></param> /// <param name="performableActions"></param> /// <returns></returns> private static bool BuildGraph(PlannerNode parent, List <PlannerNode> graphRef, Goal goal, List <BaseAction> performableActions) { bool planFound = false; foreach (var action in performableActions) { PlannerNode actualBatman = new PlannerNode(parent); if (IsConditionsMet(action.Conditions, actualBatman.RunningData)) { // Apply the results of the action as if completed successfully. SubjectData resultantData = CopyActionResults(actualBatman.RunningData, action.Results); PlannerNode node = new PlannerNode() { Parent = actualBatman, RunningCost = actualBatman.RunningCost + (int)action.Difficulty, RunningData = resultantData, Action = action }; if (IsConditionsMet(goal.DesiredConditions, resultantData)) { // A solution has been constructed; graphRef.Add(node); planFound = true; } else { // Solution not yet found, test remaining actions List <BaseAction> subsetActions = CreateActionSubset(performableActions, action); planFound = BuildGraph(node, graphRef, goal, subsetActions); } } } return(planFound); }
// TODO: Needs to be refactored to work only on the tiles returned by moveRange. void Astar(SearchNode _start, SearchNode _goal) { // Start fresh deselectPath(); List <PlannerNode> open = new List <PlannerNode>(); Dictionary <SearchNode, PlannerNode> visited = new Dictionary <SearchNode, PlannerNode>(); // Start the list with the start node open.Add(new PlannerNode(_start)); // We've been to the start node visited[_start] = open[0]; // TODO: Figure out which costs are being used and how. Currently functional, but slightly blackboxed. visited[_start].givenCost = 0; visited[_start].heuristicCost = calculateCost(_start, _goal); visited[_start].finalCost = visited[_start].givenCost + visited[_start].heuristicCost * heuristicWeight; // While there are tiles left to explore while (open.Count != 0) { // Prioritize the tile with the lowest cost int lowestIndex = getLowestFinal(open); PlannerNode current = open[lowestIndex]; // Remove it from the open list open.Remove(open[lowestIndex]); // If this is where we want to be if (current.vertex == _goal) { while (current.parent != null) { // Add every node along this path to the list path.Add(current.vertex.t); current = current.parent; } // Return the list return; } // If this isn't the goal, get its neighbors foreach (Edge e in current.vertex.edges) { SearchNode successor = e.node; // Calculate cost float tempGiven = current.givenCost + e.cost; // If we've been here before if (visited.ContainsKey(successor)) { // If this previously visited tile is now a better choice based on our current path if (tempGiven < visited[successor].givenCost) { // Remove the old reference, update costs, and return to the list with new priority. PlannerNode successorNode = new PlannerNode(successor); open.Remove(successorNode); successorNode.givenCost = tempGiven; successorNode.finalCost = successorNode.givenCost + successorNode.heuristicCost * heuristicWeight; successorNode.parent = current; open.Add(successorNode); } } else { // If we haven't been here before, add it to the visited list PlannerNode successorNode = new PlannerNode(successor); successorNode.givenCost = tempGiven; successorNode.heuristicCost = calculateCost(successor, _goal); successorNode.finalCost = successorNode.givenCost + successorNode.heuristicCost * heuristicWeight; successorNode.parent = current; visited[successor] = successorNode; open.Add(successorNode); } } } // The list is empty, return from procedure. return; }
public Queue <GOAPAction> Plan(GameObject agent, HashSet <GOAPAction> availableActions, HashSet <KeyValuePair <string, object> > worldState, HashSet <KeyValuePair <string, object> > goal) { foreach (GOAPAction a in availableActions) { a.DoReset(); } // check which actions can run var usableActions = new HashSet <GOAPAction>(); foreach (GOAPAction a in availableActions) { if (a.CheckProceduralPrecondition(agent)) { usableActions.Add(a); } } // we now have all actions that can run, stored in usableActions // build tree and record leaf nodes that provide solution to goal HashSet <PlannerNode> leaves = new HashSet <PlannerNode>(); // build tree PlannerNode start = new PlannerNode(null, 0, worldState, null); bool success = BuildGraph(start, leaves, usableActions, goal); if (!success) { // no plan created return(null); } // get cheapest leaf PlannerNode cheapest = null; foreach (PlannerNode l in leaves) { if (cheapest == null) { cheapest = l; } else { if (l.RunningCost < cheapest.RunningCost) { cheapest = l; } } } // get its node and work back through parents List <GOAPAction> result = new List <GOAPAction>(); PlannerNode n = cheapest; while (n != null) { if (n.Action != null) { // slide action in the front result.Insert(0, n.Action); } n = n.Parent; } // we now have this action HashSet in correct order Queue <GOAPAction> queue = new Queue <GOAPAction>(); foreach (GOAPAction a in result) { queue.Enqueue(a); } return(queue); }
public float GetDistance(PlannerNode <TKey, TValue> _base, PlannerNode <TKey, TValue> _end) { /// GetNeighbours for this planner already filters out all the nodes that /// don't get us closer to the end so we simply return the heuristic cost of the node return(_base.GetHeuristicCost()); }