Startsidan  ▸  Texter  ▸  Teknikblogg

Anders Hesselbom

Programmerare, skeptiker, sekulärhumanist, antirasist.
Författare till bok om C64 och senbliven lantis.
Röstar pirat.

Evolutionary sudoku

2013-11-07

This is a successful attempt to use an evolutionary algorithm to generate a sudoku board. A complete board is created after usually less than a million iterations, sometimes after a couple of hundred generations.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
   class Program
   {
      static void Main(string[] args)
      {
         var s = new Sudoku();
         s.Display();
         Console.Write("Done. Press enter to quit.");
         Console.ReadLine();
      }
   }

   class Sudoku
   {
      int[,] field = new int[9,9];
      const int mutation_speed = 3;
      const int dead_end_limit = 60000;

      public Sudoku()
      {
         var r = new Random();
         //Create a list of possible characters on the game field.
         var l = new List<int>();
         for (var i = 1; i < 10; i++)
            for (var j = 1; j < 10; j++)
               l.Add(i);
         //Place them on the field randomly.
         for (var i = 0; i < 9; i++)
            for (var j = 0; j < 9; j++) {
               var index = r.Next(0, l.Count);
               var value = l[index]; l.RemoveAt(index);
               this.field[j, i] = value;
            }
         //Prepare for score counting. Low is better.
         int score = int.MaxValue, iterations = 0, generations = 0,
         iterations_since_last_climb = 0;
         //Define how a mutation works - a random element swap.
         Action<int[,]> mutate = a => {
            var speed = (score > 2 ? mutation_speed : 1);
            for (var i = 0; i < speed; i++) {
               int x1 = r.Next(0, 9), y1 = r.Next(0, 9),
                 x2 = r.Next(0, 9), y2 = r.Next(0, 9);
               var v = a[x1, y1]; a[x1, y1] = a[x2, y2]; a[x2, y2] = v;
            }
         };
         //Show progress.
         Action status = () => {
            Console.Title = string
               .Format("Score: {0:00} - Generations: {1:000} - " +
               "Iterations: {2:000000}" +
               " - Since last climb: {3:00000}",
               score, generations, iterations, iterations_since_last_climb);
         };
         //Let evolution work.
         do {
            iterations++;
            //If adaptation has stopped, the parent must mutate.
            if (iterations_since_last_climb >= dead_end_limit) {
               mutate(this.field);
               score = this.GetScore(this.field);
            }
            //Breed two new sudokos and modify them slightly.
            int[,] child1 = (int[,])this.field.Clone();
            int[,] child2 = (int[,])this.field.Clone();
            mutate(child1); mutate(child2);
            //Check new scores.
            var child1score = this.GetScore(child1);
            var child2score = this.GetScore(child2);
            //Keep the best one, if any.
            Action<int[,], int> keep = (a, s) => {
               this.field = (int[,])a.Clone(); score = s; generations++;
               iterations_since_last_climb = 0; };
            if (child1score < score)
               keep(child1, child1score);
            else if (child2score < score)
               keep(child2, child2score);
            else
               iterations_since_last_climb++;
            System.Threading.Thread.Yield();
            if ((iterations % 1000) == 0)
               status();
#if DEBUG
            this.Display();
            Console.CursorTop = 0;
#endif
         } while (score > 0);
         status();
      }

      private int GetScore(int[,] p)
      {
         //Define how fitness is determined. A better solution is wanted.
         Func<int[,], int, int, bool> checkrow = (arr, x, y) => {
            var val = arr[x, y]; var count = 0;
            for (var i = 0; i < 9; i++)
               if (arr[i, y] == val) count++;
            return (count == 1);
         };
         Func<int[,], int, int, bool> checkcol = (arr, x, y) => {
            var val = arr[x, y]; var count = 0;
            for (var i = 0; i < 9; i++)
               if (arr[x, i] == val) count++;
            return (count == 1);
         };
         Func<int[,], int, int, bool> checksection = (arr, x, y) => {
            var section_x = x; while (!((section_x % 3) == 0)) section_x--;
            var section_y = y; while (!((section_y % 3) == 0)) section_y--;
            var val = arr[x, y]; var count = 0;
            for (var row = section_x; row < section_x + 3; row++)
               for (var col = section_y; col < section_y + 3; col++)
                  if (arr[col, row] == val) count++;
            return (count == 1);
         };
         var score = 0;
         for (var i = 0; i < 9; i++)
            for (var j = 0; j < 9; j++) {
               if (!(checkrow(p, j, i))) score++;
               if (!(checkcol(p, j, i))) score++;
               if (!(checksection(p, j, i))) score++;
            }
         return score;
      }

      public void Display()
      {
         for (var y = 0; y < 9; y++) {
            for (var x = 0; x < 9; x++)
               Console.Write(field[x, y]);
            Console.WriteLine();
         }
      }

   }
}

Sudoku

Update 2023-02-09: .NET 7.0 source code

Categories: C#, Geeky, Microsoft .NET

One response to “Evolutionary sudoku”

Leave a Reply

Your email address will not be published. Required fields are marked *



En kopp kaffe!

Bjud mig på en kopp kaffe (20:-) som tack för bra innehåll!

Bjud på en kopp kaffe!

Om...

Kontaktuppgifter, med mera, finns här.

Följ mig

Twitter Instagram
GitHub RSS

Public Service

Folkbildning om public service.

Hem   |   linktr.ee/hesselbom   |   winsoft.se   |   80tal.se   |   Filmtips