public Stats(StatsFilter bonusType, Monster vsMonster)
        {
            this.OriginalItem = null;

            this.Damage = 0;
            this.Accuracy = 0;
            this.CriticalRate = 0;
            this.Defense = 0;
            this.MagicDefense = 0;
            this.Hp = 0;

            if (bonusType == StatsFilter.Damage)
                this.Damage = 1 / (1 + vsMonster.Defense / 100.0);
            if (bonusType == StatsFilter.MagicAttack)
                this.Damage = 1 / (1 + vsMonster.MagicDefense / 100.0);

            if (bonusType == StatsFilter.Accuracy)
                this.Accuracy = 0.01;
            if (bonusType == StatsFilter.CriticalRate)
                this.CriticalRate = 0.01;
            if (bonusType == StatsFilter.Defense)
                this.Defense = 0.01;
            if (bonusType == StatsFilter.MagicDefense)
                this.MagicDefense = 0.01;
            if (bonusType == StatsFilter.Hp)
                this.Hp = 1;
        }
        private static Dictionary<ItemSlot, Stats[]> removeRedundantItems(IEnumerable<Item> items, Hero hero, Monster monster, StatsFilter statsMask)
        {
            var itemGroups = items.Select(x => new Stats(x, hero, monster)).GroupBy(x => x.OriginalItem.Slot);
            var filteredItems = new Dictionary<ItemSlot, Stats[]>();

            foreach (var itemGroup in itemGroups)
            {
                var filteredList = new List<Stats>();
                foreach (var item in itemGroup)
                {
                    bool redundant = false;
                    var superiorTo = new HashSet<Stats>();
                    foreach (var filteredItem in filteredList)
                    {
                        if (item.isSuperiorTo(filteredItem, statsMask))
                            superiorTo.Add(filteredItem);
                        redundant |= item.isInferiorTo(filteredItem, statsMask);
                    }
                    foreach (var toRemove in superiorTo)
                        filteredList.Remove(toRemove);
                    if (!redundant)
                        filteredList.Add(item);
                }

                if (filteredList.Count == 0)
                {
                    var maxDamage = itemGroup.Max(x => x.Damage);
                    filteredList.Add(itemGroup.First(x => Math.Abs(x.Damage - maxDamage) < 1e-3));
                }

                filteredItems.Add(itemGroup.Key, filteredList.ToArray());
            }

            return filteredItems;
        }
        public Stats(Item item, Hero forHero, Monster vsMonster)
        {
            this.OriginalItem = item;

            this.Damage =
                (item.Attack + item.Strength * forHero.Strength) / (1 + vsMonster.Defense / 100.0) +
                (item.MagicAttack + item.Intelligence * forHero.Intelligence) / (1 + vsMonster.MagicDefense / 100.0);

            this.Accuracy = (item.Accuracy + item.Dexterity * forHero.Dexterity) / 100.0;
            this.CriticalRate = item.CriticalRate / 100.0;
            this.Defense = item.Defense / 100.0;
            this.MagicDefense = item.MagicDefense / 100.0;
            this.Hp = item.HP;
        }
        public Stats(Hero hero, int heroLevel, Monster vsMonster)
        {
            this.OriginalItem = null;
            heroLevel--; //Heros start at level 1 and gain per level stats afterwards

            this.Damage =
                (hero.StartAttack + hero.LevelAttack * heroLevel) / (1 + vsMonster.Defense / 100.0) +
                (hero.StartMagicAttack + hero.LevelMagicAttack * heroLevel) / (1 + vsMonster.MagicDefense / 100.0);

            this.Accuracy = (hero.StartAccuracy + hero.LevelAccuracy * heroLevel) / 100.0;
            this.CriticalRate = (hero.StartCriticalRate + hero.LevelCriticalRate * heroLevel) / 100.0;
            this.Defense = (hero.StartDefense + hero.LevelDefense * heroLevel) / 100.0;
            this.MagicDefense = (hero.StartMagicDefense + hero.LevelMagicDefense * heroLevel) / 100.0;
            this.Hp = hero.StartHP + hero.LevelHP * heroLevel;
        }
        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 static Dictionary<ItemSlot, Stats[]> RelevantFor(Hero hero, int maxItemLevel, Monster monster, StatsFilter statsMask)
 {
     return removeRedundantItems(
         MakeItems(maxItemLevel),
         hero, monster, statsMask);
 }
 public PartySimulator(Monster monster, int heroLevel, int itemLevel, bool exhaustiveSimulation)
     : base(monster, heroLevel, itemLevel)
 {
     this.exhaustive	= exhaustiveSimulation;
 }
 public SingleHeroSimulator(Monster monster, int heroLevel, int itemLevel)
     : base(monster, heroLevel, itemLevel)
 {
 }