sudokusolver 1.3 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sudokusolver.rb +36 -22
- metadata +2 -2
data/lib/sudokusolver.rb
CHANGED
@@ -4,19 +4,20 @@
|
|
4
4
|
# Algorithm, overall structure and original python source code by Peter Norvig
|
5
5
|
# See http://norvig.com/sudoku.html
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
# Throughout this program:
|
8
|
+
# r is a row, e.g. 'A'
|
9
|
+
# c is a column, e.g. '3'
|
10
|
+
# s is a square, e.g. 'A3'
|
11
|
+
# d is a digit, e.g. '9'
|
12
|
+
# u is a unit, e.g. ['A1','B1','C1','D1','E1','F1','G1','H1','I1']
|
13
|
+
# g is a grid, e.g. 81 non-blank chars, e.g. starting with '.18...7...
|
14
|
+
# values is a hash of possible values, e.g. {'A1'=>'123489', 'A2'=>'8', ...}
|
15
15
|
|
16
16
|
class SudokuSolver
|
17
|
-
VERSION = "1.
|
17
|
+
VERSION = "1.4"
|
18
18
|
attr_reader :rows, :cols, :squares, :unitlist, :peers, :units
|
19
19
|
|
20
|
+
# Cross-product
|
20
21
|
def cross(a, b)
|
21
22
|
cp = Array.new # cross product
|
22
23
|
a.each do |x|
|
@@ -60,6 +61,8 @@ class SudokuSolver
|
|
60
61
|
|
61
62
|
end
|
62
63
|
|
64
|
+
# A grid is an 81 character string composed of the digits 0-9
|
65
|
+
# A blank is represented as a period.
|
63
66
|
def parse_grid(g)
|
64
67
|
g = g.chomp
|
65
68
|
g = g.split('')
|
@@ -72,6 +75,9 @@ class SudokuSolver
|
|
72
75
|
return values
|
73
76
|
end
|
74
77
|
|
78
|
+
# Assign a value to a square in the Sudoku grid:
|
79
|
+
# Eliminate all other possible digits from the square
|
80
|
+
# by calling the eliminate function (mutually recursive)
|
75
81
|
def assign(values, s, d)
|
76
82
|
values[s].split('').each do |d2|
|
77
83
|
unless d2 == d
|
@@ -81,19 +87,23 @@ class SudokuSolver
|
|
81
87
|
return values
|
82
88
|
end
|
83
89
|
|
90
|
+
# Remove a possibility from a square.
|
91
|
+
# Recursively propagate the constraints: look at the source code for how this is done.
|
84
92
|
def eliminate(values, s, d)
|
85
93
|
return values unless values[s].include?(d) ## Already eliminated.
|
86
94
|
|
87
|
-
values[s] = values[s].sub(d,'')
|
88
|
-
|
95
|
+
values[s] = values[s].sub(d,'') # Remove the digit from the string of possibilities
|
96
|
+
# values[s].sub!(d,'') => why doesn't sub!() work?
|
89
97
|
|
90
|
-
return false if values[s].length == 0
|
98
|
+
return false if values[s].length == 0 # Contradiction: no more values (no more digits can be assigned)
|
91
99
|
|
92
|
-
|
100
|
+
# Remove digit from all peers
|
101
|
+
# If the square has only one remaining possibility, that is the assigned value for the square and
|
102
|
+
# that value must be removed from all that square's peers.
|
93
103
|
peers[s].each { |s2| return false unless eliminate(values, s2, values[s]) } if values[s].length == 1
|
94
104
|
|
95
|
-
|
96
|
-
|
105
|
+
# Assign the digit to the square if, by elimination
|
106
|
+
# this is the only square that has the digit as a possibility
|
97
107
|
units[s].each do |u|
|
98
108
|
dplaces = Array.new
|
99
109
|
u.each { |s2| dplaces << s2 if values[s2].include?(d) }
|
@@ -103,10 +113,11 @@ class SudokuSolver
|
|
103
113
|
return values
|
104
114
|
end
|
105
115
|
|
116
|
+
# Search if constraint satisfaction does not solve the puzzle
|
106
117
|
def search(values)
|
107
118
|
return false if values == false
|
108
119
|
|
109
|
-
solved = true
|
120
|
+
solved = true # assumption
|
110
121
|
squares.each do |s|
|
111
122
|
unless values[s].length == 1
|
112
123
|
solved = false
|
@@ -117,7 +128,7 @@ class SudokuSolver
|
|
117
128
|
|
118
129
|
min = 10
|
119
130
|
start = nil
|
120
|
-
squares.each do |s|
|
131
|
+
squares.each do |s| # Chose the undetermined square s with the fewest possibilities
|
121
132
|
l = values[s].length
|
122
133
|
if l > 1 && l < min
|
123
134
|
min = l
|
@@ -132,6 +143,7 @@ class SudokuSolver
|
|
132
143
|
return false
|
133
144
|
end
|
134
145
|
|
146
|
+
# Print a text Sudoku grid to STDOUT
|
135
147
|
def print_grid(values)
|
136
148
|
return if values == false
|
137
149
|
max = 0
|
@@ -163,6 +175,7 @@ class SudokuSolver
|
|
163
175
|
return values
|
164
176
|
end
|
165
177
|
|
178
|
+
# Transform the solution into an 81 character string
|
166
179
|
def string_solution(values)
|
167
180
|
solution = ""
|
168
181
|
squares.each do |s|
|
@@ -171,6 +184,7 @@ class SudokuSolver
|
|
171
184
|
return solution
|
172
185
|
end
|
173
186
|
|
187
|
+
# Verify the Sudoku solution
|
174
188
|
def check_solution(solution)
|
175
189
|
values = Hash.new
|
176
190
|
for s,d in squares.zip(solution.split(''))
|
@@ -189,9 +203,9 @@ class SudokuSolver
|
|
189
203
|
|
190
204
|
end
|
191
205
|
|
192
|
-
|
206
|
+
# Algorithm by Peter Norvig @ http://www.norvig.com/sudoku.html
|
193
207
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
208
|
+
# More constraints:
|
209
|
+
# http://www.scanraid.com/BasicStrategies.htm
|
210
|
+
# http://www.krazydad.com/blog/2005/09/29/an-index-of-sudoku-strategies/
|
211
|
+
# http://www2.warwick.ac.uk/fac/sci/moac/currentstudents/peter_cock/python/sudoku/
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sudokusolver
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "1.
|
7
|
-
date: 2007-04-
|
6
|
+
version: "1.4"
|
7
|
+
date: 2007-04-22 00:00:00 -04:00
|
8
8
|
summary: Commandline program and library for solving Sudoku puzzles
|
9
9
|
require_paths:
|
10
10
|
- lib
|