예제 #1
0
    /// <summary>
    /// Run combat simulations
    /// </summary>
    /// <param name="numberOfRuns">The number of simulations to run</param>
    /// <param name="simulationLevel">Sumulation complexity level</param>
    /// <returns>Number of simulations that actually ran</returns>
    public int RunCombatSimulations(int numberOfRuns, int simulationLevel)
    {
        FileLogger.Trace("AI", "Running combat simulations for " + _province.GetName());
        _state = State.NOT_REVIEWED;

        List <Unit> units = _province.GetUnits();

        // if nobody's here to defend the province attacked, then
        // attackers always win and there are no losses
        if (units.Count == 0 && simulationLevel == 0)
        {
            _winRatio = 1;
            _gain     = _provinceValue;
            return(0);
        }

        // looks like there will be a fight after all
        // set up unit stacks for defenders
        List <UnitStack> defenders = new List <UnitStack>();
        // not using _defendersTrainingCost directly because of a hacky way
        // to simulate enemy retaliation implemented below
        // remainder: _defendersTrainingCost is zero for non-playable functions
        // because, frankly, who cares about them?
        int defendersTrainingCost = _defendersTrainingCost;

        for (int j = 0; j < units.Count; j++)
        {
            defenders.Add(new UnitStack(units[j], _province));
        }

        // now do the same for the attackers
        List <UnitStack> attackers          = new List <UnitStack>();
        List <Province>  attackingProvinces = new List <Province>();
        int attackersTrainingCost           = 0;

        for (int i = 0; i < _orders.Count; i++)
        {
            units = _orders[i].GetUnits();
            for (int j = 0; j < units.Count; j++)
            {
                attackers.Add(new UnitStack(units[j], _orders[i].GetOrigin()));
                if (!attackingProvinces.Contains(_orders[i].GetOrigin()))
                {
                    attackingProvinces.Add(_orders[i].GetOrigin());
                }
                attackersTrainingCost += units[j].GetTrainingCost();
            }
        }

        // that was the easy part
        // now let's consider possible enemy's retalliation (in a hacky way)
        // btw, the easy AI doesn't do this
        if (simulationLevel > 0)
        {
            Faction         attackingFaction = _orders[0].GetOrigin().GetOwnersFaction();
            List <Province> neighbors        = _province.GetNeighbors();
            for (int i = 0; i < neighbors.Count; i++)
            {
                Faction opponent = neighbors[i].GetOwnersFaction();
                if (opponent.IsPlayable() && opponent != attackingFaction)
                {
                    units = neighbors[i].GetUnits();
                    for (int j = 0; j < units.Count; j++)
                    {
                        defenders.Add(new UnitStack(units[j], neighbors[i]));
                        defendersTrainingCost += units[j].GetTrainingCost();
                    }
                }
            }
        }

        // initialize cumulative statistics
        int numberOfWins = 0;
        // what's the replacement cost for the losses suffered?
        double cumulativeLoss = 0;
        // if fighting a playable faction, same for the enemy
        // because we want to inflict losses on them
        double cumulativeEnemyLoss = 0;

        for (int k = 0; k < numberOfRuns; k++)
        {
            Combat combat = new Combat(_province, UnitStack.Clone(attackers), UnitStack.Clone(defenders), false);
            combat.ResolveCombat();

            _combatSimulations.Add(combat);

            List <UnitStack> remainingAttackers = combat.GetAttackers();
            if (remainingAttackers.Count > 0)
            {
                numberOfWins++;

                // count the losses, ours...
                int remainingAttackersTrainingCost = 0;
                for (int j = 0; j < remainingAttackers.Count; j++)
                {
                    remainingAttackersTrainingCost += remainingAttackers[j].GetBaseUnit().GetTrainingCost();
                }
                cumulativeLoss += attackersTrainingCost - remainingAttackersTrainingCost;

                // ... and our enemies'
                cumulativeEnemyLoss += defendersTrainingCost;
            }
            else
            {
                // we lost the battle and all of our units!
                cumulativeLoss += attackersTrainingCost;

                // but maybe the enemy lost some too?
                List <UnitStack> remainingDefenders = combat.GetDefenders();
                int remainingDefendersTrainingCost  = 0;
                for (int j = 0; j < remainingDefenders.Count; j++)
                {
                    Faction opponent = remainingDefenders[j].GetProvinceToRetreat().GetOwnersFaction();
                    if (opponent.IsPlayable())
                    {
                        remainingDefendersTrainingCost += remainingDefenders[j].GetBaseUnit().GetTrainingCost();
                    }
                }
                cumulativeEnemyLoss += defendersTrainingCost - remainingDefendersTrainingCost;
            }
        }

        _winRatio  = (double)numberOfWins / numberOfRuns;
        _gain      = (double)_provinceValue * numberOfWins / numberOfRuns;
        _loss      = cumulativeLoss / numberOfRuns;
        _enemyLoss = cumulativeEnemyLoss / numberOfRuns;

        string attackOriginDescription = GameUtils.DescribeProvinceList(attackingProvinces);

        FileLogger.Trace("AI", _province.GetName() + " can be conquered " + numberOfWins + " out of " + numberOfRuns + " times if attacked from [" + attackOriginDescription + "] (value: " + _provinceValue + ", loss: " + _loss + ", enemy loss: " + _enemyLoss + ", gain: " + _gain + ", score: " + GetScore() + ")");

        return(numberOfRuns);
    }