Example #1
0
        public static int[] CalculateOptimalAssignment(List <Task> newGoals, List <CreatureAI> agents)
        {
            int numGoals  = newGoals.Count;
            int numAgents = agents.Count;
            int maxSize   = Math.Max(numGoals, numAgents);

            int[,] goalMatrix = new int[maxSize, maxSize];
            const float multiplier = 100;

            if (numGoals == 0 || numAgents == 0)
            {
                return(null);
            }

            for (int goalIndex = 0; goalIndex < numGoals; goalIndex++)
            {
                Task goal = newGoals[goalIndex];

                for (int agentIndex = 0; agentIndex < numAgents; agentIndex++)
                {
                    CreatureAI agent     = agents[agentIndex];
                    float      floatCost = goal.ComputeCost(agent.Creature);

                    int cost = (int)(floatCost * multiplier);

                    if (!goal.IsFeasible(agent.Creature))
                    {
                        cost += 99999;
                    }

                    if (agent.Creature.Status.IsAsleep)
                    {
                        cost += 99999;
                    }

                    cost += agents[agentIndex].Tasks.Count;

                    goalMatrix[agentIndex, goalIndex] = cost;
                }
            }

            // Add additional columns or rows
            if (numAgents > numGoals)
            {
                int maxValue = GetMax(goalMatrix, numAgents, numGoals) + 1;
                for (int dummyGoal = numGoals; dummyGoal < maxSize; dummyGoal++)
                {
                    for (int i = 0; i < numAgents; i++)
                    {
                        // If we have more agents than goals, we need to add additional fake goals
                        // Since goals are in columns, we are essentially adding a new column.
                        goalMatrix[i, dummyGoal] = maxValue;
                    }
                }
            }
            else if (numGoals > numAgents)
            {
                int maxValue = GetMax(goalMatrix, numAgents, numGoals) + 1;
                for (int dummyAngent = numAgents; dummyAngent < maxSize; dummyAngent++)
                {
                    for (int i = 0; i < numGoals; i++)
                    {
                        // If we have more goals than agents, we need to add additional fake agents
                        // Since goals are in columns, we are essentially adding a new row.
                        goalMatrix[dummyAngent, i] = maxValue;
                    }
                }
            }

            return(goalMatrix.FindAssignments());
        }
Example #2
0
        public static void AssignTasksGreedy(List <Task> newGoals, List <CreatureAI> creatures, int maxPerGoal)
        {
            string trackingString = "AssignTasksGreedy(" + newGoals.Count + ")";

            GamePerformance.Instance.StartTrackPerformance(trackingString);
            // We are going to keep track of the unassigned goal count
            // to avoid having to parse the list at the end of the loop.
            int        goalsUnassigned = newGoals.Count;
            List <int> counts          = new List <int>(goalsUnassigned);

            for (int i = 0; i < goalsUnassigned; i++)
            {
                counts.Add(0);
            }

            // Randomized list changed from the CreatureAI objects themselves to an index into the
            // List passed in.  This is to avoid having to shift the masterCosts list around to match
            // each time we randomize.
            List <int> randomIndex = new List <int>(creatures.Count);

            for (int i = 0; i < creatures.Count; i++)
            {
                randomIndex.Add(i);
            }

            // We create the comparer outside of the loop.  It gets reused for each sort.
            CostComparer toCompare = new CostComparer();

            // One of the biggest issues with the old function was that it was recalculating the whole task list for
            // the creature each time through the loop, using one item and then throwing it away.  Nothing changed
            // in how the calculation happened between each time so we will instead make a costs list for each creature
            // and keep them all.  This not only avoids rebuilding the list but the sheer KeyValuePair object churn there already was.
            List <List <KeyValuePair <int, float> > > masterCosts = new List <List <KeyValuePair <int, float> > >(creatures.Count);

            // We will set this up in the next loop rather than make it's own loop.
            List <int> costsPositions = new List <int>(creatures.Count);

            for (int costIndex = 0; costIndex < creatures.Count; costIndex++)
            {
                List <KeyValuePair <int, float> > costs = new List <KeyValuePair <int, float> >();
                CreatureAI creature = creatures[costIndex];

                // We already were doing an index count to be able to make the KeyValuePair for costs
                // and foreach uses Enumeration which is slower.
                for (int i = 0; i < newGoals.Count; i++)
                {
                    Task task = newGoals[i];
                    // We are checking for tasks the creature is already assigned up here to avoid having to check
                    // every task in the newGoals list against every task in the newGoals list.  The newGoals list
                    // should reasonably not contain any task duplicates.
                    if (creature.Tasks.Contains(task))
                    {
                        continue;
                    }

                    float cost = 0;
                    // We've swapped the order of the two checks to take advantage of a new ComputeCost that can act different
                    // if we say we've already called IsFeasible first.  This allows us to skip any calculations that are repeated in both.
                    if (!task.IsFeasible(creature.Creature))
                    {
                        cost += 1e10f;
                    }
                    cost += task.ComputeCost(creature.Creature, true);
                    costs.Add(new KeyValuePair <int, float>(i, cost));
                }
                // The sort lambda function has been replaced by an IComparer class.
                // This is faster but I mainly did it because VS can not Edit & Continue
                // any function with a Lambda function in it which was slowing down dev time.
                costs.Sort(toCompare);

                masterCosts.Add(costs);
                costsPositions.Add(0);
            }


            // We are going to precalculate the maximum iterations and count down
            // instead of up.
            int iters = goalsUnassigned * creatures.Count;

            while (goalsUnassigned > 0 && iters > 0)
            {
                randomIndex.Shuffle();
                iters--;
                for (int creatureIndex = 0; creatureIndex < randomIndex.Count; creatureIndex++)
                {
                    int        randomCreature = randomIndex[creatureIndex];
                    CreatureAI creature       = creatures[randomCreature];

                    List <KeyValuePair <int, float> > costs = masterCosts[randomCreature];
                    int costPosition = costsPositions[randomCreature];
                    // This loop starts with the previous spot we stopped.  This avoids us having to constantly run a task we
                    // know we have processed.
                    for (int i = costPosition; i < costs.Count; i++)
                    {
                        // Incremented at the start in case we find a task and break.
                        costPosition++;

                        KeyValuePair <int, float> taskCost = costs[i];
                        // We've swapped the checks here.  Tasks.Contains is far more expensive so being able to skip
                        // if it's going to fail the maxPerGoal check anyways is very good.
                        if (counts[taskCost.Key] < maxPerGoal)//  && !creature.Tasks.Contains(newGoals[taskCost.Key]))
                        {
                            // We have to check to see if the task we are assigning is fully unassigned.  If so
                            // we reduce the goalsUnassigned count.  If it's already assigned we skip it.
                            if (counts[taskCost.Key] == 0)
                            {
                                goalsUnassigned--;
                            }

                            counts[taskCost.Key]++;
                            creature.Tasks.Add(newGoals[taskCost.Key].Clone());
                            break;
                        }
                    }
                    // We have to set the position we'll start the loop at the next time based on where we found
                    // our task.
                    costsPositions[randomCreature] = costPosition;
                    // The loop at the end to see if all are unassigned is gone now, replaced by a countdown
                    // variable: goalsUnassigned.
                }
            }
            GamePerformance.Instance.EndTrackPerformance(trackingString);
        }