sudoku_generator.pl 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 30 January 2017
  4. # Edit: 20 December 2021
  5. # https://github.com/trizen
  6. # Recursive brute-force Sudoku generator and solver.
  7. # See also:
  8. # https://en.wikipedia.org/wiki/Sudoku
  9. use 5.020;
  10. use strict;
  11. use List::Util qw(shuffle);
  12. use experimental qw(signatures);
  13. sub check ($i, $j) {
  14. use integer;
  15. my ($id, $im) = ($i / 9, $i % 9);
  16. my ($jd, $jm) = ($j / 9, $j % 9);
  17. $jd == $id && return 1;
  18. $jm == $im && return 1;
  19. $id / 3 == $jd / 3
  20. and $jm / 3 == $im / 3;
  21. }
  22. my @lookup;
  23. foreach my $i (0 .. 80) {
  24. foreach my $j (0 .. 80) {
  25. $lookup[$i][$j] = check($i, $j);
  26. }
  27. }
  28. sub solve_sudoku ($callback, $grid) {
  29. sub {
  30. foreach my $i (0 .. 80) {
  31. if (!$grid->[$i]) {
  32. my %t;
  33. undef @t{@{$grid}[grep { $lookup[$i][$_] } 0 .. 80]};
  34. foreach my $k (shuffle(1 .. 9)) {
  35. if (!exists $t{$k}) {
  36. $grid->[$i] = $k;
  37. __SUB__->();
  38. $grid->[$i] = 0;
  39. }
  40. }
  41. return;
  42. }
  43. }
  44. $callback->(@$grid);
  45. }
  46. ->();
  47. }
  48. sub generate_sudoku ($known, $solution_count = 1) {
  49. my @grid = (0) x 81;
  50. eval {
  51. solve_sudoku(
  52. sub {
  53. my (@solution) = @_;
  54. my %table;
  55. @table{(shuffle(0 .. $#solution))[0 .. $known - 1]} = ();
  56. my @candidate = map { exists($table{$_}) ? $solution[$_] : 0 } 0 .. $#solution;
  57. my $res = eval {
  58. my $count = 0;
  59. solve_sudoku(sub { die "error" if (++$count > $solution_count) }, [@candidate]);
  60. $count;
  61. };
  62. if (defined($res) and $res == $solution_count) {
  63. @grid = @candidate;
  64. die "found";
  65. }
  66. },
  67. \@grid
  68. );
  69. };
  70. return @grid;
  71. }
  72. sub display_grid_as_ascii_table {
  73. my (@grid) = @_;
  74. my $t = Text::ASCIITable->new();
  75. $t->setCols(map { '1 2 3' } 1 .. 3);
  76. $t->setOptions({hide_HeadLine => 1, hide_HeadRow => 1});
  77. my @collect;
  78. foreach my $i (0 .. $#grid) {
  79. push @collect, $grid[$i] ? $grid[$i] : '0';
  80. if (($i + 1) % 9 == 0) {
  81. my @row = splice(@collect);
  82. my @chunks;
  83. while (@row) {
  84. push @chunks, join ' ', splice(@row, 0, 3);
  85. }
  86. $t->addRow(@chunks);
  87. }
  88. if (($i + 1) % 27 == 0) {
  89. $t->addRowLine();
  90. }
  91. }
  92. print $t;
  93. }
  94. sub display_grid {
  95. my (@grid) = @_;
  96. my $has_ascii_table = eval { require Text::ASCIITable; 1 };
  97. if ($has_ascii_table) {
  98. return display_grid_as_ascii_table(@grid);
  99. }
  100. foreach my $i (0 .. $#grid) {
  101. print "$grid[$i] ";
  102. print " " if ($i + 1) % 3 == 0;
  103. print "\n" if ($i + 1) % 9 == 0;
  104. print "\n" if ($i + 1) % 27 == 0;
  105. }
  106. }
  107. my $known = 35; # number of known entries
  108. my $solution_count = 1; # number of solutions the puzzle must have
  109. my @sudoku = generate_sudoku($known, $solution_count);
  110. say "\n:: Random Sudoku with $known known entries:\n";
  111. display_grid(@sudoku);
  112. say "\n:: Solution(s):\n";
  113. solve_sudoku(
  114. sub {
  115. my (@solution) = @_;
  116. display_grid(@solution);
  117. },
  118. \@sudoku
  119. );
  120. __END__
  121. :: Random Sudoku with 35 known entries:
  122. .-----------------------.
  123. | 8 9 0 | 6 4 5 | 2 0 3 |
  124. | 7 4 0 | 8 0 0 | 9 0 0 |
  125. | 0 0 5 | 0 3 0 | 8 1 4 |
  126. +-------+-------+-------+
  127. | 3 0 0 | 0 0 9 | 0 0 1 |
  128. | 0 1 2 | 4 7 0 | 5 0 8 |
  129. | 0 8 0 | 0 0 0 | 4 3 0 |
  130. +-------+-------+-------+
  131. | 1 0 0 | 0 6 0 | 3 0 0 |
  132. | 0 0 0 | 0 0 0 | 0 0 5 |
  133. | 0 0 0 | 0 5 4 | 7 0 0 |
  134. '-------+-------+-------'
  135. :: Solution(s):
  136. .-----------------------.
  137. | 8 9 1 | 6 4 5 | 2 7 3 |
  138. | 7 4 3 | 8 2 1 | 9 5 6 |
  139. | 2 6 5 | 9 3 7 | 8 1 4 |
  140. +-------+-------+-------+
  141. | 3 7 4 | 5 8 9 | 6 2 1 |
  142. | 6 1 2 | 4 7 3 | 5 9 8 |
  143. | 5 8 9 | 2 1 6 | 4 3 7 |
  144. +-------+-------+-------+
  145. | 1 5 8 | 7 6 2 | 3 4 9 |
  146. | 4 2 7 | 3 9 8 | 1 6 5 |
  147. | 9 3 6 | 1 5 4 | 7 8 2 |
  148. '-------+-------+-------'