Exemple #1
0
        //  private List<Organism> Hive;
        //  private OrganismBase TheBest;

        /// <summary>
        /// Attempts find an optimal solution to a sudoku puzzle with the a hive of organisms
        /// The organisms each have a total number of allowed Epochs (generations) to find the optimal solution.
        /// If after the max epochs is reached without an optimal solution, they are culled and another hive is started within the maximum number of specified extinctions
        /// NOTE: Combinatorial evolution doesn't guarantee and optimal solution will be found.
        /// </summary>
        /// <param name="problem">int[][]</param>
        /// <param name="numOrganisms">how many virtual organism are in the hive at a given time, each representing a possible solution</param>
        /// <param name="maxEpochs">number of iterations (generations) each organism can run through to find the optimal solution</param>
        /// <param name="maxExtinctions">if an optimal solution isn't found after the maxEpochs, all organisms are killed and the cycle is restarted if not more than maxExtinctions.</param>
        /// <returns>The best solution (int[][]) found within the specified time.</returns>
        public async Task <int[][]> SolveB4ExtinctCount_Async(int[][] problem, int numOrganisms, int maxEpochs, int maxExtinctions)
        {
            OriginalMatrix = SudokuTool.DuplicateMatrix(problem);
            // wrapper for SolveEvo()
            var err  = int.MaxValue;
            var seed = 0;

            int[][] bestSolution = null;
            var     extinctions  = 0;
            int     epochs       = 0;

            // generate a new hive of organisms with a number of epochs to try for an error-free solution or until we've killed them off too often
            while (err != 0 && extinctions < maxExtinctions)
            {
                _rnd         = new Random(seed);
                bestSolution = await SolveAsync(problem, numOrganisms, maxEpochs).ConfigureAwait(true);

                err = SudokuTool.Errors(bestSolution);

                extinctions++; // keep track of the attempts
                Debug.WriteLine($"{err} errors prior to {extinctions} extinction level events");
                ++seed;        // increment the seed
            }

            _stats.extinctions = extinctions;
            return(bestSolution);
        }
Exemple #2
0
        /// <summary>
        /// Core engine for finding a solution. Running this is going to spawn a hive of organisms (some percentage of workers, the rest explorers)
        ///
        /// </summary>
        /// <param name="problem"></param>
        /// <param name="numOrganisms"></param>
        /// <param name="maxEpochs"></param>
        /// <returns></returns>
        private async Task <int[][]> SolveAsync(int[][] problem, int numOrganisms, int maxEpochs)
        {
            // start fresh
            var Hive = new List <Organism>(new Organism[numOrganisms]);

            // the best organisms in each epoch will be stored here
            var TheBest = new OrganismBase(0, null, Int32.MaxValue, 0);

            // initialize combinatorial Organisms with 90% of them workers and the rest explorers
            // Explorers always get a new random matrix to test against.
            var numWorker   = (int)(numOrganisms * 0.90);
            var numExplorer = numOrganisms - numWorker;


            // fill the hive with new organisms having a random puzzle and known error count and also if it was the best yet
            for (var i = 0; i < numOrganisms; ++i)
            {
                var orgType = i < numWorker ? OrganismTypes.Worker : OrganismTypes.Explorer;

                // get a new random puzzle and count the errors
                int[][] rndMatrix = await Task.FromResult(SudokuTool.GetRandomMatrix(problem, _rnd));

                var currentErrors = await Task.FromResult(SudokuTool.Errors(rndMatrix));

                // create a new organism, give it the random puzzle and errors
                Hive[i] = new Organism(orgType, rndMatrix, currentErrors, 0);
                _stats.organismsBorn++;

                // track the best so far puzzle and copy it to TheBest
                if (currentErrors < TheBest.Error)
                {
                    TheBest.Error  = currentErrors;
                    TheBest.Matrix = await Task.FromResult(SudokuTool.DuplicateMatrix(rndMatrix));

                    _stats.randomWasTheBest++; // this should average out at some point?
                    // Splash();
                }
            }

            // main loop - start the generations (epochs)
            var epoch = 0;

            while (epoch < maxEpochs)
            {
                // keep a little log for every 1000 generations
                if (epoch % 1000 == 0)
                {
                    Debug.WriteLine("epoch = " + epoch);
                    Debug.WriteLine("best error = " + TheBest.Error);
                }

                // if we somehow have no errors (optimal solution!) then just stop now.
                if (TheBest.Error == 0) // solution found
                {
                    break;
                }

                // process each organism and give explorers random solutions and evolve workers
                // (with for loop)...why not foreach? because if it ages out we want to put a new organism in place
                for (var i = 0; i < numOrganisms; ++i)
                {
                    // we treat Explorers differently than Workers. Explorers always get a new random matrix to test against. Workers evolve, mutate, age +/- or die of old age
                    switch (Hive[i].Type)
                    {
                    case OrganismTypes.Explorer:
                        // give it a new random matrix
                        int[][] rndMatrix = await Task.FromResult(SudokuTool.GetRandomMatrix(problem, _rnd));

                        Hive[i].Matrix = await Task.FromResult(SudokuTool.DuplicateMatrix(rndMatrix));

                        Hive[i].Error = await Task.FromResult(SudokuTool.Errors(rndMatrix));

                        if (Hive[i].Error < TheBest.Error)
                        {
                            TheBest.Error  = Hive[i].Error;
                            TheBest.Matrix = SudokuTool.DuplicateMatrix(rndMatrix);
                            _stats.randomWasTheBest++;
                            // Splash();
                        }
                        break;

                    case OrganismTypes.Worker:

                        // mutate the matrix, pick a random block and pick two random cells and swap them.
                        int[][] evolving = await Task.FromResult(SudokuTool.EvolveMatrixAsync(problem, Hive[i].Matrix, _rnd));

                        var evolutionErrors = await Task.FromResult(SudokuTool.Errors(evolving));

                        var  probability  = _rnd.NextDouble();
                        var  rareMutation = probability < 0.001;
                        bool evoWorked    = evolutionErrors < Hive[i].Error;

                        // if the evolution is better (natural selection) or if this is a rareMutation, take it
                        if (evoWorked || rareMutation)     // better, or a mistake
                        {
                            // positive result to evolution provides refreshed genepool and resets age
                            if (evoWorked)
                            {
                                Hive[i].Age = 0;
                                _stats.evolutionWorked++;
                            }

                            // if you didn't get the better end of the deal
                            if (rareMutation && !evoWorked)
                            {
                                _stats.mutationFailed++;
                            }

                            Hive[i].Matrix = await Task.FromResult(SudokuTool.DuplicateMatrix(evolving));     // by value

                            Hive[i].Error = evolutionErrors;

                            if (Hive[i].Error < TheBest.Error)
                            {
                                TheBest.Error  = Hive[i].Error;
                                TheBest.Matrix = await Task.FromResult(SudokuTool.DuplicateMatrix(evolving));

                                _stats.bestMutationsEver++;
                                // Splash();
                            }
                        }
                        else     // evolved code is not better and there was no chance for forced mutation
                        {
                            // age this organism
                            Hive[i].Age++;

                            if (Hive[i].Age > 1000)     // die - if so, get a random to replace
                            {
                                _stats.diedOfOldAge++;

                                // create a new replacement for the hive.
                                var m = await Task.FromResult(SudokuTool.GetRandomMatrix(problem, _rnd));

                                var me = await Task.FromResult(SudokuTool.Errors(m));

                                Hive[i] = new Organism(0, m, me, 0);
                                _stats.organismsBorn++;
                            }
                        }
                        break;
                    }
                } // each organism


                // find the index of the best worker
                var bestWkrIndex = await Task.FromResult(SudokuTool.GetBestWorkerIndex(Hive, numWorker));

                var bestWorker = Hive[bestWkrIndex];

                // find the index of the best explorer
                var bestExpIndex = await Task.FromResult(SudokuTool.GetBestExplorerIndex(Hive, numWorker));

                var bestExplorer = Hive[bestExpIndex];

                // find the index of the worst worker
                var worstWkrIndex = await Task.FromResult(SudokuTool.GetWorstWorkerIndex(Hive, numWorker));


                // have a  50/50 chance for each block in 2nd organism will be blended into 1st organism
                var babyOrg = await Task.FromResult(SudokuTool.MatingResult(bestWorker.Matrix, bestExplorer.Matrix, 0.50, _rnd));

                _stats.matingPairs++;
                var babyErr = await Task.FromResult(SudokuTool.Errors(babyOrg));

                var genNext = new Organism(OrganismTypes.Worker, babyOrg, babyErr, 0)
                {
                    // generations are only incremented if one of the parents have a GeneMarker
                    Generation = incrementGeneration(bestWorker, bestExplorer),
                    // GeneMarker = "GenX" // might use this if you happened to want to see details later.
                }; _stats.organismsBorn++;

                // replace the worst worker with the next generation of worker
                Hive[worstWkrIndex] = genNext;
                _stats.culledCount++;

                if (babyErr < TheBest.Error)
                {
                    TheBest.Error  = babyErr;
                    TheBest.Matrix = await Task.FromResult(SudokuTool.DuplicateMatrix(babyOrg));

                    _stats.bestBabies++;
                    // Splash();
                }
                ++epoch;
                _stats.epochsCompleted++;
            } // while


            return(TheBest.Matrix);
        } // SolveEvo