Example #1
0
    private List <OneStepPlan> getShotgunInitialSolution()
    {
        List <OneStepPlan> result = null;

        ShooterOneStepPlan[] shooterOneStepPlans;
        OneStepPlan          oneStepPlan;


        // Find "meeting" point
        int   meetingVertexIndex = 0;
        State bestMeetingState   = null;
        float bestMeetingUtility = float.NegativeInfinity;

        for (int i = 0; i < world.graphVertices.Count; i++)
        {
            List <OneStepPlan> currResult = new List <OneStepPlan>();

            World.VisibilityVertex[] startVertices = new World.VisibilityVertex[comradeCount];
            List <int>[]             paths         = new List <int> [comradeCount];
            for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
            {
                paths [comradeIndex]         = shortestPathsFinder.reconstructShortestPath(comradeStartIndex + comradeIndex, i);
                startVertices [comradeIndex] = world.graphVertices [comradeStartIndex + comradeIndex];
            }

            float[] health = new float[comradeCount + enemyCount];
            for (int j = 0; j < health.Length; j++)
            {
                health[j] = maxHealth;
            }
            State currState = new State(startVertices, health);

            bool  allReachedFinish = false;
            int[] prevPivots       = new int[comradeCount];
            while (!allReachedFinish)
            {
                shooterOneStepPlans = new ShooterOneStepPlan[comradeCount];
                oneStepPlan         = new OneStepPlan(shooterOneStepPlans);

                allReachedFinish = true;
                for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                {
                    List <Vector2> currComradePositions = new List <Vector2>();
                    shooterOneStepPlans[comradeIndex] = new ShooterOneStepPlan(currComradePositions, currState.health[comradeIndex]);

                    if (prevPivots [comradeIndex] == paths [comradeIndex].Count - 1 || !(currState.health[comradeIndex] > 0.0f))
                    {
                        var timeLeft = 1.0f;
                        if (prevPivots[comradeIndex] == paths[comradeIndex].Count - 1)
                        {
                            while (timeLeft > 0.0f)
                            {
                                currComradePositions.Add(world.graphVertices[paths[comradeIndex].Last()].vertex);
                                timeLeft -= world.vehicle.dt;
                            }
                        }
                        else if (!(currState.health[comradeIndex] > 0.0f))
                        {
                            //currComradePositions.Add(shooterOneStepPlans[comradeIndex].positions.Last());
                            currComradePositions.AddRange(currResult.Last().shooterPlans[comradeIndex].positions);
                        }

                        continue;
                    }
                    else
                    {
                        allReachedFinish = false;
                    }

                    float   maxTravelDist   = world.vehicle.maxVelocity;
                    Vector2 nextVertex      = world.graphVertices[paths[comradeIndex][prevPivots[comradeIndex] + 1]].vertex;
                    Vector2 currVertex      = world.graphVertices[paths[comradeIndex][prevPivots[comradeIndex]]].vertex;
                    float   prevStepRemDist = (currState.positions[comradeIndex].vertex - currVertex).magnitude;
                    float   remainingDist   = (nextVertex - currVertex).magnitude - prevStepRemDist;
                    bool    reachedFinish   = false;

                    float stepTimeLeft    = 1.0f;
                    float accumulatedTime = 0.0f;

                    float travelTime;
                    float prevStepRemTime;

                    while (maxTravelDist > remainingDist)
                    {
                        // we're at point prevPosition + prevStepRemDist

                        prevStepRemTime = prevStepRemDist / world.vehicle.maxVelocity;

                        travelTime = remainingDist / world.vehicle.maxVelocity;
                        while (accumulatedTime + world.vehicle.dt < travelTime)
                        {
                            accumulatedTime += world.vehicle.dt;
                            stepTimeLeft    -= world.vehicle.dt;

                            if (stepTimeLeft <= 0.0f)
                            {
                                Debug.Log("FUCKKKK !!!");
                                break;
                            }

                            // calculate position, add it
                            currComradePositions.Add((nextVertex - currVertex) * ((prevStepRemTime + accumulatedTime) / (prevStepRemTime + travelTime)) + currVertex);
                        }
                        accumulatedTime = accumulatedTime + world.vehicle.dt - travelTime;
                        // calculate position, add it


                        prevPivots[comradeIndex]++;
                        if (prevPivots[comradeIndex] == paths[comradeIndex].Count - 1)
                        {
                            remainingDist = 0.0f;
                            reachedFinish = true;
                            break;
                        }

                        maxTravelDist  -= remainingDist;
                        prevStepRemDist = 0.0f;

                        nextVertex    = world.graphVertices [paths [comradeIndex] [prevPivots [comradeIndex] + 1]].vertex;
                        currVertex    = world.graphVertices [paths [comradeIndex] [prevPivots [comradeIndex]]].vertex;
                        remainingDist = (nextVertex - currVertex).magnitude;
                    }

                    prevStepRemTime = prevStepRemDist / world.vehicle.maxVelocity;

                    travelTime = remainingDist / world.vehicle.maxVelocity;
                    while (accumulatedTime + world.vehicle.dt < travelTime)
                    {
                        accumulatedTime += world.vehicle.dt;
                        stepTimeLeft    -= world.vehicle.dt;

                        if (stepTimeLeft <= 0.0f)
                        {
                            break;
                        }

                        // calculate position, add it
                        //currComradePositions.Add((nextVertex - currVertex) * (accumulatedTime / travelTime) + currVertex);

                        currComradePositions.Add((nextVertex - currVertex) * ((prevStepRemTime + accumulatedTime) / (prevStepRemTime + travelTime)) + currVertex);
                    }
                    accumulatedTime = accumulatedTime + world.vehicle.dt - travelTime;

                    var prevPosition = world.graphVertices [paths [comradeIndex] [prevPivots [comradeIndex]]].vertex;
                    if (reachedFinish)
                    {
                        currState.positions[comradeIndex] = world.graphVertices[i];

                        while (stepTimeLeft > 0.0f)
                        {
                            currComradePositions.Add(currState.positions[comradeIndex].vertex);
                            stepTimeLeft -= 0.1f;
                        }

                        continue;
                    }

                    float currTravelLineLength = world.visibilityGraph [paths [comradeIndex] [prevPivots [comradeIndex]]] [paths [comradeIndex] [prevPivots [comradeIndex] + 1]];
                    currState.positions[comradeIndex] = new World.VisibilityVertex((world.graphVertices[paths[comradeIndex][prevPivots[comradeIndex] + 1]].vertex - prevPosition) *
                                                                                   ((maxTravelDist + prevStepRemDist) / currTravelLineLength) + prevPosition, false);

                    currComradePositions.Add(currState.positions[comradeIndex].vertex);
                }

                if (!allReachedFinish)
                {
                    float[] enemyHealths = new float[enemyCount];
                    oneStepPlan.enemyHealths = enemyHealths;
                    for (int enemIndex = 0; enemIndex < enemyCount; enemIndex++)
                    {
                        enemyHealths[enemIndex] = currState.health[comradeCount + enemIndex];
                    }

                    int[] comradeTargets;
                    int[] enemyTargets;
                    float enemiesTotalHealth;
                    shoot(currState, 2.0f, 1.0f / 5, 1.0f, 1.0f / 20, out comradeTargets, out enemyTargets, out enemiesTotalHealth);

                    oneStepPlan.enemyTargetIndices = enemyTargets;

                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        shooterOneStepPlans[comradeIndex].targetAgentIndex = comradeTargets[comradeIndex];
                    }

                    currResult.Add(oneStepPlan);
                }
            }

            // Estimate utility of currState
            float currUtility = estimateUtility(currState, isRelative: true);
            if (currUtility > bestMeetingUtility)
            {
                bestMeetingUtility = currUtility;
                bestMeetingState   = currState;
                meetingVertexIndex = i;
                result             = currResult;
            }
        }

        // Find the best enemies traversing order

        float bestPermUtility = float.NegativeInfinity;
        List <OneStepPlan> bestPermuResult = null;

        System.Collections.IList bestPermu = null;
        State bestPermuState = null;

        var enemyIndices = Enumerable.Range(0, enemyCount).Where(i => bestMeetingState.health[comradeCount + i] > 0.0f).ToList();

        foreach (var permu in UtilityClass.Permutate(enemyIndices, enemyIndices.Count))
        {
            List <int> path = new List <int>();
            List <int> reconstructedPath = shortestPathsFinder.reconstructShortestPath(meetingVertexIndex, enemyStartIndex + (int)permu[0]);
            path.AddRange(reconstructedPath);
            for (int i = 0; i < permu.Count - 1; i++)
            {
                reconstructedPath = shortestPathsFinder.reconstructShortestPath(enemyStartIndex + (int)permu[i], enemyStartIndex + (int)permu[i + 1]);
                reconstructedPath.RemoveAt(0);
                path.AddRange(reconstructedPath);
            }

            // Simulate reconstructed path
            float[] health = new float[comradeCount + enemyCount];
            for (int j = 0; j < health.Length; j++)
            {
                health[j] = bestMeetingState.health[j];
            }
            World.VisibilityVertex[] startVertices = new World.VisibilityVertex[comradeCount];
            for (int i = 0; i < startVertices.Length; i++)
            {
                startVertices[i] = new World.VisibilityVertex(bestMeetingState.positions[i]);
            }
            State curState = new State(startVertices, health);

            List <OneStepPlan> currPermuResult = new List <OneStepPlan>();


            int   prevPivot       = 0;
            float currTotalHealth = 1.0f;
            bool  reachedFinish   = false;
            while (!reachedFinish && currTotalHealth > 0.0f)
            {
                shooterOneStepPlans = new ShooterOneStepPlan[comradeCount];
                oneStepPlan         = new OneStepPlan(shooterOneStepPlans);

                for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                {
                    List <Vector2> curComradePositions = new List <Vector2>();
                    shooterOneStepPlans[comradeIndex] = new ShooterOneStepPlan(curComradePositions, curState.health[comradeIndex]);
                }

                float   maxTravelDist   = world.vehicle.maxVelocity;
                Vector2 nextVertex      = world.graphVertices[path[prevPivot + 1]].vertex;
                Vector2 currVertex      = world.graphVertices[path[prevPivot]].vertex;
                float   prevStepRemDist = (curState.positions[0].vertex - currVertex).magnitude;
                float   remainingDist   = (nextVertex - currVertex).magnitude - prevStepRemDist;

                float stepTimeLeft    = 1.0f;
                float accumulatedTime = 0.0f;
                float travelTime;

                float prevStepRemTime;

                while (maxTravelDist > remainingDist)
                {
                    prevStepRemTime = prevStepRemDist / world.vehicle.maxVelocity;

                    // we're at point prevPosition + prevStepRemDist
                    travelTime = remainingDist / world.vehicle.maxVelocity;
                    while (accumulatedTime + world.vehicle.dt < travelTime)
                    {
                        accumulatedTime += world.vehicle.dt;
                        stepTimeLeft    -= world.vehicle.dt;

                        if (stepTimeLeft <= 0.0f)
                        {
                            Debug.Log("FUCKKKK !!!");
                            break;
                        }

                        // calculate position, add it (for each comrade)

                        for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                        {
                            shooterOneStepPlans[comradeIndex].positions.Add((nextVertex - currVertex) * ((prevStepRemTime + accumulatedTime) / (prevStepRemTime + travelTime)) + currVertex);
                        }
                    }
                    accumulatedTime = accumulatedTime + world.vehicle.dt - travelTime;


                    prevPivot++;
                    if (prevPivot == path.Count - 1)
                    {
                        reachedFinish = true;
                        break;
                    }

                    maxTravelDist  -= remainingDist;
                    prevStepRemDist = 0.0f;

                    nextVertex    = world.graphVertices[path[prevPivot + 1]].vertex;
                    currVertex    = world.graphVertices[path[prevPivot]].vertex;
                    remainingDist = (nextVertex - currVertex).magnitude;
                }

                prevStepRemTime = prevStepRemDist / world.vehicle.maxVelocity;

                travelTime = remainingDist / world.vehicle.maxVelocity;
                while (accumulatedTime + world.vehicle.dt < travelTime)
                {
                    accumulatedTime += world.vehicle.dt;
                    stepTimeLeft    -= world.vehicle.dt;

                    if (stepTimeLeft <= 0.0f)
                    {
                        break;
                    }

                    // calculate position, add it (for each f****r)

                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        shooterOneStepPlans[comradeIndex].positions.Add((nextVertex - currVertex) * ((prevStepRemTime + accumulatedTime) / (prevStepRemTime + travelTime)) + currVertex);
                    }
                }
                accumulatedTime = accumulatedTime + world.vehicle.dt - travelTime;

                var prevPosition = world.graphVertices[path[prevPivot]].vertex;
                if (reachedFinish)
                {
                    // TODO: Change positions so that healthiest f****r is in the front

                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        curState.positions[comradeIndex] = world.graphVertices[path[path.Count - 1]];
                    }

                    while (stepTimeLeft > 0.0f)
                    {
                        for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                        {
                            shooterOneStepPlans[comradeIndex].positions.Add(curState.positions[comradeIndex].vertex);
                        }
                        stepTimeLeft -= 0.1f;
                    }

                    continue;
                }

                float currTravelLineLength = world.visibilityGraph[path[prevPivot]][path[prevPivot + 1]];

                // TODO: Change positions so that healthiest f****r is in the front

                for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                {
                    curState.positions[comradeIndex] = new World.VisibilityVertex((world.graphVertices[path[prevPivot + 1]].vertex - prevPosition) *
                                                                                  ((maxTravelDist + prevStepRemDist) / currTravelLineLength) + prevPosition, false);
                    shooterOneStepPlans[comradeIndex].positions.Add(curState.positions[comradeIndex].vertex);
                }


                float[] enemyHealths = new float[enemyCount];
                oneStepPlan.enemyHealths = enemyHealths;
                for (int enemIndex = 0; enemIndex < enemyCount; enemIndex++)
                {
                    enemyHealths[enemIndex] = curState.health[comradeCount + enemIndex];
                }

                int[] comradeTargets;
                int[] enemyTargets;
                float enemiesTotalHealth;
                currTotalHealth = shoot(curState, 2.0f, 1.0f / 5, 1.0f, 1.0f / 20, out comradeTargets, out enemyTargets, out enemiesTotalHealth);

                oneStepPlan.enemyTargetIndices = enemyTargets;

                for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                {
                    shooterOneStepPlans[comradeIndex].targetAgentIndex = comradeTargets[comradeIndex];
                }

                currPermuResult.Add(oneStepPlan);

                if (!(enemiesTotalHealth > 0.0f))
                {
                    reachedFinish = true;
                }
            }


            // Adding the final state information
            shooterOneStepPlans = new ShooterOneStepPlan[comradeCount];
            oneStepPlan         = new OneStepPlan(shooterOneStepPlans);
            for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
            {
                List <Vector2> curComradePositions = new List <Vector2>();
                shooterOneStepPlans[comradeIndex] = new ShooterOneStepPlan(curComradePositions, curState.health[comradeIndex]);
            }
            for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
            {
                shooterOneStepPlans[comradeIndex].positions.Add(curState.positions[comradeIndex].vertex);
            }
            float[] lastEnemyHealths = new float[enemyCount];
            oneStepPlan.enemyHealths = lastEnemyHealths;
            for (int enemIndex = 0; enemIndex < enemyCount; enemIndex++)
            {
                lastEnemyHealths[enemIndex] = curState.health[comradeCount + enemIndex];
            }
            currPermuResult.Add(oneStepPlan);


            // Estimate utility of curState
            float curUtility = estimateUtility(curState, isRelative: true);
            if (curUtility > bestPermUtility)
            {
                bestPermUtility = curUtility;
                bestPermuState  = curState;
                bestPermu       = permu;
                bestPermuResult = currPermuResult;
            }
        }

        result.AddRange(bestPermuResult);

        return(result);
    }
Example #2
0
    private List <OneStepPlan> getRifleInitialSolution()
    {
        List <OneStepPlan> result = new List <OneStepPlan>();

        Dictionary <int, int> numberOfPointVisits = new Dictionary <int, int>();

        for (int i = 0; i < world.graphVertices.Count; i++)
        {
            numberOfPointVisits.Add(i, 0);
        }

        int[] prevCombination = new int[comradeCount];
        for (int i = 0; i < comradeCount; i++)
        {
            prevCombination[i] = comradeStartIndex + i;
            numberOfPointVisits[prevCombination[i]] = 1;
        }

        Dictionary <int[], StateVisits> stateVisits = new Dictionary <int[], StateVisits>(new CombinationsEqualityComparer())
        {
            { prevCombination, new StateVisits()
              {
                  numberOfVisits = 1
              } }
        };

        World.VisibilityVertex[] startVertices = new World.VisibilityVertex[comradeCount];
        for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
        {
            startVertices[comradeIndex] = world.graphVertices[comradeStartIndex + comradeIndex];
        }
        float[] health = new float[comradeCount + enemyCount];
        for (int j = 0; j < health.Length; j++)
        {
            health[j] = maxHealth;
        }
        State prevBestState = new State(startVertices, health);

        bool terminalStateReached = false;

        while (!terminalStateReached)
        {
            // Select feasible vertices to go to
            List <int>[] oneStepAccessVertices = new List <int> [comradeCount];
            for (int i = 0; i < comradeCount; i++)
            {
                oneStepAccessVertices[i] = new List <int>();
                for (int j = 0; j < world.graphVertices.Count; j++)
                {
                    var comraringValue = shortestDists[prevCombination[i], j];
                    if (comraringValue <= world.vehicle.maxVelocity * 1.0f)
                    {
                        oneStepAccessVertices[i].Add(j);
                    }
                }
            }

            bool        currTerminalStateReached = false;
            OneStepPlan bestOneStepPlan          = null;
            int[]       bestCombination          = new int[comradeCount];
            State       currBestState            = null;
            float       bestUtility = float.NegativeInfinity;

            int[] accessVertIndices        = new int[comradeCount];
            int[] newCombination           = new int[comradeCount];
            bool  allCombinationsExhausted = false;
            while (!allCombinationsExhausted)
            {
                // New combination that we're assessing
                bool isInvalidCombination = false;
                for (int j = 0; j < comradeCount; j++)
                {
                    newCombination[j] = oneStepAccessVertices[j][accessVertIndices[j]];
                    if (prevBestState == null || prevBestState.health == null || newCombination == null || prevCombination == null)
                    {
                        Debug.Log("F**k");
                        isInvalidCombination = true;
                        break;
                    }

                    if (!(prevBestState.health[j] > 0.0f) && newCombination[j] != prevCombination[j])
                    {
                        isInvalidCombination = true;
                        break;
                    }

                    for (int k = 0; k < comradeCount; k++)
                    {
                        if (j != k && newCombination[j] == newCombination[k])
                        {
                            isInvalidCombination = true;
                            break;
                        }
                    }
                }

                StateVisits combVisits;
                if (!isInvalidCombination && (!stateVisits.TryGetValue(newCombination, out combVisits) || combVisits.numberOfVisits < MAX_NUMBER_OF_VISITS))
                {
                    ShooterOneStepPlan[] shooterOneStepPlans = new ShooterOneStepPlan[comradeCount];
                    OneStepPlan          oneStepPlan         = new OneStepPlan(shooterOneStepPlans);

                    int[] prevPivots = new int[comradeCount];

                    // Simulation

                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        List <Vector2> currComradePositions = new List <Vector2>();
                        shooterOneStepPlans[comradeIndex] = new ShooterOneStepPlan(currComradePositions, prevBestState.health[comradeIndex]);

                        if (prevCombination[comradeIndex] == newCombination[comradeIndex] || !(prevBestState.health[comradeIndex] > 0.0f))
                        {
                            var timeLeft = 1.0f;
                            if (prevCombination[comradeIndex] == newCombination[comradeIndex])
                            {
                                while (timeLeft > 0.0f)
                                {
                                    currComradePositions.Add(world.graphVertices[prevCombination[comradeIndex]].vertex);
                                    timeLeft -= world.vehicle.dt;
                                }
                            }
                            else if (!(prevBestState.health[comradeIndex] > 0.0f))
                            {
                                //currComradePositions.Add(shooterOneStepPlans[comradeIndex].positions.Last());
                                currComradePositions.Add(prevBestState.positions[comradeIndex].vertex);
                            }

                            continue;
                        }

                        List <int> path = shortestPathsFinder.reconstructShortestPath(prevCombination[comradeIndex], newCombination[comradeIndex]);

                        float   maxTravelDist = world.vehicle.maxVelocity * 1.0f;
                        Vector2 nextVertex    = world.graphVertices[path[prevPivots[comradeIndex] + 1]].vertex;
                        Vector2 currVertex    = world.graphVertices[path[prevPivots[comradeIndex]]].vertex;
                        float   remainingDist = (nextVertex - currVertex).magnitude;

                        float stepTimeLeft    = 1.0f;
                        float accumulatedTime = 0.0f;

                        float travelTime;
                        float prevStepRemTime;

                        while (maxTravelDist >= remainingDist)
                        {
                            // we're at point prevPosition + prevStepRemDist
                            travelTime = remainingDist / world.vehicle.maxVelocity;
                            while (accumulatedTime + world.vehicle.dt < travelTime)
                            {
                                accumulatedTime += world.vehicle.dt;
                                stepTimeLeft    -= world.vehicle.dt;
                                if (stepTimeLeft <= 0.0f)
                                {
                                    Debug.Log("FUCKKKK !!!");
                                    break;
                                }
                                // calculate position, add it (for each comrade)
                                currComradePositions.Add((nextVertex - currVertex) * ((accumulatedTime) / (travelTime)) + currVertex);
                            }
                            accumulatedTime = accumulatedTime + world.vehicle.dt - travelTime;


                            prevPivots[comradeIndex]++;
                            if (prevPivots[comradeIndex] == path.Count - 1)
                            {
                                remainingDist = 0.0f;
                                break;
                            }

                            maxTravelDist -= remainingDist;

                            nextVertex    = world.graphVertices[path[prevPivots[comradeIndex] + 1]].vertex;
                            currVertex    = world.graphVertices[path[prevPivots[comradeIndex]]].vertex;
                            remainingDist = (nextVertex - currVertex).magnitude;
                        }

                        // Adding missing dt-points
                        while (stepTimeLeft > 0.0f)
                        {
                            Vector2 lastVertex = world.graphVertices[path.Last()].vertex;
                            if (Vector2.Distance(lastVertex, nextVertex) > 0.000001f)
                            {
                                Debug.Log("FUCKKKK !!!");
                            }
                            currComradePositions.Add(world.graphVertices[path.Last()].vertex);
                            stepTimeLeft -= 0.1f;
                        }
                    }

                    // Create a new state

                    World.VisibilityVertex[] currVertices = new World.VisibilityVertex[comradeCount];
                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        currVertices[comradeIndex] = world.graphVertices[newCombination[comradeIndex]];
                    }
                    float[] currHealths = new float[comradeCount + enemyCount];
                    for (int j = 0; j < currHealths.Length; j++)
                    {
                        currHealths[j] = prevBestState.health[j];
                    }
                    State currState = new State(currVertices, currHealths);

                    float[] enemyHealths = new float[enemyCount];
                    oneStepPlan.enemyHealths = enemyHealths;
                    for (int enemIndex = 0; enemIndex < enemyCount; enemIndex++)
                    {
                        enemyHealths[enemIndex] = currState.health[comradeCount + enemIndex];
                    }

                    // TODO: If some fuckers are at one point - change positions so that healthiest f****r is in the front

                    int[] comradeTargets;
                    int[] enemyTargets;
                    float enemiesTotalHealth;
                    // Shooting
                    float currTotalHealth = shoot(currState, 1.0f, 1.0f / 20, 2.0f, 1.0f / 5, out comradeTargets, out enemyTargets, out enemiesTotalHealth);

                    oneStepPlan.enemyTargetIndices = enemyTargets;

                    for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                    {
                        shooterOneStepPlans[comradeIndex].targetAgentIndex = comradeTargets[comradeIndex];
                    }

                    if (!(enemiesTotalHealth > 0.0f) || !(currTotalHealth > 0.0f))
                    {
                        currTerminalStateReached = true;
                        //allCombinationsExhausted = true;
                    }

                    float currUtility = estimateUtility(currState, isRelative: true);

                    int   currCombPointVisitsTotal = 0;
                    int   numberOfVisibleOpponents = 0;
                    float sumShootingDist          = 0.0f;
                    int   numShoots           = 0;
                    float sumShortDistToOp    = 0.0f;
                    float sumDistToComrades   = 0.0f;
                    int   numberComradesAlive = 0;
                    for (int j = 0; j < comradeCount; j++)
                    {
                        if (!(currState.health[j] > 0.0f))
                        {
                            continue;
                        }

                        numberComradesAlive++;
                        sumDistToComrades += Enumerable.Range(0, comradeCount).Where(ind => currState.health[ind] > 0.0f)
                                             .Sum(sumInd => shortestDists[newCombination[j], newCombination[sumInd]]);

                        currCombPointVisitsTotal += numberOfPointVisits[newCombination[j]];

                        if (comradeTargets[j] != -1)
                        {
                            int numRepeat = comradeTargets.Count(x => x == comradeTargets[j]);
                            numberOfVisibleOpponents += (numRepeat * numRepeat);
                            numShoots++;
                            sumShootingDist += Vector2.Distance(currState.positions[j].vertex, world.graphVertices[enemyStartIndex + comradeTargets[j]].vertex);
                        }
                        else
                        {
                            float minDistToOp = float.PositiveInfinity;
                            for (int k = 0; k < enemyCount; k++)
                            {
                                if (shortestDists[newCombination[j], enemyStartIndex + k] < minDistToOp)
                                {
                                    minDistToOp = shortestDists[newCombination[j], enemyStartIndex + k];
                                }
                            }
                            sumShortDistToOp += minDistToOp;
                        }
                    }
                    currUtility -= 0.2f * currCombPointVisitsTotal;
                    currUtility += 1.0f * numberOfVisibleOpponents;
                    if (numShoots != 0)
                    {
                        currUtility += 0.3f * sumShootingDist / numShoots;
                    }
                    else
                    {
                        currUtility -= 50.0f * sumShortDistToOp / numberComradesAlive;
                    }
                    currUtility -= 100.0f * sumDistToComrades / numberComradesAlive;

                    // Adding Gaussian noise
                    double u1            = 1.0 - WorldController.rand.NextDouble();
                    double u2            = 1.0 - WorldController.rand.NextDouble();
                    double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
                    currUtility += (float)(randStdNormal * 0.1);

                    if (currUtility > bestUtility)
                    {
                        // Record best values
                        bestUtility          = currUtility;
                        bestOneStepPlan      = oneStepPlan;
                        currBestState        = currState;
                        terminalStateReached = currTerminalStateReached;

                        for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
                        {
                            bestCombination[comradeIndex] = newCombination[comradeIndex];
                        }
                    }
                }

                // Generate a new combination
                int i = comradeCount - 1;
                accessVertIndices[i]++;
                while (accessVertIndices[i] >= oneStepAccessVertices[i].Count)
                {
                    accessVertIndices[i] = 0;
                    i--;
                    if (i < 0)
                    {
                        allCombinationsExhausted = true;
                        break;
                    }

                    accessVertIndices[i]++;
                }
            }


            StateVisits bestCombVisits;
            if (!stateVisits.TryGetValue(bestCombination, out bestCombVisits))
            {
                bestCombVisits = new StateVisits();
                stateVisits.Add(bestCombination, bestCombVisits);
            }
            bestCombVisits.numberOfVisits++;

            prevBestState = currBestState;
            result.Add(bestOneStepPlan);

            for (int comradeIndex = 0; comradeIndex < comradeCount; comradeIndex++)
            {
                prevCombination[comradeIndex] = bestCombination[comradeIndex];
                numberOfPointVisits[prevCombination[comradeIndex]]++;
            }

            if (terminalStateReached)
            {
                // Adding the last state info with zero healths

                ShooterOneStepPlan[] shooterLastStepPlans = new ShooterOneStepPlan[comradeCount];
                OneStepPlan          lastStepPlan         = new OneStepPlan(shooterLastStepPlans);

                float[] lastEnemyHealths = new float[enemyCount];
                for (int i = 0; i < enemyCount; i++)
                {
                    lastEnemyHealths[i] = prevBestState.health[comradeCount + i];
                }
                lastStepPlan.enemyHealths = lastEnemyHealths;

                for (int i = 0; i < comradeCount; i++)
                {
                    shooterLastStepPlans[i] = new ShooterOneStepPlan(new List <Vector2> {
                        prevBestState.positions[i].vertex
                    }, prevBestState.health[i]);
                    // prevBestState.positions.Select(p => p.vertex).ToList()
                }

                result.Add(lastStepPlan);
            }
        }

        return(result);
    }