sudoku_solver_iterative.pl 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 12 February 2024
  4. # https://github.com/trizen
  5. # Fast algorithm to solve the Sudoku puzzle (iterative solution).
  6. use 5.036;
  7. sub is_valid ($board, $row, $col, $num) {
  8. # Check if the number is not present in the current row and column
  9. foreach my $i (0 .. 8) {
  10. if (($board->[$row][$i] == $num) || ($board->[$i][$col] == $num)) {
  11. return 0;
  12. }
  13. }
  14. # Check if the number is not present in the current 3x3 subgrid
  15. my ($start_row, $start_col) = (3 * int($row / 3), 3 * int($col / 3));
  16. foreach my $i (0 .. 2) {
  17. foreach my $j (0 .. 2) {
  18. if ($board->[$start_row + $i][$start_col + $j] == $num) {
  19. return 0;
  20. }
  21. }
  22. }
  23. return 1;
  24. }
  25. sub find_empty_locations ($board) {
  26. my @locations;
  27. # Find all empty positions (cells with 0)
  28. foreach my $i (0 .. 8) {
  29. foreach my $j (0 .. 8) {
  30. if ($board->[$i][$j] == 0) {
  31. push @locations, [$i, $j];
  32. }
  33. }
  34. }
  35. return @locations;
  36. }
  37. sub find_empty_location ($board) {
  38. # Find an empty position (cell with 0)
  39. foreach my $i (0 .. 8) {
  40. foreach my $j (0 .. 8) {
  41. if ($board->[$i][$j] == 0) {
  42. return ($i, $j);
  43. }
  44. }
  45. }
  46. return (undef, undef); # If the board is filled
  47. }
  48. sub solve_sudoku_fallback ($board) { # fallback method
  49. my ($row, $col) = find_empty_location($board);
  50. if (!defined($row) && !defined($col)) {
  51. return 1; # Puzzle is solved
  52. }
  53. foreach my $num (1 .. 9) {
  54. if (is_valid($board, $row, $col, $num)) {
  55. # Try placing the number
  56. $board->[$row][$col] = $num;
  57. # Recursively try to solve the rest of the puzzle
  58. if (__SUB__->($board)) {
  59. return 1;
  60. }
  61. # If placing the current number doesn't lead to a solution, backtrack
  62. $board->[$row][$col] = 0;
  63. }
  64. }
  65. return 0; # No solution found
  66. }
  67. sub solve_sudoku ($board) {
  68. while (1) {
  69. (my @empty_locations = find_empty_locations($board)) || last;
  70. my $found = 0;
  71. # Solve easy cases
  72. foreach my $ij (@empty_locations) {
  73. my ($i, $j) = @$ij;
  74. my ($count, $value) = (0, 0);
  75. foreach my $n (1 .. 9) {
  76. is_valid($board, $i, $j, $n) || next;
  77. last if (++$count > 1);
  78. $value = $n;
  79. }
  80. if ($count == 1) {
  81. $board->[$i][$j] = $value;
  82. $found ||= 1;
  83. }
  84. }
  85. next if $found;
  86. # Solve more complex cases
  87. my @stats;
  88. foreach my $ij (@empty_locations) {
  89. my ($i, $j) = @$ij;
  90. $stats[$i][$j] = [grep { is_valid($board, $i, $j, $_) } 1 .. 9];
  91. }
  92. my (@rows, @cols, @subgrid);
  93. foreach my $ij (@empty_locations) {
  94. my ($i, $j) = @$ij;
  95. foreach my $v (@{$stats[$i][$j]}) {
  96. ++$cols[$j][$v];
  97. ++$rows[$i][$v];
  98. ++$subgrid[3 * int($i / 3)][3 * int($j / 3)][$v];
  99. }
  100. }
  101. foreach my $ij (@empty_locations) {
  102. my ($i, $j) = @$ij;
  103. foreach my $v (@{$stats[$i][$j]}) {
  104. if ( $cols[$j][$v] == 1
  105. or $rows[$i][$v] == 1
  106. or $subgrid[3 * int($i / 3)][3 * int($j / 3)][$v] == 1) {
  107. $board->[$i][$j] = $v;
  108. $found ||= 1;
  109. }
  110. }
  111. }
  112. next if $found;
  113. # Give up and try brute-force
  114. solve_sudoku_fallback($board);
  115. return $board;
  116. }
  117. return $board;
  118. }
  119. #<<<
  120. # Example usage:
  121. # Define the Sudoku puzzle as a 9x9 list with 0 representing empty cells
  122. my $sudoku_board = [
  123. [2, 0, 0, 0, 7, 0, 0, 0, 3],
  124. [1, 0, 0, 0, 0, 0, 0, 8, 0],
  125. [0, 0, 4, 2, 0, 9, 0, 0, 5],
  126. [9, 4, 0, 0, 0, 0, 6, 0, 8],
  127. [0, 0, 0, 8, 0, 0, 0, 9, 0],
  128. [0, 0, 0, 0, 0, 0, 0, 7, 0],
  129. [7, 2, 1, 9, 0, 8, 0, 6, 0],
  130. [0, 3, 0, 0, 2, 7, 1, 0, 0],
  131. [4, 0, 0, 0, 0, 3, 0, 0, 0]
  132. ];
  133. $sudoku_board = [
  134. [0, 0, 0, 8, 0, 1, 0, 0, 0],
  135. [0, 0, 0, 0, 0, 0, 0, 4, 3],
  136. [5, 0, 0, 0, 0, 0, 0, 0, 0],
  137. [0, 0, 0, 0, 7, 0, 8, 0, 0],
  138. [0, 0, 0, 0, 0, 0, 1, 0, 0],
  139. [0, 2, 0, 0, 3, 0, 0, 0, 0],
  140. [6, 0, 0, 0, 0, 0, 0, 7, 5],
  141. [0, 0, 3, 4, 0, 0, 0, 0, 0],
  142. [0, 0, 0, 2, 0, 0, 6, 0, 0]
  143. ] if 1;
  144. $sudoku_board = [
  145. [8, 0, 0, 0, 0, 0, 0, 0, 0],
  146. [0, 0, 3, 6, 0, 0, 0, 0, 0],
  147. [0, 7, 0, 0, 9, 0, 2, 0, 0],
  148. [0, 5, 0, 0, 0, 7, 0, 0, 0],
  149. [0, 0, 0, 0, 4, 5, 7, 0, 0],
  150. [0, 0, 0, 1, 0, 0, 0, 3, 0],
  151. [0, 0, 1, 0, 0, 0, 0, 6, 8],
  152. [0, 0, 8, 5, 0, 0, 0, 1, 0],
  153. [0, 9, 0, 0, 0, 0, 4, 0, 0]
  154. ] if 0;
  155. #>>>
  156. sub display_grid ($grid) {
  157. foreach my $i (0 .. $#$grid) {
  158. print "$grid->[$i] ";
  159. print " " if ($i + 1) % 3 == 0;
  160. print "\n" if ($i + 1) % 9 == 0;
  161. print "\n" if ($i + 1) % 27 == 0;
  162. }
  163. }
  164. my $solution = solve_sudoku($sudoku_board);
  165. if ($solution) {
  166. display_grid([map { @$_ } @$solution]);
  167. }
  168. else {
  169. warn "No unique solution exists!\n";
  170. }