private static MustBeOrderedList <ItemInfo> TakeBest(int count, MustBeOrderedList <ItemInfo> items)
        {
            var result = new MustBeOrderedList <ItemInfo>();

            foreach (var item in items)
            {
                if (item.Count >= count)
                {
                    result.Add(new ItemInfo(item.Item, count));
                    break;
                }

                result.Add(item);
                count -= item.Count;
            }

            return(result);
        }
        private static IDictionary <ChangeType, MustBeOrderedList <ItemInfo> > GroupByChangeType(
            MustBeOrderedList <ItemInfo> items)
        {
            var result = new Dictionary <ChangeType, MustBeOrderedList <ItemInfo> >();

            foreach (var item in items)
            {
                Debug.Assert(item.Item.Effect != null, "item.Item.Effect != null");
                if (result.TryGetValue(item.Item.Effect.ChangeType, out var list))
                {
                    list.Add(item);
                }
                else
                {
                    result[item.Item.Effect.ChangeType] = new MustBeOrderedList <ItemInfo> {
                        item
                    };
                }
            }

            return(result);
        }
        private List <ItemInfo> ActivateItems(StructFlag <StatsProperty> prop, MustBeOrderedList <ItemInfo> items)
        {
            if (!ActiveProportions.TryGetValue(prop, out var limit))
            {
                return(new List <ItemInfo>());
            }

            var groups = GroupByChangeType(items);
            var useSet = DetectUseSet(prop, groups);

            var max = limit;

            if (useSet)
            {
                max--;
            }

            var selectedItems = new List <ItemInfo>();

            if (useSet)
            {
                selectedItems.AddRange(TakeBest(1, groups[ChangeType.Set]));
            }

            if (!groups.ContainsKey(ChangeType.Add) && !groups.ContainsKey(ChangeType.Multiply))
            {
                // No multiplication and addition. Only set, maybe.
                // So just do nothing, because DetectUseSet() will handle set.
                return(selectedItems);
            }

            if (!groups.ContainsKey(ChangeType.Add))
            {
                // Only multiplication (and set, maybe)
                selectedItems.AddRange(TakeBest(max, groups[ChangeType.Multiply]));
                return(selectedItems);
            }

            if (!groups.ContainsKey(ChangeType.Multiply))
            {
                // Only addition (and set, maybe)
                selectedItems.AddRange(TakeBest(max, groups[ChangeType.Add]));
                return(selectedItems);
            }

            // Both multiplication and addition (and set, maybe)

            /*
             * Наилучшая комбинация будет если сначала складывать, а потом умножать, т.к. (x + a) * b > (x * b) + a
             * Этот алгоритм сначала пробует взять максимальное количество умножения,
             * потом в каждой итерации добавляет одно сложение. В конце концов пробует только сложения.
             * Работает за O(N^2), где N -- max (ActiveProportions[prop])
             */
            var selectedItemsBase = selectedItems.ToList();
            var bestStats         = User.Info.BaseStats;
            var bestItems         = new List <ItemInfo>();

            for (var addCount = 0; addCount < max; addCount++)
            {
                selectedItems = selectedItemsBase.ToList();

                var mulCount = max - addCount;
                selectedItems.AddRange(TakeBest(addCount, groups[ChangeType.Add]));
                selectedItems.AddRange(TakeBest(mulCount, groups[ChangeType.Multiply]));

                var currentStats = UserInfo.ApplyItems(User.Info.BaseStats, selectedItems);

                if (StatsEffect.Compare(prop, bestStats, currentStats) == 1)
                {
                    bestStats = currentStats;
                    bestItems = selectedItems.ToList();
                }
            }

            return(bestItems);
        }