Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
I just discovered Sudoku last weekend, and I thought... I can generate these; but when I looked around online for code, I didn't find any. I found plenty of solvers, and I recognized that solving a blank grid would result in a puzzle if you had a random element in your solution method. So I set out to write my own solver/generator that did just that. This code can create and solve Sudoku puzzles of varying sizes, although not very quickly. One problem I'd like to solve is that the puzzles generated are either too easy or too hard. A puzzle tends to be too hard when there are more than one solution, and too easy when there are too many givens. This code tries to find a middle ground by testing to see if randomly solving the puzzle gets the expected solution, and requiring a minimum number of open spots. Enjoy. And yes, I know that Brovnik already posted some code (SuDoKu solver) to solve these, but my method is different (that is, more brutish). Optimizations and corrections are welcome and encouraged.
#!perl -w use strict; # Sudoku # # This set of functions allows generation of various dimensioned Sudok +u puzzles # The ResolveGrid() function can also be used to resolve partially con +structed grids, # which is to say that it can be used to solve puzzles you find elsewh +ere. # Also, due to the computational complexity (NP) of prooving that an i +ncomplete grid # leads to only one possible solution, not all generated puzzles will +have a unique # solution. This also means that it may take awhile to generate a lar +ge puzzle. # use constant Symetry => 1; # 1 => Symetrical Puzzle use constant Dim => 4; # 3 = Traditional use constant Side => Dim * Dim; # One side of the grid use constant Squared => Side * Side; # Total spaces in grid use constant MinDots => int( Squared / 2 ); # Specifying the max giv +ens. use constant Options => (1..9,0,'A'..'Z','a'..'z')[0..Side()-1]; use constant Form => "| ". join(' | ',(join(' ',('@')x Dim))x Dim) + ." |\n"; use constant Line => "+-". join('-+-',(join('-',('-')x Dim))x Dim) + ."-+\n"; # For convenience, functions which return an array of results # return a reference to that array in scalar context. # Fisher-Yates sub Shuffle { my @array = @_; if ( not defined $_[1] and UNIVERSAL::isa( $_[0], 'ARRAY' ) ) { @array = @{$_[0]}; } my $i=@array; while($i--) { my $j=int rand(1+$i); @array[$i, $j]=@array[$j, $i] } return wantarray ? @array : \@array; } # Coordinates are zero based. # Index = Crd2Index( Row, Col ) sub Crd2Index($$) { $_[0] * Side + $_[1] } sub Index2Crd($) { my $i = $_[0]; my $r = int( $i / Side ); my $c = $i % Side; return wantarray ? ( $r, $c ) : [ $r, $c ]; } # I know, I know... still, it is easier to read this way. sub DotCount($) { return $_[0] =~ s/\./\./g } # Put the grid into a ... grid. sub Format($) { die "ASSERT" unless $_[0] and length $_[0] == Squared; $^A = ""; for ( 0 .. Side - 1 ) { $^A .= Line if $_ % Dim == 0; formline Form, split //, substr( $_[0], Side * $_, Side ); } return $^A ? $^A . Line : undef; } # Determine available options for a given spot in a given grid # Avail( Grid, Index ) # Avail( Grid, Row, Col ) sub Avail($$;$) { my ( $grid, $row, $col ) = @_; ( $row, $col ) = Index2Crd( $row ) if not defined $col; my %used = map { $_ => 0 } Options; # A little error checking die "ASSERT( $row, $col )" unless Crd2Index( $row, $col ) < Square +d; die "ASSERT( '$grid' )" unless length( $grid ) == Squared; # Row and Col - Could also do Row via a split, but whatever. for ( 0 .. Side - 1 ) { ++$used{substr( $grid, Crd2Index( $_, $col ), 1 )}; ++$used{substr( $grid, Crd2Index( $row, $_ ), 1 )}; } # Now determine which square we are in my ( $x, $y ) = map { int( $_ / Dim ) * Dim } ( $col, $row ); for my $r ( $y .. $y + Dim - 1 ) { for my $c ( $x .. $x + Dim - 1 ) { ++$used{substr( $grid, Crd2Index( $r, $c ), 1 )}; } } my @result = grep { not $used{$_} } Options; return wantarray ? @result : \@result; } # Making and Solving a grid are basically the exact same thing # You don't need to shuffle the available array if you are just # solving, but it doesn't hurt anything - and it allows you to # find multiple solutions, if they exist. sub ResolveGrid(;$) { my $grid = $_[0]; $grid = '.' x Squared if not defined $grid; my @stack = ( ); my $next = 0; while ( 0 <= $next and $next < Squared ) { if ( substr( $grid, $next, 1 ) ne '.' ) { ++$next; next; } my $avail = Shuffle Avail( $grid, $next ); if ( not @{$avail} ) { die "INVALID GRID\n" if not @stack; my $prev = pop @stack; $grid = $prev->[0]; $avail = $prev->[1]; $next = $prev->[2]; } my $choice = shift @{$avail}; push @stack, [ $grid, $avail, $next ] if @{$avail}; substr( $grid, $next, 1 ) = $choice; ++$next; } return $grid; } # Could also resolve a grid recursivly, but you hit Perl's limit with # grids larger then (3x3)^2 --- that is, Dim => 3. sub ResolveGridRecursive { my $grid = $_[0]; my $next = $_[1] || 0; die "ASSERT!\nGRID = '$grid'\nNEXT='$next'\n\t" if $next < 0; $grid = '.' x Squared if not defined $grid; return $grid if $next >= length $grid; substr( $grid, $next, 1 ) = '.'; for ( Shuffle Avail( $grid, $next ) ) { substr( $grid, $next, 1 ) = $_; my $testgrid = ResolveGridRecursive( $grid, $next + 1 ); return $testgrid if defined $testgrid; } substr( $grid, $next, 1 ) = '.'; return undef; } # Generate a puzzle # sub MakePuzzle(;$) { my $soln = ResolveGrid( $_[0] ); # Resolving empty grid creates a +random solution. my $puz = $soln; my @location = Shuffle( 0 .. Squared - 1 ); my $i; while ( @location ) { $i = pop @location; substr( $puz, $i, 1 ) = '.'; last if ResolveGrid( $puz ) ne $soln; } substr( $puz, $i, 1 ) = substr( $soln, $i, 1 ) if defined $i; return $puz; } # Some people prefer symetrical puzzles. sub MakePuzzleSym(;$) { my $soln = $_[0] || ResolveGrid; # Resolving empty grid creates a +random solution. my $puz = $soln; my @location; for ( 0 .. Side - 1 ) { push @location, $_ * Side .. $_ * Side + S +ide - ( Side - $_ ) } @location = Shuffle( @location ); my ( $i, $j ); while ( @location ) { $i = pop @location; $j = Crd2Index( Index2Crd( $i )->[1], Index2Crd( $i )->[0] ); die "ASSERT( $i, $j )" if $j > Squared; substr( $puz, $i, 1 ) = '.'; substr( $puz, $j, 1 ) = '.'; last if ResolveGrid( $puz ) ne $soln; } substr( $puz, $i, 1 ) = substr( $soln, $i, 1 ) if defined $i; substr( $puz, $j, 1 ) = substr( $soln, $j, 1 ) if defined $j; return $puz; } ######## # Main # ######## # Resolving and empty grid gives you a legitimate Sudoku square # The MakePuzzle functions will do this for you, if you don't want the + solution handy. my $solution = ResolveGrid; # Now find a sufficiently 'hard' set of givens, where 'hard' is merely + a function # of how many givens you have. MakePuzzle attempts to verify that the +puzzle you # get has only the desired solution, but of course you can't really ve +rify that # without checking every possible solution. my $puzzle = $solution; while ( DotCount( $puzzle ) < MinDots ) { print "Attempt\n"; $puzzle = Symetry ? MakePuzzleSym( $solution ) : MakePuzzle( $solu +tion ); } # Now you can display the puzzle in a lovely grid print Format( $puzzle ), "\n"; # Or just dump out a simple string form, suitable for testing other so +lvers print "Puzzle : $puzzle\n"; # And, of course, the solution... just in case you want it. print "Solution: $solution\n"; __END__ Example Output for a (4x4)^2 symetrical puzzle: +---------+---------+---------+---------+ | . . C 7 | 4 . . 9 | . . E 3 | 1 . . . | | . . 5 2 | F . . . | A 6 . 1 | 9 B . . | | 9 D 8 . | . 5 B . | . 2 0 . | . . . . | | 1 0 . . | E 3 . A | . . C 8 | . 2 . . | +---------+---------+---------+---------+ | C B . A | 8 . E 2 | 1 . . 0 | . D 7 5 | | . . 7 1 | . . 3 4 | 5 9 B . | C . 2 A | | . . F . | 1 C D 6 | . . 4 . | . 9 B . | | 2 . . 3 | 7 B A 5 | 8 C D . | . 1 4 . | +---------+---------+---------+---------+ | . 4 . . | 2 1 . C | E . . . | . . . 7 | | . 9 2 . | . A . B | . . F 5 | . 4 . 1 | | 3 . 1 F | . D 4 E | . B . A | . C . . | | 5 A . C | 9 . . . | . 1 2 . | 3 . . . | +---------+---------+---------+---------+ | 7 C . . | . E . . | . . . 2 | . . 9 . | | . E . 9 | D . C 3 | . 0 5 . | . . . . | | . . . . | A 7 1 F | . . . . | 5 . 3 C | | . . . . | 5 2 . . | 3 4 . . | . . E 0 | +---------+---------+---------+---------+ Puzzle : ..C74..9..E31.....52F...A6.19B..9D8..5B..20.....10..E3.A..C8 +.2..CB.A8. E21..0.D75..71..3459B.C.2A..F.1CD6..4..9B.2..37BA58CD..14..4..21.CE... +...7.92..A .B..F5.4.13.1F.D4E.B.A.C..5A.C9....12.3...7C...E.....2..9..E.9D.C3.05. +........A7 1F....5.3C....52..34....E0 Solution: AFC74629BDE31508E352F08DA6719BC49D86C5B1420F73AE10B4E37A95C8 +D2F6CB4A89 E21F306D75D8710F3459B6CE2A05FE1CD62A4789B326937BA58CDE014FB4D0215CE369 +AF8769283A 0BC7F5E4D1371F6D4E0B8A2C595AEC98F7D124306B7C35BE60F8124A9D8EA9D4C3705B +F612420BA7 1F6E9D583CF16D529834ACB7E0

In reply to Sudoku generator by Adam

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-04-24 17:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found