public static Chromosome <TGene, TFitness> GetBest(GetFitnessDelegate getFitness, int targetLen,
                                                           TFitness optimalFitness, IReadOnlyList <TGene> geneSet, DisplayDelegate display,
                                                           MutateGeneDelegate customMutate = null, CreateDelegate customCreate = null, int?maxAge = null)
        {
            Chromosome <TGene, TFitness> FnMutate(Chromosome <TGene, TFitness> parent) =>
            customMutate == null
                    ? Mutate(parent, geneSet, getFitness)
                    : MutateCustom(parent, customMutate, getFitness);

            Chromosome <TGene, TFitness> FnGenerateParent()
            {
                if (customCreate == null)
                {
                    return(GenerateParent(targetLen, geneSet, getFitness));
                }

                var genes = customCreate();

                return(new Chromosome <TGene, TFitness>(genes, getFitness(genes)));
            }

            foreach (var improvement in GetImprovement(FnMutate, FnGenerateParent, maxAge))
            {
                display(improvement);
                if (optimalFitness.CompareTo(improvement.Fitness) <= 0)
                {
                    return(improvement);
                }
            }

            throw new UnauthorizedAccessException();
        }
        private static Chromosome <TGene, TFitness> MutateCustom(Chromosome <TGene, TFitness> parent,
                                                                 MutateGeneDelegate customMutate, GetFitnessDelegate getFitness)
        {
            var childGenes = parent.Genes.ToList();

            customMutate(childGenes);
            var fitness = getFitness(childGenes);

            return(new Chromosome <TGene, TFitness>(childGenes, fitness));
        }
        public static Chromosome <TGene, TFitness> GetBest(GetFitnessDelegate getFitness, int targetLen,
                                                           TFitness optimalFitness, IReadOnlyList <TGene> geneSet, DisplayDelegate display,
                                                           MutateGeneDelegate customMutate = null, CreateDelegate customCreate = null, int?maxAge = null,
                                                           int poolSize = 1, CrossoverDelegate crossover = null, int?maxSeconds = null)
        {
            Chromosome <TGene, TFitness> FnMutate(Chromosome <TGene, TFitness> parent) =>
            customMutate == null
                    ? Mutate(parent, geneSet, getFitness)
                    : MutateCustom(parent, customMutate, getFitness);

            Chromosome <TGene, TFitness> FnGenerateParent()
            {
                if (customCreate == null)
                {
                    return(GenerateParent(targetLen, geneSet, getFitness));
                }

                var genes = customCreate();

                return(new Chromosome <TGene, TFitness>(genes, getFitness(genes), Strategy.Create));
            }

            var strategyLookup =
                new Dictionary <Strategy, StrategyDelegate>
            {
                { Strategy.Create, (parentGenes, index, parents) => FnGenerateParent() },
                { Strategy.Mutate, (parentGenes, index, parents) => FnMutate(parentGenes) },
                {
                    Strategy.Crossover, (parentGenes, index, parents) => Crossover(parentGenes.Genes, index,
                                                                                   parents, getFitness, crossover, FnMutate, FnGenerateParent)
                }
            };

            var usedStrategies =
                new List <StrategyDelegate> {
                strategyLookup[Strategy.Mutate]
            };

            if (crossover != null)
            {
                usedStrategies.Add(strategyLookup[Strategy.Crossover]);
            }

            Chromosome <TGene, TFitness> FnNewChild(Chromosome <TGene, TFitness> parent, int index,
                                                    List <Chromosome <TGene, TFitness> > parents) =>
            crossover != null
                    ? Rand.SelectItem(usedStrategies)(parent, index, parents)
                    : FnMutate(parent);

            try
            {
                foreach (var improvement in GetImprovement(FnNewChild, FnGenerateParent, maxAge, poolSize, maxSeconds))
                {
                    display(improvement);
                    var f = strategyLookup[improvement.Strategy];
                    usedStrategies.Add(f);
                    if (optimalFitness.CompareTo(improvement.Fitness) <= 0)
                    {
                        return(improvement);
                    }
                }
            }
            catch (SearchTimeoutException exception)
            {
                display(exception.Improvement);
                return(exception.Improvement);
            }

            throw new UnauthorizedAccessException();
        }