private double[] simpleSimulation(Stats[] partyStats, int frontRowSeparator)
        {
            var partyHpLoss = new double[partyStats.Length];
            double monsterHp = monster.HP;

            while(partyHpLoss.Any(x => x < 1) && monsterHp > 0)
            {
                frontRowSeparator = partyHpLoss.Take(frontRowSeparator).Any(x => x < 1) ? frontRowSeparator : partyStats.Length;
                int frontRowCount = partyHpLoss.Take(frontRowSeparator).Count(x => x < 1);

                for(int i = 0; i < partyStats.Length; i++)
                {
                    if (partyHpLoss[i] >= 1)
                        continue;

                    double critRate = 1 - Math.Pow(1 - partyStats[i].CriticalRate, 1);
                    monsterHp -= partyStats[i].Damage * (1 + critRate) * partyStats[i].Accuracy / 2;

                    if (i < frontRowSeparator)
                        partyHpLoss[i] += (
                            monster.Attack / (1 + partyStats[i].Defense) +
                            monster.MagicAttack / (1 + partyStats[i].MagicDefense)
                        ) / (2 * frontRowCount * partyStats[i].Hp);

                    if (partyHpLoss[i] > 1)
                        partyHpLoss[i] = 1;
                }
            }

            var totalHp = new double[partyStats.Length + 1];
            Array.Copy(partyHpLoss, totalHp, partyStats.Length);
            totalHp[partyStats.Length] = (monsterHp < 0) ? 0 : (monsterHp /  monster.HP);

            return totalHp;
        }
        public Stats(Stats original)
        {
            this.OriginalItem = original.OriginalItem;

            this.Damage = original.Damage;
            this.Accuracy = original.Accuracy;
            this.CriticalRate = original.CriticalRate;
            this.Defense = original.Defense;
            this.MagicDefense = original.MagicDefense;
            this.Hp = original.Hp;
        }
        protected ASimulator(Monster monster, int heroLevel, int itemLevel)
        {
            this.HeroLevel = heroLevel;
            this.monster = monster;

            foreach (var hero in Library.Heroes)
            {
                var relevantItems = ItemFilter.RelevantFor(hero, itemLevel, monster, StatsFilter.All);
                var items = new Stats[(int)ItemSlot.N][];
                for (int i = 0; i < relevantItems.Count; i++)
                {
                    items[i] = relevantItems[(ItemSlot)i];
                    maxItemChoices = Math.Max(maxItemChoices, items[i].Length);
                }

                AllItems.Add(hero, items);
            }
        }
        public override double[] Run(IList<HeroBuild> builds, int frontCount)
        {
            var partyStats = new Stats[builds.Count];
            var partyHpLoss = new double[builds.Count];
            var totalHpLoss = new double[builds.Count];
            for(int i = 0; i < builds.Count; i++)
                partyStats[i] = this.HeroStats(builds[i]);

            if (exhaustive)
                takeTurn(
                    totalHpLoss,
                    partyStats,
                    partyHpLoss,
                    monster.HP,
                    frontCount,
                    frontCount,
                    0,
                    1
                );
            else
                totalHpLoss = simpleSimulation(partyStats, frontCount);

            return totalHpLoss;
        }
        protected Stats HeroStats(HeroBuild build)
        {
            var hero = Library.Heroes[build.HeroType];
            double potionStrength = Math.Max(10, HeroLevel) / 10.0;

            var heroStats = new Stats(hero, HeroLevel, monster);
            heroStats += enhancmentBonus(build.EnhancmentTypes[0]) * potionStrength * build.EnhancmentCounts[0];
            heroStats += enhancmentBonus(build.EnhancmentTypes[1]) * potionStrength * build.EnhancmentCounts[1];

            for(int i = 0; i < build.Items.Length; i++)
                if (AllItems[hero][i] != null)
                    heroStats += AllItems[hero][i][build.Items[i]];

            heroStats.Accuracy = Math.Min(1, heroStats.Accuracy + 0.8 / (1 + 2 * monster.Evasion / 100.0));
            heroStats.CriticalRate = Math.Min(heroStats.CriticalRate, 1);

            return heroStats;
        }
 public bool isSuperiorTo(Stats other, StatsFilter statsMask)
 {
     return (this.Damage >= other.Damage || !statsMask.HasFlag(StatsFilter.Damage)) &&
         (this.Accuracy >= other.Accuracy || !statsMask.HasFlag(StatsFilter.Accuracy)) &&
         (this.CriticalRate >= other.CriticalRate || !statsMask.HasFlag(StatsFilter.CriticalRate)) &&
         (this.Defense >= other.Defense || !statsMask.HasFlag(StatsFilter.Defense)) &&
         (this.MagicDefense >= other.MagicDefense || !statsMask.HasFlag(StatsFilter.MagicDefense)) &&
         (this.Hp >= other.Hp || !statsMask.HasFlag(StatsFilter.Hp));
 }
        private void takeTurn(double[] totalHpLoss, Stats[] partyStats, double[] partyHpLoss, double monsterHp, int frontRowSeparator, int frontRowCount, int currentHero, double stateChance)
        {
            if (partyHpLoss.All(x => x >= 1))
            {
                addHpLoss(totalHpLoss, partyHpLoss, stateChance);
                return;
            }

            while(currentHero < partyStats.Length && partyHpLoss[currentHero] >= 1)
                currentHero++;

            if (currentHero >= partyStats.Length)
            {
                frontRowSeparator = partyHpLoss.Take(frontRowSeparator).Any(x => x < 1) ? frontRowSeparator : partyStats.Length;
                frontRowCount = partyHpLoss.Take(frontRowSeparator).Count(x => x < 1);
                currentHero = 0;
            }

            while(partyHpLoss[currentHero] >= 1)
                currentHero++;

            if (partyStats[currentHero].Accuracy < 1)
            {
                takeTurn(totalHpLoss, partyStats,
                         monsterTurn(partyStats, partyHpLoss, frontRowCount, currentHero),
                         monsterHp, frontRowSeparator, frontRowCount, currentHero + 1,
                         stateChance * (1 - partyStats[currentHero].Accuracy));
            }

            stateChance *= partyStats[currentHero].Accuracy;

            if (partyStats[currentHero].CriticalRate > 0)
            {
                if (monsterHp > partyStats[currentHero].Damage * 2)
                    takeTurn(totalHpLoss, partyStats,
                             monsterTurn(partyStats, partyHpLoss, frontRowCount, currentHero),
                             monsterHp - partyStats[currentHero].Damage * 2,
                             frontRowSeparator, frontRowCount, currentHero + 1,
                             stateChance * partyStats[currentHero].CriticalRate);
                else
                    addHpLoss(totalHpLoss, partyHpLoss, stateChance * partyStats[currentHero].CriticalRate);
            }

            if (partyStats[currentHero].CriticalRate < 1)
            {
                if (monsterHp > partyStats[currentHero].Damage)
                    takeTurn(totalHpLoss, partyStats,
                             monsterTurn(partyStats, partyHpLoss, frontRowCount, currentHero),
                             monsterHp - partyStats[currentHero].Damage,
                             frontRowSeparator, frontRowCount, currentHero + 1,
                             stateChance * (1 - partyStats[currentHero].CriticalRate));
                else
                    addHpLoss(totalHpLoss, partyHpLoss, stateChance * (1 - partyStats[currentHero].CriticalRate));
            }
        }