Véase:
~/rubytesting$ cat -n useSudoku.rb 1 #!/usr/bin/env ruby 2 # http://www.websudoku.com/ 3 $LOAD_PATH.unshift 'RPLExamples' 4 require "Sudoku" 5 puts Sudoku.solve Sudoku::Puzzle.new ARGF.readlines
~/rubytesting$ cat -n RPLExamples/sudoku.txt 1 3.4..5.61 2 ....9.8.3 3 .1..8...2 4 4...37.5. 5 .6.9.8.2. 6 .7.54...9 7 1...7..9. 8 7.8.6.... 9 29.4..3.7
~/rubytesting$ ./useSudoku.rb RPLExamples/sudoku.txt 384725961 652194873 917386542 429637158 563918724 871542639 145873296 738269415 296451387
~/rubytesting$ cat -n RPLExamples/Sudoku.rb 1 # http://www.websudoku.com/ 2 module Sudoku 3 4 class Puzzle 5 6 ASCII = ".123456789" 7 BIN = "\000\001\002\003\004\005\006\007\010\011" 8 9 def initialize(lines) 10 if (lines.respond_to? :join) 11 s = lines.join 12 else 13 s = lines.dup 14 end 15 16 s.gsub!(/\s/, "") 17 18 raise Invalid, "Grid is the wrong size" unless s.size == 81 19 20 raise Invalid, "Illegal character #{s[i,1]} in puzzle" if i = s.index(/[^123456789\.]/) 21 22 s.tr!(ASCII, BIN) # Translate ASCII characters into bytes 23 @grid = s.unpack('c*') # Now unpack the bytes into an array of numbers 24 25 raise Invalid, "Initial puzzle has duplicates" if has_duplicates? 26 end 27 28 def to_s 29 (0..8).collect{|r| @grid[r*9,9].pack('c9')}.join("\n").tr(BIN,ASCII) 30 end 31 32 def dup 33 copy = super # Make a shallow copy by calling Object.dup 34 @grid = @grid.dup # Make a new copy of the internal data 35 copy # Return the copied object 36 end 37 38 def [](row, col) 39 @grid[row*9 + col] 40 end 41 42 def []=(row, col, newvalue) 43 raise Invalid, "illegal cell value" unless (0..9).include? newvalue 44 @grid[row*9 + col] = newvalue 45 end 46 47 BoxOfIndex = [ 48 0,0,0,1,1,1,2,2,2, 49 0,0,0,1,1,1,2,2,2, 50 0,0,0,1,1,1,2,2,2, 51 3,3,3,4,4,4,5,5,5, 52 3,3,3,4,4,4,5,5,5, 53 3,3,3,4,4,4,5,5,5, 54 6,6,6,7,7,7,8,8,8, 55 6,6,6,7,7,7,8,8,8, 56 6,6,6,7,7,7,8,8,8 57 ].freeze 58 59 def each_unknown 60 0.upto 8 do |row| # For each row 61 0.upto 8 do |col| # For each column 62 index = row*9+col # Cell index for (row,col) 63 next if @grid[index] != 0 # Move on if we know the cell's value 64 box = BoxOfIndex[index] # Figure out the box for this cell 65 yield row, col, box # Invoke the associated block 66 end 67 end 68 end 69 70 def has_duplicates? 71 0.upto(8) {|row| return true if rowdigits(row).uniq! } 72 0.upto(8) {|col| return true if coldigits(col).uniq! } 73 0.upto(8) {|box| return true if boxdigits(box).uniq! } 74 75 false # If all the tests have passed, then the board has no duplicates 76 end 77 78 AllDigits = [1, 2, 3, 4, 5, 6, 7, 8, 9].freeze 79 80 def possible(row, col, box) 81 AllDigits - (rowdigits(row) + coldigits(col) + boxdigits(box)) 82 end 83 84 85 def rowdigits(row) 86 @grid[row*9,9] - [0] 87 end 88 89 def coldigits(col) 90 result = [] # Start with an empty array 91 col.step(80, 9) {|i| # Loop from col by nines up to 80 92 v = @grid[i] # Get value of cell at that index 93 result << v if (v != 0) # Add it to the array if non-zero 94 } 95 result # Return the array 96 end 97 98 BoxToIndex = [0, 3, 6, 27, 30, 33, 54, 57, 60].freeze 99 100 def boxdigits(b) 101 i = BoxToIndex[b] 102 [ 103 @grid[i], @grid[i+1], @grid[i+2], 104 @grid[i+9], @grid[i+10], @grid[i+11], 105 @grid[i+18], @grid[i+19], @grid[i+20] 106 ] - [0] 107 end 108 end # This is the end of the Puzzle class 109 110 class Invalid < StandardError 111 end 112 113 class Impossible < StandardError 114 end 115 116 def Sudoku.scan(puzzle) 117 unchanged = false # This is our loop variable 118 119 until unchanged 120 unchanged = true # Assume no cells will be changed this time 121 rmin,cmin,pmin = nil # Track cell with minimal possible set 122 min = 10 # More than the maximal number of possibilities 123 124 puzzle.each_unknown do |row, col, box| 125 p = puzzle.possible(row, col, box) 126 127 case p.size 128 when 0 # No possible values means the puzzle is over-constrained 129 raise Impossible 130 when 1 # We've found a unique value, so set it in the grid 131 puzzle[row,col] = p[0] # Set that position on the grid to the value 132 unchanged = false # Note that we've made a change 133 else # For any other number of possibilities 134 if unchanged && p.size < min 135 min = p.size # Current smallest size 136 rmin, cmin, pmin = row, col, p # Note parallel assignment 137 end 138 end 139 end 140 end 141 142 return rmin, cmin, pmin 143 end 144 145 def Sudoku.solve(puzzle) 146 puzzle = puzzle.dup 147 148 r,c,p = scan(puzzle) 149 150 return puzzle if r == nil 151 152 p.each do |guess| # For each value in the set of possible values 153 puzzle[r,c] = guess # Guess the value 154 155 begin 156 return solve(puzzle) # If it returns, we just return the solution 157 rescue Impossible 158 next # If it raises an exception, try the next guess 159 end 160 end 161 162 raise Impossible 163 end 164 end