Example #1
0
    private double EstimateUnitDamage(Unit attacker, Unit defender)
    {
        Combat.TurnPhase phase   = Combat.TurnPhase.MAGIC;
        double           epsilon = 0.05;

        while (phase < Combat.TurnPhase.CLEANUP)
        {
            UnitStack attackerStack  = new UnitStack(new Unit(attacker.GetUnitType(), 1), null);
            UnitStack defenderStack  = new UnitStack(new Unit(defender.GetUnitType(), 1), null);
            double    attackerDamage = EstimateStackAttacksDamage(attackerStack, defenderStack, phase);
            attackerStack = new UnitStack(new Unit(attacker.GetUnitType(), 1), null);
            defenderStack = new UnitStack(new Unit(defender.GetUnitType(), 1), null);
            double defenderDamage = EstimateStackAttacksDamage(defenderStack, attackerStack, phase);
            phase++;
            // read as "attacker deals significantly less damage than defender
            // during this phase"
            if (attackerDamage + epsilon < defenderDamage)
            {
                return(0);
            }
            // "defender deals significantly less damage than attacker
            // during this phase"
            if (attackerDamage > defenderDamage + epsilon)
            {
                return(attackerDamage);
            }
        }
        return(0);
    }
Example #2
0
    private double GetEstimatedDamage(UnitStack attacker, UnitStack defender, Combat.TurnPhase phase)
    {
        int intPhase = (int)phase;

        if (attacker.GetAffectingSpells().Count == 0 && defender.GetAffectingSpells().Count == 0)
        {
            int attackerTypeId = attacker.GetUnitType().GetId();
            if (_expectedDamage.ContainsKey(attackerTypeId))
            {
                Dictionary <int, Dictionary <int, double> > attackerRecords = _expectedDamage[attackerTypeId];
                int defenderTypeId = defender.GetUnitType().GetId();
                if (attackerRecords.ContainsKey(defenderTypeId))
                {
                    Dictionary <int, double> defenderRecords = attackerRecords[defenderTypeId];
                    if (defenderRecords.ContainsKey(intPhase))
                    {
                        return(defenderRecords[intPhase]);
                    }
                }
            }
        }

        // "not found"
        return(-1);
    }
Example #3
0
    /// <summary>
    /// Get unit type's attacks for a particular phase
    /// </summary>
    /// <param name="phase">Combat turn's phase</param>
    /// <returns>List of unit type's attacks for the phase</returns>
    public List <Attack> GetAttacksForPhase(Combat.TurnPhase phase)
    {
        List <Attack> result = new List <Attack>();

        AttackData.Quality quality = AttackData.Quality.NONE;
        switch (phase)
        {
        case Combat.TurnPhase.DIVINE:
            quality = AttackData.Quality.DIVINE;
            break;

        case Combat.TurnPhase.MAGIC:
            quality = AttackData.Quality.MAGIC;
            break;

        case Combat.TurnPhase.RANGED:
            quality = AttackData.Quality.RANGED;
            break;

        case Combat.TurnPhase.SKIRMISH:
            quality = AttackData.Quality.SKIRMISH;
            break;

        case Combat.TurnPhase.CHARGE:
            quality = AttackData.Quality.CHARGE;
            break;

        case Combat.TurnPhase.MELEE:
            quality = AttackData.Quality.MELEE;
            break;
        }

        if (quality != AttackData.Quality.NONE)
        {
            for (int i = 0; i < _attacks.Count; i++)
            {
                if (_attacks[i].HasQuality(quality))
                {
                    result.Add(_attacks[i]);
                }
            }
        }
        return(result);
    }
Example #4
0
    /// <summary>
    /// Estimate results of a unit stack attacking another during a specified turn phase
    /// </summary>
    /// <param name="attacker">Attacking unit stack</param>
    /// <param name="defender">Defending unit stack</param>
    /// <param name="phase">Turn phase</param>
    /// <returns>Mathematical expectation of the attack damage value</returns>
    public double EstimateStackAttacksDamage(UnitStack attacker, UnitStack defender, Combat.TurnPhase phase)
    {
        double result = GetEstimatedDamage(attacker, defender, phase);

        if (result >= 0)
        {
            return(result);
        }
        result = 0;
        int           maxHP   = defender.GetUnitType().GetHitPoints();
        List <Attack> attacks = attacker.GetUnitType().GetAttacksForPhase(phase);

        for (int i = 0; i < attacks.Count; i++)
        {
            int numberOfAttacks = attacks[i].GetNumberOfAttacks();
            for (int attackerPlusDie = 0; attackerPlusDie < _diceSides; attackerPlusDie++)
            {
                for (int attackerMinusDie = 0; attackerMinusDie < _diceSides; attackerMinusDie++)
                {
                    AttackRollResult attackRollResult = CreateAnAttackRollResult(attacker, attacks[i],
                                                                                 attackerPlusDie + 1, attackerMinusDie + 1);

                    int defensiveSkill = CalculateDefensiveSkill(attackRollResult.Attack, defender);
                    int shield         = CalculateShieldValue(attackRollResult.Attack, defender);
                    int armor          = CalculateArmorValue(attackRollResult.Attack, defender, attackRollResult.IsCritical);

                    for (int defenderPlusDie = 0; defenderPlusDie < _diceSides; defenderPlusDie++)
                    {
                        for (int defenderMinusDie = 0; defenderMinusDie < _diceSides; defenderMinusDie++)
                        {
                            // both defenderPlusDie and defenderMinusDie would add +1 to them,
                            // so the +1s would cancel each other
                            int defenseRoll  = defenderPlusDie - defenderMinusDie;
                            int totalDefense = defenseRoll + defensiveSkill + shield;
                            result += numberOfAttacks * EstimateWeaponDamage(attackRollResult, defender, totalDefense, armor, maxHP);
                        }
                    }
                }
            }
        }
        result *= _damageScale;
        SetEstimatedDamage(attacker, defender, phase, result);
        return(result);
    }
Example #5
0
 private void SetEstimatedDamage(UnitStack attacker, UnitStack defender, Combat.TurnPhase phase, double damage)
 {
     if (attacker.GetAffectingSpells().Count == 0 && defender.GetAffectingSpells().Count == 0)
     {
         int attackerTypeId = attacker.GetUnitType().GetId();
         if (!_expectedDamage.ContainsKey(attackerTypeId))
         {
             _expectedDamage[attackerTypeId] = new Dictionary <int, Dictionary <int, double> >();
         }
         int defenderTypeId = defender.GetUnitType().GetId();
         if (!_expectedDamage[attackerTypeId].ContainsKey(defenderTypeId))
         {
             _expectedDamage[attackerTypeId][defenderTypeId] = new Dictionary <int, double>();
         }
         _expectedDamage[attackerTypeId][defenderTypeId][(int)phase] = damage;
     }
     FileLogger.Trace("ESTIMATE", "Estimated damage of " + attacker.GetUnitType().GetName() +
                      " vs " + defender.GetUnitType().GetName() + " during " +
                      phase + " phase is " + damage);
 }
Example #6
0
    /// <summary>
    /// Select the unit stack which is going to be cheapest to re-train, taking into account odds of successful defense
    /// </summary>
    /// <param name="stacks">Available unit stacks</param>
    /// <param name="attacker">Attacking enemy unit stack</param>
    /// <param name="phase">Turn phase of the combat</param>
    /// <returns>Unit stack selected</returns>
    public UnitStack SelectMinReplacementCostUnit(List <UnitStack> stacks, UnitStack attacker, Combat.TurnPhase phase)
    {
        UnitStack result          = null;
        double    replacementCost = double.MaxValue;
        double    altervative;

        for (int i = 0; i < stacks.Count; i++)
        {
            if (stacks[i].GetTotalQty() > 0)
            {
                altervative = CombatHelper.Instance.EstimateStackAttacksDamage(attacker, stacks[i], phase) * stacks[i].GetUnitType().GetTrainingCost() / stacks[i].GetUnitType().GetHitPoints();
                if (altervative < replacementCost)
                {
                    replacementCost = altervative;
                    result          = stacks[i];
                }
            }
        }
        if (result != null)
        {
            FileLogger.Trace("TAI", "Medium Level AI selected " + result.GetUnitType().GetName() + " as a target, expected replacement cost is " + replacementCost);
        }
        return(result);
    }
Example #7
0
    /// <summary>
    /// Select the unit stack which is going to soak enemy's attacks
    /// </summary>
    /// <param name="stacks">Available unit stacks</param>
    /// <param name="attacker">Attacking enemy unit stack</param>
    /// <param name="phase">Turn phase of the combat</param>
    /// <returns>Unit stack selected</returns>
    public UnitStack SelectDefendingUnitStack(List <UnitStack> stacks, UnitStack attacker, Combat.TurnPhase phase)
    {
        switch (_level)
        {
        case 1:
            // general-level AI takes into account successful defense odds
            // and selects the stack that has the lowest exectated replacement cost
            return(SelectMinReplacementCostUnit(stacks, attacker, phase));

        default:
            // captain-level AI selects the cheapest unit stack
            return(SelectCheapestUnit(stacks));
        }
    }