コード例 #1
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Initiate a new plan
    /// </summary>
    /// <param name="agent">Agent that is planning</param>
    /// <param name="goal">Agent's goal</param>
    /// <param name="currentWorldState">Agent's current worldstate</param>
    /// <param name="availableActions">Agent's available actions</param>
    /// <returns></returns>
    public static Queue <GOAP_Action> Plan(GOAP_Agent agent, List_GOAP_Worldstate goal, List_GOAP_Worldstate currentWorldState, List <string> availableActions)
    {
        plannerLog = "<color=#0000cc> <b>PLANNING</b>: " + agent.Character.characterData.characterName + "</color>\n";

        //Search for a valid plan
        Node startNode = WhileBuild(goal, availableActions, currentWorldState, agent);

        plannerLog += "\n";

        //Return null if you couldn't find a plan!
        if (startNode == null)
        {
            plannerLog += "<color=#cc0000>Couldn't find actions fulfilling " + agent.Character.characterData.characterName + "s goal.</color>\n";

            Debug.Log(plannerLog);
            return(null);
        }
        //Also return null, if the startnode is the goalNode
        if (startNode.action == null && agent.activeQuest == null)
        {
            plannerLog += "Plan has already been fulfilled.\n";

            Debug.Log(plannerLog);
            return(null);
        }
        plannerLog += "<color=#00cc00>" + agent.Character.characterData.characterName + "found plan:</color>\n";

        //Otherwise return the queue
        return(MakeQueue(startNode, agent));
    }
コード例 #2
0
 public QuestData(GOAP_Agent owner)
 {
     this.owner     = owner;
     requiredStates = new List_GOAP_Worldstate();
     providedStates = new List_GOAP_Worldstate();
     reward         = 0;
 }
コード例 #3
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
 public Node(Node parent, List_GOAP_Worldstate required, GOAP_Action action, float estimatedPathCost)
 {
     this.parent            = parent;
     this.estimatedPathCost = estimatedPathCost;
     this.required          = required;
     this.action            = action;
 }
コード例 #4
0
 public VariationData()
 {
     RequiredWorldstates = new List_GOAP_Worldstate();
     SatisfyWorldstates  = new List_GOAP_Worldstate();
     workCost            = 1f;
     range           = 1f;
     benefitingSkill = Skills.None;
 }
コード例 #5
0
 public VariationData(List_GOAP_Worldstate required, List_GOAP_Worldstate satisfy, float workCost = 1, float range = 1, Skills benefitingSkill = Skills.None)
 {
     RequiredWorldstates  = required;
     SatisfyWorldstates   = satisfy;
     this.workCost        = workCost;
     this.range           = range;
     this.benefitingSkill = benefitingSkill;
 }
コード例 #6
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Checks if the goal worldstate is already satisfied within the current worldstate
    /// </summary>
    /// <param name="currentWorldState"></param>
    /// <param name="goalWorldState"></param>
    /// <returns></returns>
    public static bool IsGoalSatisfied(List_GOAP_Worldstate currentWorldState, GOAP_Worldstate goalWorldState)
    {
        //First, check if we have not already reached the goal, by checking it against our currentWorldstate

        if (!currentWorldState.ContainsExactly(goalWorldState))
        {
            return(false);
        }
        return(true);
    }
コード例 #7
0
 public GOAP_Quest(GOAP_Agent agent, List_GOAP_Worldstate required, List_GOAP_Worldstate provided)
 {
     id        = count++;
     questData = new QuestData
     {
         owner          = agent,
         requiredStates = new List_GOAP_Worldstate(required),
         providedStates = new List_GOAP_Worldstate(provided)
     };
 }
コード例 #8
0
 public bool IsSatisfiedInCurrentWorldstate(List_GOAP_Worldstate worldstates)
 {
     for (int i = 0; i < worldstates.Count; i++)
     {
         if (!currentWorldstates.ContainsExactly(worldstates[i]))
         {
             return(false);
         }
     }
     return(true);
 }
コード例 #9
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Generates a Node containing a PostQuest action from the activeNodes required worldstates
    /// </summary>
    /// <param name="activeNode"></param>
    /// <param name="planningWorldState"></param>
    /// <param name="agent"></param>
    /// <returns></returns>
    private static Node GenerateQuestNode(Node activeNode, List_GOAP_Worldstate planningWorldState, GOAP_Agent agent)
    {
        List_GOAP_Worldstate newRequired = new List_GOAP_Worldstate();
        Action_PostQuest     action      = new Action_PostQuest();

        foreach (GOAP_Worldstate state in activeNode.required)
        {
            //Debug.Log("Adding state " + state.ToString() + " to quest");
            action.AddQuestWorldstate(state);
        }
        float estimatedQuestCost = action.ActionCost * activeNode.required.Count * heuristicFactor;

        return(new Node(activeNode, newRequired, action, estimatedQuestCost + activeNode.estimatedPathCost));
    }
コード例 #10
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Combines current and goal worldstate
    /// </summary>
    /// <param name="currentWorldState"></param>
    /// <param name="goalWorldState"></param>
    /// <returns></returns>
    private static Node GetGoalNode(List_GOAP_Worldstate currentWorldState, List_GOAP_Worldstate goalWorldState)
    {
        List_GOAP_Worldstate newRequired = new List_GOAP_Worldstate(goalWorldState);

        plannerLog += "Found Goal:\n";
        for (int state = 0; state < goalWorldState.Count; state++)
        {
            plannerLog += goalWorldState[state].ToString() + "\n";
            if (currentWorldState.ContainsExactly(goalWorldState[state]))
            {
                newRequired.Remove(goalWorldState[state]);
            }
        }
        plannerLog += "\n";
        return(new Node(null, newRequired, null, 0));
    }
コード例 #11
0
    public GOAP_Agent(GOAP_Character character, IGOAP_AgentView view)
    {
        this.character = character;
        View           = view;

        checkedCharacterGoals = new List_GOAP_Worldstate();

        currentActions     = new Queue <GOAP_Action>();
        currentWorldstates = new List_GOAP_Worldstate();
        checkedQuestIds    = new List <int>();

        postedQuestIDs    = new List <int>();
        completedQuestIDs = new List <int>();

        questPlans = new Dictionary <int, Queue <GOAP_Action> >();
        activeGoal = new List_GOAP_Worldstate();

        planMemory = new List <PlanInfo>();

        timeSincePlanned = Random.Range(0, planningWaitTimer);
    }
コード例 #12
0
 /// <summary>
 /// This is a deepCopy constructor
 /// </summary>
 /// <param name="list"></param>
 public List_GOAP_Worldstate(List_GOAP_Worldstate list) : this(list.ToArray())
 {
 }
コード例 #13
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Tries to apply the action onto the activeNode to see if it results in a valid neighbor
    /// </summary>
    /// <param name="activeNode"></param>
    /// <param name="action"></param>
    /// <param name="planningWorldState">worldstate at the current stage of planning</param>
    /// <param name="agent">currently planning agent</param>
    /// <returns></returns>
    private static List <Node> GetValidNeighborNodeVariations(Node activeNode, GOAP_Action action, List_GOAP_Worldstate planningWorldState, GOAP_Agent agent)
    {
        if (action == null)
        {
            return(null);
        }

        List <Node> nodes = new List <Node>();

        if (action.HasVariations())
        {
            for (int i = 0; i < action.variations.Count; i++)
            {
                if (action.variations[i] != null)
                {
                    nodes.Add(GetValidNeighborNode(activeNode, action.GetVariation(i), planningWorldState, agent));
                }
            }
        }
        else
        {
            nodes.Add(GetValidNeighborNode(activeNode, action, planningWorldState, agent));
        }

        return(nodes);
    }
コード例 #14
0
 protected void Init()
 {
     RequiredWorldstates = new List_GOAP_Worldstate();
     SatisfyWorldstates  = new List_GOAP_Worldstate();
     variations          = new List <VariationData>();
 }
コード例 #15
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    private static Node GetValidNeighborNode(Node activeNode, GOAP_Action action, List_GOAP_Worldstate planningWorldState, GOAP_Agent agent)
    {
        bool isUsefulAction = false;

        List_GOAP_Worldstate newRequired = new List_GOAP_Worldstate(activeNode.required);

        //Actions need to fulfill at least one required Worldstate to result in a valid neighbor
        foreach (GOAP_Worldstate state in activeNode.required)
        {
            if (action.SatisfyWorldstates.ContainsExactly(state))
            {
                if (state.key == WorldStateKey.bWasFieldTended && state.value == 0)
                {
                    Debug.LogError(action.SatisfyWorldstates.ToString());
                }
                newRequired.Remove(state);
                isUsefulAction = true;
            }
        }

        //if this action does not help the plan, return null
        if (!isUsefulAction)
        {
            return(null);
        }

        //If the actions proceduralConditions are not met, we can't perform it anyways
        //if (!action.CheckProceduralConditions(agent)) return null;

        //add the actions own required worldstates to the Node
        foreach (GOAP_Worldstate state in action.RequiredWorldstates)
        {
            if (!planningWorldState.ContainsExactly(state))
            {
                //If the state is an observable one and the agent does not have any memory of it, they just assume that it is in their favor
                if (state.IsObservableState && !agent.currentWorldstates.ContainsKey(state))
                {
                    Debug.Log("<color=#cc00cc>" + agent.Character.characterData.characterName + "</color> assumes state:" + state.ToString());
                    agent.ChangeCurrentWorldState(state);
                }
                else
                {
                    newRequired.Add(state);
                }
            }
        }

        //Apply skillmodification onto the neighbor if it is valid
        float skillModifier = 1f;

        if (action.BenefitingSkill != Skills.None)
        {
            GOAP_Skill skill = agent.Character.characterData.skills.Find(x => x.id == action.BenefitingSkill);
            if (skill != null)
            {
                //If the character is actually skilled in this action, adjust the skillmodifier
                skillModifier /= skill.level;
            }
            else
            {
                //If the character is not skilled in this action, the skillmodifier is set to 5. This only comes into play, when global knowledge planning is used.
                skillModifier = 1f;
            }
        }

        //Change the skillmodifier on the action
        action.ApplySkillModifier(skillModifier);

        return(new Node(activeNode, newRequired, action, newRequired.Count * heuristicFactor + action.ActionCost + activeNode.estimatedPathCost));
    }
コード例 #16
0
 public void Init()
 {
     requiredStates = new List_GOAP_Worldstate();
     providedStates = new List_GOAP_Worldstate();
 }
コード例 #17
0
ファイル: GOAP_Planner.cs プロジェクト: Sushiy/Master_Thesis
    /// <summary>
    /// Perform an A*-search to find a valid plan
    /// </summary>
    /// <param name="goal">Agent's goal</param>
    /// <param name="availableActions">Agent's available actions</param>
    /// <param name="currentWorldState">Agent's current worldstate</param>
    /// <param name="agent">Agent that wants a plan</param>
    /// <returns>the starting node, if one was found, else null</returns>
    private static Node WhileBuild(List_GOAP_Worldstate goal, List <string> availableActions, List_GOAP_Worldstate currentWorldState, GOAP_Agent agent)
    {
        //Pick the latest planInfo
        PlanInfo planInfo      = agent.planMemory[agent.planMemory.Count - 1];
        int      currentNodeID = 0;

        Node current = null;

        List <Node> openSet = new List <Node>();

        //Get a Node for the goal
        Node goalNode = GetGoalNode(currentWorldState, goal);

        goalNode.id = currentNodeID;
        planInfo.AddNode(goalNode.id, -1, goalNode.required.ToString(), "None", 0f);

        //If the goal is already fulfilled, return the goal
        if (goalNode.required.Count == 0)
        {
            plannerLog += "Goal was already fulfilled\n";
            return(goalNode);
        }

        plannerLog += "Starting reverse planning: \n\n";
        //Add the goal as first node
        openSet.Add(goalNode);


        int graphDepth = 0;

        //Reverse A*
        while (openSet.Count > 0)
        {
            if (graphDepth >= 20)
            {
                Debug.LogError("Plan must be recursive and has exceeded 20 iterations");
                break;
            }
            //Sort the open set by pathcosts and pick the best node
            openSet.Sort();
            string last = "";
            if (current != null)
            {
                last = current.ToString();
            }
            current = openSet[0];

            //Debug Log to visualize the process
            string openSetString = "";
            for (int i = 0; i < openSet.Count; i++)
            {
                openSetString += "\n" + makeIndent(graphDepth) + "- " + openSet[i].ToString();
            }
            if (writePlannerLog)
            {
                plannerLog += "\n" + makeIndent(graphDepth) + "Planning at depth" + graphDepth;
                plannerLog += "\n" + makeIndent(graphDepth) + "Current: " + last;

                plannerLog += "\n" + makeIndent(graphDepth) + "<color=#CCCC00>OpenSet(" + openSet.Count + "): </color>";
                plannerLog += openSetString;

                plannerLog += "\n";

                plannerLog += makeIndent(graphDepth) + "-><color=#00CC00>Best Node Chosen:</color> " + current.ToString() + "\n\n";
            }

            planInfo.AddIteration(graphDepth, "Current:\n- " + last.ToString(), "OpenSet(" + openSet.Count + "):" + openSetString, current.ToString());

            if (current.required.Count == 0)
            {
                plannerLog += makeIndent(graphDepth) + "-><color=#00CC00>Planning Completed with:</color> " + current.ToString() + "\n";
                return(current);
            }

            openSet.Remove(current);

            bool foundValidNeighbor = false;
            for (int i = 0; i < availableActions.Count; i++)
            {
                if (availableActions[i].Equals("Action_" + current.action))
                {
                    continue;                                                         //Dont do the same action twice
                }
                List <Node> neighbors = GetValidNeighborNodeVariations(current, InstantiateAction(availableActions[i]), currentWorldState, agent);
                if (neighbors != null && neighbors.Count > 0)
                {
                    for (int j = 0; j < neighbors.Count; j++)
                    {
                        if (neighbors[j] == null)
                        {
                            break;
                        }
                        foundValidNeighbor = true;
                        neighbors[j].id    = currentNodeID++;
                        int indexOfNodeWithSameState = openSet.IndexOf(neighbors[j]);
                        if (indexOfNodeWithSameState != -1)
                        {
                            string tmp = openSet[indexOfNodeWithSameState].ToString();
                            //if there is already another node with the same resulting planningworldstate, check which has the lower pathcost
                            if (openSet[indexOfNodeWithSameState].estimatedPathCost > neighbors[j].estimatedPathCost)
                            {
                                //if the new node has a lower pathcost, pick that
                                openSet.Remove(openSet[indexOfNodeWithSameState]);
                                openSet.Add(neighbors[j]);

                                if (writePlannerLog)
                                {
                                    plannerLog += makeIndent(graphDepth) + "-><color=#CCCC00>OpenSet Replaced</color>  " + tmp + "with" + neighbors[j].ToString() + "\n";
                                }
                            }
                            else
                            {
                                //if the new node has a higher pathcost, don't do anything
                                if (writePlannerLog)
                                {
                                    plannerLog += makeIndent(graphDepth) + "-><color=#CC0000>OpenSet Not Replaced</color>  " + tmp + "with" + neighbors[j].ToString() + "\n";
                                }
                            }
                        }
                        else
                        {
                            openSet.Add(neighbors[j]);
                            //Debug Log to visualize the process
                            if (writePlannerLog)
                            {
                                plannerLog += makeIndent(graphDepth) + "-><color=#CCCC00>OpenSet Updated</color>  " + neighbors[j].ToString() + "\n";
                            }
                        }
                    }
                }
            }
            if (!foundValidNeighbor && current != goalNode)
            {
                plannerLog += "No valid neigbor found:\n";
                Node questNode = GenerateQuestNode(current, currentWorldState, agent);
                questNode.id = currentNodeID++;
                openSet.Add(questNode);
                //Debug Log to visualize the process
                if (writePlannerLog)
                {
                    plannerLog += makeIndent(graphDepth) + "-><color=#660000>OpenSet Updated</color>  " + questNode.ToString() + "\n";
                }
            }
            graphDepth++;
        }

        return(null);
    }