private static EnemyList RecursivelyFindBestEnemyCombination(List <EnemyTypes> availableEnemyTypes, List <double> partialInput, EnemyList existingList, double pointsRemaining)
        {
            if (pointsRemaining == 0 || existingList.TotalEnemies >= MaxEnemies)
            {
                return(existingList);
            }

            var       bestFitness = 0.0;
            EnemyList bestList    = null;

            foreach (var enemyType in availableEnemyTypes)
            {
                var currentPoints   = pointsRemaining;
                var currentList     = existingList.DeepClone();
                var enemyPointValue = enemyType.PointValue();

                if (enemyPointValue > currentPoints)
                {
                    continue;
                }

                currentPoints -= enemyPointValue;
                currentList.Add(enemyType);

                if (currentPoints > 0 && currentList.TotalEnemies < MaxEnemies)
                {
                    currentList = RecursivelyFindBestEnemyCombination(availableEnemyTypes, partialInput, currentList, currentPoints);
                    RecursionCount++;
                }

                double fitnessOfList;

                if (RecordedFitness.ContainsKey(currentList))
                {
                    fitnessOfList = RecordedFitness[currentList];
                }
                else
                {
                    fitnessOfList = ScoreEnemyList(currentList, partialInput);
                    RecordedFitness.Add(currentList, fitnessOfList);
                }

                if (fitnessOfList < bestFitness)
                {
                    continue;
                }

                bestFitness = fitnessOfList;
                bestList    = currentList;
            }

            return(bestList);
        }
        private static EnemyList GreedilyFindBestEnemyCombination(List <EnemyTypes> availableEnemyTypes,
                                                                  List <double> partialInput, double pointsAvailable)
        {
            var enemyList     = new EnemyList();
            var minPointValue = availableEnemyTypes.Min(e => e.PointValue());

            while (pointsAvailable >= minPointValue && enemyList.TotalEnemies < MaxEnemies)
            {
                var bestFitnessImprovement = 0.0;
                var bestEnemyType          = availableEnemyTypes.MinBy(e => e.PointValue());
                var currentFitness         = enemyList.TotalEnemies == 0 ? 0 : ScoreEnemyList(enemyList, partialInput);

                foreach (var enemyType in availableEnemyTypes)
                {
                    var enemyPointValue = enemyType.PointValue();
                    if (enemyPointValue > pointsAvailable)
                    {
                        continue;
                    }

                    var tempEnemyList = enemyList.DeepClone();
                    tempEnemyList.Add(enemyType);

                    var fitnessOfList = ScoreEnemyList(tempEnemyList, partialInput);

                    var enemyFitnessImprovement = fitnessOfList - currentFitness;
                    enemyFitnessImprovement /= enemyPointValue;

                    if (enemyFitnessImprovement < bestFitnessImprovement)
                    {
                        continue;
                    }

                    bestEnemyType          = enemyType;
                    bestFitnessImprovement = enemyFitnessImprovement;
                }

                pointsAvailable -= bestEnemyType.PointValue();
                enemyList.Add(bestEnemyType);
            }
            return(enemyList);
        }