private int UpdateKillsCount(int currentCount, FightResult result, SimulationType type)
        {
            int count = currentCount;

            switch (type)
            {
            case SimulationType.MinimalDamage:
                count -= result.KilledMin;
                break;

            case SimulationType.MaximalDamage:
                count -= result.KilledMax;
                break;

            case SimulationType.AverageDamage:
                count -= result.KilledAvg;
                break;

            default:
                break;
            }
            if (count < 0)
            {
                count = 0;
            }

            return(count);
        }
 private void MergeFightResult(FightResult f1, FightResult f2)
 {
     f1.DamageAvg += f2.DamageAvg;
     f1.DamageMax += f2.DamageMax;
     f1.DamageMin += f2.DamageMin;
     f1.KilledAvg += f2.KilledAvg;
     f1.KilledMax += f2.KilledMax;
     f1.KilledMin += f2.KilledMin;
 }
        private int GetLeftDamage(FightResult result, SimulationType type)
        {
            switch (type)
            {
            case SimulationType.MinimalDamage:
                return(result.DamageLeftMin);

            case SimulationType.MaximalDamage:
                return(result.DamageLeftMax);

            case SimulationType.AverageDamage:
                return(result.DamageLeftAvg);

            default:
                return(0);
            }
        }
        private FightResult CreatureFight(Fight fight, Hero attackerHero, Hero defenderHero, CreatureRef attackerRef, CreatureRef defenderRef, int attackerCount, int defenderCount, TextWriter log, AttackType attackType, int damageLeft = 0)
        {
            log.WriteLine("{0} ({1}) attacks {2} ({3})", attackerRef.Name, attackerCount, defenderRef.Name, defenderCount);
            decimal I1 = 0, R1 = 0;
            decimal I2 = 0, I3 = 0, I4 = 0, I5 = 0, R2 = 0, R3 = 0, R4 = 0, R5 = 0, R6 = 0, R7 = 0, R8 = 0;

            int DMGbmin = 0;
            int DMGbmax = 0;

            // TODO: check for spells modification for DMGb
            Creature attacker = World.Creatures[attackerRef.Name];
            Creature defender = World.Creatures[defenderRef.Name];

            if (attackType == AttackType.Ranged &&
                !System.Array.Exists(attacker.AttackType, element => element == AttackType.Ranged))
            {
                log.WriteLine("{0} cannot attack ranged. Attack type changed to Melee", attackerRef.Name);
                attackType = AttackType.Melee;
            }



            Dictionary <FactorType, List <FactorInfo> > attackerFactors = new Dictionary <FactorType, List <FactorInfo> >();
            Dictionary <FactorType, List <FactorInfo> > defenderFactors = new Dictionary <FactorType, List <FactorInfo> >();

            // Loading heroes factors
            AddHeroFactors(attackerFactors, attackerHero, fight, attacker, defender, attackType, attackerRef, defenderRef);
            AddHeroFactors(defenderFactors, defenderHero, fight, attacker, defender, attackType, attackerRef, defenderRef);

            // Loading creature factors
            AddCreatureFactors(attackerFactors, attackerRef, fight, attacker, defender, attackType, attackerRef, defenderRef);
            AddCreatureFactors(defenderFactors, defenderRef, fight, attacker, defender, attackType, attackerRef, defenderRef);


            // I1 & R1
            // basic I1 & R1
            int attack  = attackerHero.Attack + attacker.Attack;
            int defense = defenderHero.Defense + defender.Defense;

            if (attacker.Terrain[0] == fight.Terrain[0])
            {
                attack += 1;
                log.WriteLine("+1 added to atack to {0} for terrain bonus", attacker.Name);
            }
            if (defender.Terrain[0] == fight.Terrain[0])
            {
                defense += 1;
                log.WriteLine("+1 added to defense to {0} for terrain bonus", defender.Name);
            }

            DMGbmin = attacker.MinDamage;
            DMGbmax = attacker.MaxDamage;
            int dmg = System.Convert.ToInt32(ExecuteFactor(FactorType.I0, attackerFactors, defenderFactors, attackerHero, attacker, log));

            if (dmg > 0)
            {
                DMGbmax += dmg;
                log.WriteLine("{0} added to DMGMax.", DMGbmax);
            }

            DMGbmin = DMGbmin * attackerCount;
            DMGbmax = DMGbmax * attackerCount;

            attack  += System.Convert.ToInt32(ExecuteFactor(FactorType.I1, attackerFactors, defenderFactors, attackerHero, attacker, log));
            defense += System.Convert.ToInt32(ExecuteFactor(FactorType.R1, defenderFactors, defenderFactors, defenderHero, defender, log));

            attack  = System.Convert.ToInt32(attack * (1 - ExecuteFactor(FactorType.IgnoreAttack, defenderFactors, defenderFactors, attackerHero, attacker, log)));
            defense = System.Convert.ToInt32(defense * (1 - ExecuteFactor(FactorType.IgnoreDefense, attackerFactors, defenderFactors, attackerHero, attacker, log)));

            if (attack >= defense)
            {
                I1 = (decimal)0.05 * (attack - defense);
                if (I1 > 3)
                {
                    I1 = 3;
                }
                log.WriteLine("Attack({1}) > Defense({2}), I1={0}", I1, attack, defense);
            }

            if (defense >= attack)
            {
                R1 = (decimal)0.025 * (defense - attack);
                if (R1 >= (decimal)0.7)
                {
                    R1 = (decimal)0.7;
                }
                log.WriteLine("Defense({1}) > Attack({2}), R1={0}", R1, defense, attack);
            }

            I2 = ExecuteFactor(FactorType.I2, attackerFactors, defenderFactors, attackerHero, attacker, log);
            log.WriteLine("I2 value: {0}", I2);

            I3 = ExecuteFactor(FactorType.I3, attackerFactors, defenderFactors, attackerHero, attacker, log, I2);
            log.WriteLine("I3 value: {0}", I3);

            if (attackerRef.Luck)
            {
                I4 = 1;
            }
            log.WriteLine("I4 (luck) value: {0}", I4);

            I5 = ExecuteFactor(FactorType.I5, attackerFactors, defenderFactors, attackerHero, attacker, log);
            log.WriteLine("I5 value: {0}", I5);

            R2 = ExecuteFactor(FactorType.R2, defenderFactors, defenderFactors, defenderHero, defender, log, I2);
            log.WriteLine("R2 value: {0}", R2);

            R3 = ExecuteFactor(FactorType.R3, defenderFactors, defenderFactors, defenderHero, defender, log, I2, R2);
            log.WriteLine("R3 value: {0}", R3);

            R4 = ExecuteFactor(FactorType.R4, defenderFactors, defenderFactors, defenderHero, defender, log, I2, R2);
            log.WriteLine("R4 value: {0}", R4);



            if ((attackType == AttackType.Ranged && attackerRef.RangePenalty == Penalty.value50) ||
                ((attackType == AttackType.Melee) &&
                 System.Array.Exists(attacker.AttackType, element => element == AttackType.Ranged) &&
                 !attackerFactors.ContainsKey(FactorType.NoMeleePenalty))
                )
            {
                R5 = (decimal)0.5;
            }
            log.WriteLine("R5 (melee or ranged penalty) value: {0}", R5);

            if (attackType == AttackType.Ranged && attackerRef.ObstaclePenalty == Penalty.value50)
            {
                R6 = (decimal)0.5;
            }
            log.WriteLine("R6 (obstacle penalty) value: {0}", R6);

            R7 = ExecuteFactor(FactorType.R7, defenderFactors, defenderFactors, attackerHero, attacker, log, I2, R2);
            log.WriteLine("R7 value: {0}", R7);

            R8 = ExecuteFactor(FactorType.R8, defenderFactors, defenderFactors, defenderHero, defender, log, I2, R2);
            log.WriteLine("R8 value: {0}", R8);


            decimal damageMin = DMGbmin * (1 + I1 + I2 + I3 + I4 + I5) * (1 - R1) * (1 - R2 - R3) * (1 - R4) * (1 - R5) * (1 - R6) * (1 - R7) * (1 - R8);
            decimal damageMax = DMGbmax * (1 + I1 + I2 + I3 + I4 + I5) * (1 - R1) * (1 - R2 - R3) * (1 - R4) * (1 - R5) * (1 - R6) * (1 - R7) * (1 - R8);

            log.WriteLine("Damage min: {0}, Damage max: {1}", damageMin, damageMax);

            int damageMinInt = System.Convert.ToInt32(System.Math.Ceiling(damageMin));
            int damageMaxInt = System.Convert.ToInt32(System.Math.Ceiling(damageMax));

            // side effect of Armorer - if damage is not int and defender has armorer secondardy skill then damage is decreased by 1
            bool armorer = false;

            if (damageMin - damageMinInt == 0 ||
                damageMax - damageMaxInt == 0)
            {
                int i = 0;
                foreach (object refItem in defenderHero.Items)
                {
                    if (refItem is Reference && defenderHero.ItemsElementName[i].ToString() == "SecondarySkill")
                    {
                        if (((Reference)refItem).Name == "Armorer")
                        {
                            armorer = true;
                        }
                    }
                    ++i;
                }

                if (armorer && damageMin - damageMinInt == 0)
                {
                    damageMinInt--;
                    log.WriteLine("Side effect of armorer for min damage added");
                }
                if (armorer && damageMax - damageMaxInt == 0)
                {
                    damageMaxInt--;
                    log.WriteLine("Side effect of armorer for max damage added");
                }
            }

            FightResult result = new FightResult();

            result.DamageMin = damageMinInt;
            result.DamageMax = damageMaxInt;
            result.DamageAvg = (damageMinInt + damageMaxInt) / 2;


            result.KilledMin = System.Convert.ToInt32(System.Math.Floor((decimal)(result.DamageMin) / (decimal)defender.Health));
            result.KilledMax = System.Convert.ToInt32(System.Math.Floor((decimal)(result.DamageMax) / (decimal)defender.Health));
            result.KilledAvg = System.Convert.ToInt32(System.Math.Floor((decimal)(result.DamageAvg) / (decimal)defender.Health));

            log.WriteLine("Left {0} damage for {1} from previous rounds", damageLeft, defenderRef.Name);

            if (damageLeft == 0)
            {
                damageLeft = defender.Health;
            }

            if (result.KilledMin > defenderCount)
            {
                result.KilledMin = defenderCount;
            }
            else
            {
                result.DamageLeftMin = damageLeft - result.DamageMin % defender.Health;
                if (result.DamageLeftMin <= 0)
                {
                    if (result.DamageLeftMin < 0)
                    {
                        result.DamageLeftMin = defender.Health + result.DamageLeftMin;
                    }
                    ++result.KilledMin;
                }
            }

            if (result.KilledMax > defenderCount)
            {
                result.KilledMax = defenderCount;
            }
            else
            {
                result.DamageLeftMax = damageLeft - result.DamageMax % defender.Health;
                if (result.DamageLeftMax <= 0)
                {
                    if (result.DamageLeftMax < 0)
                    {
                        result.DamageLeftMax = defender.Health + result.DamageLeftMax;
                    }
                    ++result.KilledMax;
                }
            }

            if (result.KilledAvg > defenderCount)
            {
                result.KilledAvg = defenderCount;
            }
            else
            {
                result.DamageLeftAvg = damageLeft - result.DamageAvg % defender.Health;
                if (result.DamageLeftAvg <= 0)
                {
                    if (result.DamageLeftAvg < 0)
                    {
                        result.DamageLeftAvg = defender.Health + result.DamageLeftAvg;
                    }
                    ++result.KilledAvg;
                }
            }

            log.WriteLine("Damage min: {0} ronuded, Damage max: {1} rounded", damageMinInt, damageMaxInt);
            log.WriteLine("{0} killed min: {1}, max: {2} {3}", attacker.Name, result.KilledMin, result.KilledMax, defender.Name);


            if (attackType == AttackType.Ranged)
            {
                log.WriteLine("Ranged attack, no Retaliations");
                result.Retaliations = 0;
            }
            else
            {
                if (attackerFactors.ContainsKey(FactorType.NoEnemyRetaliation))
                {
                    log.WriteLine("{0} has no Retaliations ability", attackerRef.Name);
                    result.Retaliations = 0;
                }
                else
                {
                    result.Retaliations = 1;
                    log.WriteLine("{0} relaties once", defenderRef.Name);
                }
            }

            if (attackType == AttackType.Melee && attackerFactors.ContainsKey(FactorType.StrikesTwice))
            {
                log.WriteLine("{0} has double attack", attackerRef.Name);
                result.DoubleAttack = true;
            }
            if (attackType == AttackType.Ranged && attackerFactors.ContainsKey(FactorType.ShootsTwice))
            {
                log.WriteLine("{0} has double attack", attackerRef.Name);
                result.DoubleAttack = true;
            }



            return(result);
        }
        private void CreatureFightSimulation(Fight fight, SimulationType type, FightSimulationResult.Info info, CreatureRef attackerRef, CreatureRef defenderRef, TextWriter log)
        {
            string val  = string.Format("Fight simulation ({2}) between {0} and {1}", attackerRef.Name, defenderRef.Name, System.Enum.GetName(type.GetType(), type));
            string dash = new string('-', val.Length);

            log.WriteLine(dash);
            log.WriteLine(val);
            log.WriteLine(dash);

            AttackType attackType         = attackerRef.AttackType[0];
            int        attackerCount      = attackerRef.Count;
            int        attackerDamageLeft = 0;
            int        defenderCount      = defenderRef.Count;
            int        defenderDamageLeft = 0;

            CreatureRef currentAttackerRef = attackerRef;
            CreatureRef currentDefenderRef = defenderRef;

            Hero attackerHero = Fight.Attacker;
            Hero defenderHero = Fight.Defender;

            int rounds = 0;

            do
            {
                log.WriteLine("ROUND: {0}", rounds + 1);
                log.WriteLine("*** Strike ***");
                FightResult strike = CreatureFight(Fight, attackerHero, defenderHero, currentAttackerRef, currentDefenderRef, attackerCount, defenderCount, log, attackType, defenderDamageLeft);
                defenderCount      = UpdateKillsCount(defenderCount, strike, type);
                defenderDamageLeft = GetLeftDamage(strike, type);
                FightResult counterStrike = new FightResult();

                int retaliations = strike.Retaliations;
                if (retaliations > 0 && defenderCount > 0)
                {
                    log.WriteLine("*** Counterstrike ***");
                    log.WriteLine("Conterstrike attack, atack type is always melee");
                    counterStrike = CreatureFight(Fight, defenderHero, attackerHero, currentDefenderRef, currentAttackerRef, defenderCount, attackerCount, log, AttackType.Melee, attackerDamageLeft);
                    --retaliations;
                    attackerCount      = UpdateKillsCount(attackerCount, counterStrike, type);
                    attackerDamageLeft = GetLeftDamage(counterStrike, type);
                }
                if (strike.DoubleAttack && attackerCount > 0)
                {
                    log.WriteLine("*** Double Attack ***");
                    FightResult secondStrike = CreatureFight(Fight, attackerHero, defenderHero, currentAttackerRef, currentDefenderRef, attackerCount, defenderCount, log, attackType, defenderDamageLeft);
                    defenderCount      = UpdateKillsCount(defenderCount, secondStrike, type);
                    defenderDamageLeft = GetLeftDamage(secondStrike, type);
                    MergeFightResult(strike, secondStrike);
                }
                if (retaliations > 0 && defenderCount > 0)
                {
                    log.WriteLine("*** Counterstrike ***");
                    log.WriteLine("Conterstrike attack, atack type is always melee");
                    FightResult secondCounterStrike = CreatureFight(Fight, defenderHero, attackerHero, currentDefenderRef, currentAttackerRef, defenderCount, attackerCount, log, AttackType.Melee, attackerDamageLeft);
                    --retaliations;
                    attackerCount      = UpdateKillsCount(attackerCount, secondCounterStrike, type);
                    attackerDamageLeft = GetLeftDamage(secondCounterStrike, type);
                    MergeFightResult(counterStrike, secondCounterStrike);
                }

                if (attackerRef.AttackType[0] == AttackType.Melee || defenderRef.AttackType[0] == AttackType.Melee)
                {
                    log.WriteLine("One of the fighers did melee attack, rest of the battle is melee.");
                    attackType = AttackType.Melee;
                }

                // store info about first attack
                if (rounds == 0)
                {
                    info.Strike = strike;
                    switch (type)
                    {
                    case SimulationType.MinimalDamage:
                        info.CounterMin = counterStrike;
                        break;

                    case SimulationType.MaximalDamage:
                        info.CounterMax = counterStrike;
                        break;

                    case SimulationType.AverageDamage:
                        info.CounterAvg = counterStrike;
                        break;

                    default:
                        break;
                    }
                }
                CreatureRef tmp        = currentDefenderRef;
                int         countTmp   = defenderCount;
                int         dmgLeftTmp = defenderDamageLeft;
                Hero        heroTmp    = defenderHero;

                currentDefenderRef = currentAttackerRef;
                currentAttackerRef = tmp;

                defenderCount = attackerCount;
                attackerCount = countTmp;

                defenderDamageLeft = attackerDamageLeft;
                attackerDamageLeft = dmgLeftTmp;

                defenderHero = attackerHero;
                attackerHero = heroTmp;

                ++rounds;
            }while (attackerCount > 0 && defenderCount > 0);

            FightFinalResult res = new FightFinalResult();

            if (rounds % 2 > 0)
            {
                res.AttackerLeft = defenderCount;
                res.DefenderLeft = attackerCount;
            }
            else
            {
                res.AttackerLeft = attackerCount;
                res.DefenderLeft = defenderCount;
            }
            res.Rounds = rounds;

            switch (type)
            {
            case SimulationType.MinimalDamage:
                info.FinalMin = res;
                break;

            case SimulationType.MaximalDamage:
                info.FinalMax = res;
                break;

            case SimulationType.AverageDamage:
                info.FinalAvg = res;
                break;

            default:
                break;
            }
        }