sudokusolver_ng 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/COPYING +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/lib/sudokusolver_ng.rb +212 -0
- data/spec/boards/easy50.txt +50 -0
- data/spec/boards/hardest.txt +11 -0
- data/spec/boards/top95.txt +95 -0
- data/spec/spec_helper.rb +85 -0
- data/spec/sudoku_spec.rb +51 -0
- data/sudokusolver_ng.gemspec +23 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bab3bb7004fd538bbae6ff152f7e63342bcc3de9
|
4
|
+
data.tar.gz: adfdadd1bf26701147c1b2d14b8d3629a63a6ee7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2bb58f45c66d4808dcafb6ad17943e72ead7760125af3c69c5d6970689891b4dbb66a5f3f87cbe8334707ced4d390c1ae5351caa2d6ccd44618a0b3e34ca759b
|
7
|
+
data.tar.gz: 70d465640329480d528d31254908b7083db94fca96876330961b8bcf7a5b0223d9a981c7ee7b7f01e5940433c510f12fa91626f24658bb8275de80ffff6a25f3
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/COPYING
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
See the file called LICENSE
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2007
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
sudokusolver_ng
|
2
|
+
============
|
3
|
+
|
4
|
+
Commandline program and library for solving Sudoku puzzles
|
5
|
+
|
6
|
+
[![Build Status](https://travis-ci.org/kaspergrubbe/sudokusolver_ng.svg?branch=master)](https://travis-ci.org/kaspergrubbe/sudokusolver_ng)
|
7
|
+
|
8
|
+
## History
|
9
|
+
|
10
|
+
This software was translated from the python source code obtained from Peter Norvig's website:
|
11
|
+
|
12
|
+
http://www.norvig.com/sudoku.html
|
13
|
+
http://www.norvig.com/sudo.py
|
14
|
+
|
15
|
+
Thank you to Peter Norvig for the original python source code, algorithms and exceptionally clear explanations.
|
16
|
+
|
17
|
+
All this work was done by Martin-Louis Bright, and then gemified and converted to Ruby 1.9 by Kasper Grubbe. The original project is called "sudokusolver" and is available from Rubygems here: https://rubygems.org/gems/sudokusolver, this is a fork moved to Github.
|
18
|
+
|
19
|
+
## Install
|
20
|
+
|
21
|
+
Add this to your `Gemfile`:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'sudokusolver_ng'
|
25
|
+
```
|
26
|
+
|
27
|
+
And remember to `bundle`
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'sudokusolver_ng'
|
33
|
+
|
34
|
+
# The puzzle representation is simply the 9 rows of the Sudoku grid stringed together
|
35
|
+
# from top to bottom (periods representing blank squares)
|
36
|
+
|
37
|
+
puzzle = "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......"
|
38
|
+
s = SudokuSolverNg.new
|
39
|
+
s.print_grid(s.search(s.parse_grid(puzzle)))
|
40
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Translated into ruby from python by Martin-Louis Bright
|
4
|
+
# Algorithm, overall structure and original python source code by Peter Norvig
|
5
|
+
# See http://norvig.com/sudoku.html
|
6
|
+
|
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
|
+
|
16
|
+
# Algorithm by Peter Norvig @ http://www.norvig.com/sudoku.html
|
17
|
+
|
18
|
+
# More constraints:
|
19
|
+
# http://www.scanraid.com/BasicStrategies.htm
|
20
|
+
# http://www.krazydad.com/blog/2005/09/29/an-index-of-sudoku-strategies/
|
21
|
+
# http://www2.warwick.ac.uk/fac/sci/moac/currentstudents/peter_cock/python/sudoku/
|
22
|
+
|
23
|
+
class SudokuSolverNg
|
24
|
+
VERSION = "2.0.0"
|
25
|
+
|
26
|
+
attr_reader :rows, :cols, :squares, :unitlist, :peers, :units
|
27
|
+
|
28
|
+
# Cross-product
|
29
|
+
def cross(a, b)
|
30
|
+
cp = Array.new # cross product
|
31
|
+
a.each do |x|
|
32
|
+
b.each do |y|
|
33
|
+
cp << x+y
|
34
|
+
end
|
35
|
+
end
|
36
|
+
return cp
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize()
|
40
|
+
@rows = ('A'..'I').to_a
|
41
|
+
@cols = ('1'..'9').to_a
|
42
|
+
@squares = cross(@rows, @cols)
|
43
|
+
@unitlist = Array.new
|
44
|
+
cols.each { |c| @unitlist.push(cross(rows, [c])) }
|
45
|
+
rows.each { |r| @unitlist.push(cross([r], cols)) }
|
46
|
+
for rb in ['ABC','DEF','GHI'] do
|
47
|
+
for cb in ['123','456','789'] do
|
48
|
+
@unitlist << cross(rb.split(''),cb.split(''))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@units = Hash.new
|
53
|
+
squares.each do |s|
|
54
|
+
@units[s] = Array.new
|
55
|
+
unitlist.each do |u|
|
56
|
+
u.each do |x|
|
57
|
+
@units[s].push(u) if s == x
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@peers = Hash.new
|
63
|
+
squares.each do |s|
|
64
|
+
@peers[s] = Array.new
|
65
|
+
units[s].each do |u|
|
66
|
+
u.each { |s2| @peers[s] << s2 if s2 != s }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# A grid is an 81 character string composed of the digits 0-9
|
73
|
+
# A blank is represented as a period.
|
74
|
+
def parse_grid(g)
|
75
|
+
g = g.chomp
|
76
|
+
g = g.split('')
|
77
|
+
values = Hash.new
|
78
|
+
# Initially any square can be anything.
|
79
|
+
squares.each { |s| values[s] = "123456789" }
|
80
|
+
for s,d in squares.zip(g)
|
81
|
+
return false unless assign(values, s, d) if d =~ /\d/
|
82
|
+
end
|
83
|
+
return values
|
84
|
+
end
|
85
|
+
|
86
|
+
# Assign a value to a square in the Sudoku grid:
|
87
|
+
# Eliminate all other possible digits from the square
|
88
|
+
# by calling the eliminate function (mutually recursive)
|
89
|
+
def assign(values, s, d)
|
90
|
+
values[s].split('').each do |d2|
|
91
|
+
unless d2 == d
|
92
|
+
return false if eliminate(values, s, d2) == false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return values
|
96
|
+
end
|
97
|
+
|
98
|
+
# Remove a possibility from a square.
|
99
|
+
# Recursively propagate the constraints: look at the source code for how this is done.
|
100
|
+
def eliminate(values, s, d)
|
101
|
+
return values unless values[s].include?(d) ## Already eliminated.
|
102
|
+
|
103
|
+
values[s] = values[s].sub(d,'') # Remove the digit from the string of possibilities
|
104
|
+
# values[s].sub!(d,'') => why doesn't sub!() work?
|
105
|
+
|
106
|
+
return false if values[s].length == 0 # Contradiction: no more values (no more digits can be assigned)
|
107
|
+
|
108
|
+
# Remove digit from all peers
|
109
|
+
# If the square has only one remaining possibility, that is the assigned value for the square and
|
110
|
+
# that value must be removed from all that square's peers.
|
111
|
+
peers[s].each { |s2| return false unless eliminate(values, s2, values[s]) } if values[s].length == 1
|
112
|
+
|
113
|
+
# Assign the digit to the square if, by elimination
|
114
|
+
# this is the only square that has the digit as a possibility
|
115
|
+
units[s].each do |u|
|
116
|
+
dplaces = Array.new
|
117
|
+
u.each { |s2| dplaces << s2 if values[s2].include?(d) }
|
118
|
+
return false if dplaces.length == 0 # bad
|
119
|
+
return false if assign(values, dplaces[0], d) == false if dplaces.length == 1
|
120
|
+
end
|
121
|
+
return values
|
122
|
+
end
|
123
|
+
|
124
|
+
# Search if constraint satisfaction does not solve the puzzle
|
125
|
+
def search(values)
|
126
|
+
return false if values == false
|
127
|
+
|
128
|
+
solved = true # assumption
|
129
|
+
squares.each do |s|
|
130
|
+
unless values[s].length == 1
|
131
|
+
solved = false
|
132
|
+
break
|
133
|
+
end
|
134
|
+
end
|
135
|
+
return values if solved == true ## Solved!
|
136
|
+
|
137
|
+
min = 10
|
138
|
+
start = nil
|
139
|
+
squares.each do |s| # Chose the undetermined square s with the fewest possibilities
|
140
|
+
l = values[s].length
|
141
|
+
if l > 1 && l < min
|
142
|
+
min = l
|
143
|
+
start = s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
values[start].split('').each do |d|
|
148
|
+
solution = search(assign(values.clone,start,d))
|
149
|
+
return solution unless solution == false
|
150
|
+
end
|
151
|
+
return false
|
152
|
+
end
|
153
|
+
|
154
|
+
# Print a text Sudoku grid to STDOUT
|
155
|
+
def print_grid(values)
|
156
|
+
return if values == false
|
157
|
+
max = 0
|
158
|
+
squares.each { |s| max = values[s].length if values[s].length > max }
|
159
|
+
width = 1 + max
|
160
|
+
a = Array.new
|
161
|
+
3.times do |c|
|
162
|
+
tmp = ""
|
163
|
+
(3*width).times do
|
164
|
+
tmp = tmp + '-'
|
165
|
+
end
|
166
|
+
tmp += "-" if c == 1
|
167
|
+
a.push(tmp)
|
168
|
+
end
|
169
|
+
line = "\n" + a.join('+')
|
170
|
+
|
171
|
+
tmp = ""
|
172
|
+
for r in rows
|
173
|
+
for c in cols
|
174
|
+
tmp = tmp + values[r+c].center(width)
|
175
|
+
if c == '3' or c == '6'
|
176
|
+
tmp = tmp + '| '
|
177
|
+
end
|
178
|
+
end
|
179
|
+
tmp = tmp + line if r == 'C' or r == 'F'
|
180
|
+
tmp = tmp + "\n"
|
181
|
+
end
|
182
|
+
puts tmp + "\n"
|
183
|
+
return values
|
184
|
+
end
|
185
|
+
|
186
|
+
# Transform the solution into an 81 character string
|
187
|
+
def string_solution(values)
|
188
|
+
solution = ""
|
189
|
+
squares.each do |s|
|
190
|
+
solution += values[s]
|
191
|
+
end
|
192
|
+
return solution
|
193
|
+
end
|
194
|
+
|
195
|
+
# Verify the Sudoku solution
|
196
|
+
def check_solution(solution)
|
197
|
+
values = Hash.new
|
198
|
+
for s,d in squares.zip(solution.split(''))
|
199
|
+
values[s] = d
|
200
|
+
end
|
201
|
+
|
202
|
+
unitlist.each do |u|
|
203
|
+
tmp = Hash.new
|
204
|
+
u.each do |s|
|
205
|
+
tmp[values[s]] = true
|
206
|
+
end
|
207
|
+
return false unless tmp.keys.length == 9
|
208
|
+
end
|
209
|
+
return true
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..
|
2
|
+
2...8.3...6..7..84.3.5..2.9...1.54.8.........4.27.6...3.1..7.4.72..4..6...4.1...3
|
3
|
+
......9.7...42.18....7.5.261..9.4....5.....4....5.7..992.1.8....34.59...5.7......
|
4
|
+
.3..5..4...8.1.5..46.....12.7.5.2.8....6.3....4.1.9.3.25.....98..1.2.6...8..6..2.
|
5
|
+
.2.81.74.7....31...9...28.5..9.4..874..2.8..316..3.2..3.27...6...56....8.76.51.9.
|
6
|
+
1..92....524.1...........7..5...81.2.........4.27...9..6...........3.945....71..6
|
7
|
+
.43.8.25.6.............1.949....4.7....6.8....1.2....382.5.............5.34.9.71.
|
8
|
+
48...69.2..2..8..19..37..6.84..1.2....37.41....1.6..49.2..85..77..9..6..6.92...18
|
9
|
+
...9....2.5.1234...3....16.9.8.......7.....9.......2.5.91....5...7439.2.4....7...
|
10
|
+
..19....39..7..16..3...5..7.5......9..43.26..2......7.6..1...3..42..7..65....68..
|
11
|
+
...1254....84.....42.8......3.....95.6.9.2.1.51.....6......3.49.....72....1298...
|
12
|
+
.6234.75.1....56..57.....4.....948..4.......6..583.....3.....91..64....7.59.8326.
|
13
|
+
3..........5..9...2..5.4....2....7..16.....587.431.6.....89.1......67.8......5437
|
14
|
+
63..........5....8..5674.......2......34.1.2.......345.....7..4.8.3..9.29471...8.
|
15
|
+
....2..4...8.35.......7.6.2.31.4697.2...........5.12.3.49...73........1.8....4...
|
16
|
+
361.259...8.96..1.4......57..8...471...6.3...259...8..74......5.2..18.6...547.329
|
17
|
+
.5.8.7.2.6...1..9.7.254...6.7..2.3.15.4...9.81.3.8..7.9...762.5.6..9...3.8.1.3.4.
|
18
|
+
.8...5........3457....7.8.9.6.4..9.3..7.1.5..4.8..7.2.9.1.2....8423........1...8.
|
19
|
+
..35.29......4....1.6...3.59..251..8.7.4.8.3.8..763..13.8...1.4....2......51.48..
|
20
|
+
...........98.51...519.742.29.4.1.65.........14.5.8.93.267.958...51.36...........
|
21
|
+
.2..3..9....9.7...9..2.8..5..48.65..6.7...2.8..31.29..8..6.5..7...3.9....3..2..5.
|
22
|
+
..5.....6.7...9.2....5..1.78.415.......8.3.......928.59.7..6....3.4...1.2.....6..
|
23
|
+
.4.....5...19436....9...3..6...5...21.3...5.68...2...7..5...2....24367...3.....4.
|
24
|
+
..4..........3...239.7...8.4....9..12.98.13.76..2....8.1...8.539...4..........8..
|
25
|
+
36..2..89...361............8.3...6.24..6.3..76.7...1.8............418...97..3..14
|
26
|
+
5..4...6...9...8..64..2.........1..82.8...5.17..5.........9..84..3...6...6...3..2
|
27
|
+
..72564..4.......5.1..3..6....5.8.....8.6.2.....1.7....3..7..9.2.......4..63127..
|
28
|
+
..........79.5.18.8.......7..73.68..45.7.8.96..35.27..7.......5.16.3.42..........
|
29
|
+
.3.....8...9...5....75.92..7..1.5..8.2..9..3.9..4.2..1..42.71....2...8...7.....9.
|
30
|
+
2..17.6.3.5....1.......6.79....4.7.....8.1.....9.5....31.4.......5....6.9.6.37..2
|
31
|
+
.......8.8..7.1.4..4..2..3.374...9......3......5...321.1..6..5..5.8.2..6.8.......
|
32
|
+
.......85...21...996..8.1..5..8...16.........89...6..7..9.7..523...54...48.......
|
33
|
+
6.8.7.5.2.5.6.8.7...2...3..5...9...6.4.3.2.5.8...5...3..5...2...1.7.4.9.4.9.6.7.1
|
34
|
+
.5..1..4.1.7...6.2...9.5...2.8.3.5.1.4..7..2.9.1.8.4.6...4.1...3.4...7.9.2..6..1.
|
35
|
+
.53...79...97534..1.......2.9..8..1....9.7....8..3..7.5.......3..76412...61...94.
|
36
|
+
..6.8.3...49.7.25....4.5...6..317..4..7...8..1..826..9...7.2....75.4.19...3.9.6..
|
37
|
+
..5.8.7..7..2.4..532.....84.6.1.5.4...8...5...7.8.3.1.45.....916..5.8..7..3.1.6..
|
38
|
+
...9..8..128..64...7.8...6.8..43...75.......96...79..8.9...4.1...36..284..1..7...
|
39
|
+
....8....27.....54.95...81...98.64...2.4.3.6...69.51...17...62.46.....38....9....
|
40
|
+
...6.2...4...5...1.85.1.62..382.671...........194.735..26.4.53.9...2...7...8.9...
|
41
|
+
...9....2.5.1234...3....16.9.8.......7.....9.......2.5.91....5...7439.2.4....7...
|
42
|
+
38..........4..785..9.2.3...6..9....8..3.2..9....4..7...1.7.5..495..6..........92
|
43
|
+
...158.....2.6.8...3.....4..27.3.51...........46.8.79..5.....8...4.7.1.....325...
|
44
|
+
.1.5..2..9....1.....2..8.3.5...3...7..8...5..6...8...4.4.1..7.....7....6..3..4.5.
|
45
|
+
.8.....4....469...4.......7..59.46...7.6.8.3...85.21..9.......5...781....6.....1.
|
46
|
+
9.42....7.1..........7.65.....8...9..2.9.4.6..4...2.....16.7..........3.3....57.2
|
47
|
+
...7..8....6....31.4...2....24.7.....1..3..8.....6.29....8...7.86....5....2..6...
|
48
|
+
..1..7.9.59..8...1.3.....8......58...5..6..2...41......8.....3.1...2..79.2.7..4..
|
49
|
+
.....3.17.15..9..8.6.......1....7.....9...2.....5....4.......2.5..6..34.34.2.....
|
50
|
+
3..2........1.7...7.6.3.5...7...9.8.9...2...4.1.8...5...9.4.3.1...7.2........8..6
|
@@ -0,0 +1,11 @@
|
|
1
|
+
85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.
|
2
|
+
..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..
|
3
|
+
12..4......5.69.1...9...5.........7.7...52.9..3......2.9.6...5.4..9..8.1..3...9.4
|
4
|
+
...57..3.1......2.7...234......8...4..7..4...49....6.5.42...3.....7..9....18.....
|
5
|
+
7..1523........92....3.....1....47.8.......6............9...5.6.4.9.7...8....6.1.
|
6
|
+
1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..
|
7
|
+
1...34.8....8..5....4.6..21.18......3..1.2..6......81.52..7.9....6..9....9.64...2
|
8
|
+
...92......68.3...19..7...623..4.1....1...7....8.3..297...8..91...5.72......64...
|
9
|
+
.6.5.4.3.1...9...8.........9...5...6.4.6.2.7.7...4...5.........4...8...1.5.2.3.4.
|
10
|
+
7.....4...2..7..8...3..8.799..5..3...6..2..9...1.97..6...3..9...3..4..6...9..1.35
|
11
|
+
....7..2.8.......6.1.2.5...9.54....8.........3....85.1...3.2.8.4.......9.7..6....
|
@@ -0,0 +1,95 @@
|
|
1
|
+
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
|
2
|
+
52...6.........7.13...........4..8..6......5...........418.........3..2...87.....
|
3
|
+
6.....8.3.4.7.................5.4.7.3..2.....1.6.......2.....5.....8.6......1....
|
4
|
+
48.3............71.2.......7.5....6....2..8.............1.76...3.....4......5....
|
5
|
+
....14....3....2...7..........9...3.6.1.............8.2.....1.4....5.6.....7.8...
|
6
|
+
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
|
7
|
+
6.2.5.........3.4..........43...8....1....2........7..5..27...........81...6.....
|
8
|
+
.524.........7.1..............8.2...3.....6...9.5.....1.6.3...........897........
|
9
|
+
6.2.5.........4.3..........43...8....1....2........7..5..27...........81...6.....
|
10
|
+
.923.........8.1...........1.7.4...........658.........6.5.2...4.....7.....9.....
|
11
|
+
6..3.2....5.....1..........7.26............543.........8.15........4.2........7..
|
12
|
+
.6.5.1.9.1...9..539....7....4.8...7.......5.8.817.5.3.....5.2............76..8...
|
13
|
+
..5...987.4..5...1..7......2...48....9.1.....6..2.....3..6..2.......9.7.......5..
|
14
|
+
3.6.7...........518.........1.4.5...7.....6.....2......2.....4.....8.3.....5.....
|
15
|
+
1.....3.8.7.4..............2.3.1...........958.........5.6...7.....8.2...4.......
|
16
|
+
6..3.2....4.....1..........7.26............543.........8.15........4.2........7..
|
17
|
+
....3..9....2....1.5.9..............1.2.8.4.6.8.5...2..75......4.1..6..3.....4.6.
|
18
|
+
45.....3....8.1....9...........5..9.2..7.....8.........1..4..........7.2...6..8..
|
19
|
+
.237....68...6.59.9.....7......4.97.3.7.96..2.........5..47.........2....8.......
|
20
|
+
..84...3....3.....9....157479...8........7..514.....2...9.6...2.5....4......9..56
|
21
|
+
.98.1....2......6.............3.2.5..84.........6.........4.8.93..5...........1..
|
22
|
+
..247..58..............1.4.....2...9528.9.4....9...1.........3.3....75..685..2...
|
23
|
+
4.....8.5.3..........7......2.....6.....5.4......1.......6.3.7.5..2.....1.9......
|
24
|
+
.2.3......63.....58.......15....9.3....7........1....8.879..26......6.7...6..7..4
|
25
|
+
1.....7.9.4...72..8.........7..1..6.3.......5.6..4..2.........8..53...7.7.2....46
|
26
|
+
4.....3.....8.2......7........1...8734.......6........5...6........1.4...82......
|
27
|
+
.......71.2.8........4.3...7...6..5....2..3..9........6...7.....8....4......5....
|
28
|
+
6..3.2....4.....8..........7.26............543.........8.15........8.2........7..
|
29
|
+
.47.8...1............6..7..6....357......5....1..6....28..4.....9.1...4.....2.69.
|
30
|
+
......8.17..2........5.6......7...5..1....3...8.......5......2..4..8....6...3....
|
31
|
+
38.6.......9.......2..3.51......5....3..1..6....4......17.5..8.......9.......7.32
|
32
|
+
...5...........5.697.....2...48.2...25.1...3..8..3.........4.7..13.5..9..2...31..
|
33
|
+
.2.......3.5.62..9.68...3...5..........64.8.2..47..9....3.....1.....6...17.43....
|
34
|
+
.8..4....3......1........2...5...4.69..1..8..2...........3.9....6....5.....2.....
|
35
|
+
..8.9.1...6.5...2......6....3.1.7.5.........9..4...3...5....2...7...3.8.2..7....4
|
36
|
+
4.....5.8.3..........7......2.....6.....5.8......1.......6.3.7.5..2.....1.8......
|
37
|
+
1.....3.8.6.4..............2.3.1...........958.........5.6...7.....8.2...4.......
|
38
|
+
1....6.8..64..........4...7....9.6...7.4..5..5...7.1...5....32.3....8...4........
|
39
|
+
249.6...3.3....2..8.......5.....6......2......1..4.82..9.5..7....4.....1.7...3...
|
40
|
+
...8....9.873...4.6..7.......85..97...........43..75.......3....3...145.4....2..1
|
41
|
+
...5.1....9....8...6.......4.1..........7..9........3.8.....1.5...2..4.....36....
|
42
|
+
......8.16..2........7.5......6...2..1....3...8.......2......7..3..8....5...4....
|
43
|
+
.476...5.8.3.....2.....9......8.5..6...1.....6.24......78...51...6....4..9...4..7
|
44
|
+
.....7.95.....1...86..2.....2..73..85......6...3..49..3.5...41724................
|
45
|
+
.4.5.....8...9..3..76.2.....146..........9..7.....36....1..4.5..6......3..71..2..
|
46
|
+
.834.........7..5...........4.1.8..........27...3.....2.6.5....5.....8........1..
|
47
|
+
..9.....3.....9...7.....5.6..65..4.....3......28......3..75.6..6...........12.3.8
|
48
|
+
.26.39......6....19.....7.......4..9.5....2....85.....3..2..9..4....762.........4
|
49
|
+
2.3.8....8..7...........1...6.5.7...4......3....1............82.5....6...1.......
|
50
|
+
6..3.2....1.....5..........7.26............843.........8.15........8.2........7..
|
51
|
+
1.....9...64..1.7..7..4.......3.....3.89..5....7....2.....6.7.9.....4.1....129.3.
|
52
|
+
.........9......84.623...5....6...453...1...6...9...7....1.....4.5..2....3.8....9
|
53
|
+
.2....5938..5..46.94..6...8..2.3.....6..8.73.7..2.........4.38..7....6..........5
|
54
|
+
9.4..5...25.6..1..31......8.7...9...4..26......147....7.......2...3..8.6.4.....9.
|
55
|
+
...52.....9...3..4......7...1.....4..8..453..6...1...87.2........8....32.4..8..1.
|
56
|
+
53..2.9...24.3..5...9..........1.827...7.........981.............64....91.2.5.43.
|
57
|
+
1....786...7..8.1.8..2....9........24...1......9..5...6.8..........5.9.......93.4
|
58
|
+
....5...11......7..6.....8......4.....9.1.3.....596.2..8..62..7..7......3.5.7.2..
|
59
|
+
.47.2....8....1....3....9.2.....5...6..81..5.....4.....7....3.4...9...1.4..27.8..
|
60
|
+
......94.....9...53....5.7..8.4..1..463...........7.8.8..7.....7......28.5.26....
|
61
|
+
.2......6....41.....78....1......7....37.....6..412....1..74..5..8.5..7......39..
|
62
|
+
1.....3.8.6.4..............2.3.1...........758.........7.5...6.....8.2...4.......
|
63
|
+
2....1.9..1..3.7..9..8...2.......85..6.4.........7...3.2.3...6....5.....1.9...2.5
|
64
|
+
..7..8.....6.2.3...3......9.1..5..6.....1.....7.9....2........4.83..4...26....51.
|
65
|
+
...36....85.......9.4..8........68.........17..9..45...1.5...6.4....9..2.....3...
|
66
|
+
34.6.......7.......2..8.57......5....7..1..2....4......36.2..1.......9.......7.82
|
67
|
+
......4.18..2........6.7......8...6..4....3...1.......6......2..5..1....7...3....
|
68
|
+
.4..5..67...1...4....2.....1..8..3........2...6...........4..5.3.....8..2........
|
69
|
+
.......4...2..4..1.7..5..9...3..7....4..6....6..1..8...2....1..85.9...6.....8...3
|
70
|
+
8..7....4.5....6............3.97...8....43..5....2.9....6......2...6...7.71..83.2
|
71
|
+
.8...4.5....7..3............1..85...6.....2......4....3.26............417........
|
72
|
+
....7..8...6...5...2...3.61.1...7..2..8..534.2..9.......2......58...6.3.4...1....
|
73
|
+
......8.16..2........7.5......6...2..1....3...8.......2......7..4..8....5...3....
|
74
|
+
.2..........6....3.74.8.........3..2.8..4..1.6..5.........1.78.5....9..........4.
|
75
|
+
.52..68.......7.2.......6....48..9..2..41......1.....8..61..38.....9...63..6..1.9
|
76
|
+
....1.78.5....9..........4..2..........6....3.74.8.........3..2.8..4..1.6..5.....
|
77
|
+
1.......3.6.3..7...7...5..121.7...9...7........8.1..2....8.64....9.2..6....4.....
|
78
|
+
4...7.1....19.46.5.....1......7....2..2.3....847..6....14...8.6.2....3..6...9....
|
79
|
+
......8.17..2........5.6......7...5..1....3...8.......5......2..3..8....6...4....
|
80
|
+
963......1....8......2.5....4.8......1....7......3..257......3...9.2.4.7......9..
|
81
|
+
15.3......7..4.2....4.72.....8.........9..1.8.1..8.79......38...........6....7423
|
82
|
+
..........5724...98....947...9..3...5..9..12...3.1.9...6....25....56.....7......6
|
83
|
+
....75....1..2.....4...3...5.....3.2...8...1.......6.....1..48.2........7........
|
84
|
+
6.....7.3.4.8.................5.4.8.7..2.....1.3.......2.....5.....7.9......1....
|
85
|
+
....6...4..6.3....1..4..5.77.....8.5...8.....6.8....9...2.9....4....32....97..1..
|
86
|
+
.32.....58..3.....9.428...1...4...39...6...5.....1.....2...67.8.....4....95....6.
|
87
|
+
...5.3.......6.7..5.8....1636..2.......4.1.......3...567....2.8..4.7.......2..5..
|
88
|
+
.5.3.7.4.1.........3.......5.8.3.61....8..5.9.6..1........4...6...6927....2...9..
|
89
|
+
..5..8..18......9.......78....4.....64....9......53..2.6.........138..5....9.714.
|
90
|
+
..........72.6.1....51...82.8...13..4.........37.9..1.....238..5.4..9.........79.
|
91
|
+
...658.....4......12............96.7...3..5....2.8...3..19..8..3.6.....4....473..
|
92
|
+
.2.3.......6..8.9.83.5........2...8.7.9..5........6..4.......1...1...4.22..7..8.9
|
93
|
+
.5..9....1.....6.....3.8.....8.4...9514.......3....2..........4.8...6..77..15..6.
|
94
|
+
.....2.......7...17..3...9.8..7......2.89.6...13..6....9..5.824.....891..........
|
95
|
+
3...8.......7....51..............36...2..4....7...........6.13..452...........8..
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
4
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
5
|
+
#
|
6
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
7
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
8
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
9
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
10
|
+
# a separate helper file that requires the additional dependencies and performs
|
11
|
+
# the additional setup, and require it from the spec files that actually need it.
|
12
|
+
#
|
13
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
14
|
+
# users commonly want.
|
15
|
+
#
|
16
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# rspec-expectations config goes here. You can use an alternate
|
19
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
20
|
+
# assertions if you prefer.
|
21
|
+
config.expect_with :rspec do |expectations|
|
22
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
23
|
+
# and `failure_message` of custom matchers include text for helper methods
|
24
|
+
# defined using `chain`, e.g.:
|
25
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
26
|
+
# # => "be bigger than 2 and smaller than 4"
|
27
|
+
# ...rather than:
|
28
|
+
# # => "be bigger than 2"
|
29
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
30
|
+
end
|
31
|
+
|
32
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
33
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
34
|
+
config.mock_with :rspec do |mocks|
|
35
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
36
|
+
# a real object. This is generally recommended, and will default to
|
37
|
+
# `true` in RSpec 4.
|
38
|
+
mocks.verify_partial_doubles = true
|
39
|
+
end
|
40
|
+
|
41
|
+
# These two settings work together to allow you to limit a spec run
|
42
|
+
# to individual examples or groups you care about by tagging them with
|
43
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
44
|
+
# get run.
|
45
|
+
config.filter_run :focus
|
46
|
+
config.run_all_when_everything_filtered = true
|
47
|
+
|
48
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
49
|
+
# For more details, see:
|
50
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
51
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
52
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
53
|
+
config.disable_monkey_patching!
|
54
|
+
|
55
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
56
|
+
# be too noisy due to issues in dependencies.
|
57
|
+
config.warnings = true
|
58
|
+
|
59
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
60
|
+
# file, and it's useful to allow more verbose output when running an
|
61
|
+
# individual spec file.
|
62
|
+
if config.files_to_run.one?
|
63
|
+
# Use the documentation formatter for detailed output,
|
64
|
+
# unless a formatter has already been configured
|
65
|
+
# (e.g. via a command-line flag).
|
66
|
+
config.default_formatter = 'doc'
|
67
|
+
end
|
68
|
+
|
69
|
+
# Print the 10 slowest examples and example groups at the
|
70
|
+
# end of the spec run, to help surface which specs are running
|
71
|
+
# particularly slow.
|
72
|
+
config.profile_examples = 10
|
73
|
+
|
74
|
+
# Run specs in random order to surface order dependencies. If you find an
|
75
|
+
# order dependency and want to debug it, you can fix the order by providing
|
76
|
+
# the seed, which is printed after each run.
|
77
|
+
# --seed 1234
|
78
|
+
config.order = :random
|
79
|
+
|
80
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
81
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
82
|
+
# test failures related to randomization by passing the same `--seed` value
|
83
|
+
# as the one that triggered the failure.
|
84
|
+
Kernel.srand config.seed
|
85
|
+
end
|
data/spec/sudoku_spec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'sudokusolver_ng'
|
2
|
+
|
3
|
+
RSpec.describe 'Driver spec' do
|
4
|
+
before(:all) do
|
5
|
+
@solver = SudokuSolverNg.new
|
6
|
+
|
7
|
+
@easy = File.read(File.expand_path("spec/boards/easy50.txt")).split("\n")
|
8
|
+
@top95 = File.read(File.expand_path("spec/boards/top95.txt")).split("\n")
|
9
|
+
@hard = File.read(File.expand_path("spec/boards/hardest.txt")).split("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def multiple(puzzles)
|
13
|
+
puzzles.map{|puzzle| @solver.check_solution(@solver.string_solution(@solver.search(@solver.parse_grid(puzzle)))) }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should give the developers of this gem the idea that it solves properly' do
|
17
|
+
puts "Easy puzzle (constraint satisfaction only): "
|
18
|
+
puts
|
19
|
+
@solver.print_grid(@solver.search(@solver.parse_grid(@easy.first)))
|
20
|
+
puts "Hard puzzle (constraint satisfaction + search): "
|
21
|
+
puts
|
22
|
+
@solver.print_grid(@solver.search(@solver.parse_grid(@hard.first)))
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should solve the board' do
|
26
|
+
puzzle = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......'
|
27
|
+
solution = @solver.string_solution(@solver.search(@solver.parse_grid(puzzle)))
|
28
|
+
|
29
|
+
expect(@solver.check_solution(solution)).to be true
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'run with an empty puzzle' do
|
33
|
+
puzzle = '.................................................................................'
|
34
|
+
solution = @solver.string_solution(@solver.search(@solver.parse_grid(puzzle)))
|
35
|
+
|
36
|
+
expect(@solver.check_solution(solution)).to be true
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should solve all easy boards' do
|
40
|
+
expect( multiple(@easy).all?{|solved| solved == true} ).to be true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should solve all top95 boards' do
|
44
|
+
expect( multiple(@top95).all?{|solved| solved == true} ).to be true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should solve all hard boards' do
|
48
|
+
expect( multiple(@hard).all?{|solved| solved == true} ).to be true
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sudokusolver_ng'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sudokusolver_ng"
|
8
|
+
spec.version = SudokuSolverNg::VERSION
|
9
|
+
spec.authors = ["Martin-Louis Bright", "Kasper Grubbe"]
|
10
|
+
spec.email = ["mlbright@gmail.com", "kaspergrubbe@gmail.com"]
|
11
|
+
spec.summary = %q{Commandline program and library for solving Sudoku puzzles}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sudokusolver_ng
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin-Louis Bright
|
8
|
+
- Kasper Grubbe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-01-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.6'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.6'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.1'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.1'
|
56
|
+
description:
|
57
|
+
email:
|
58
|
+
- mlbright@gmail.com
|
59
|
+
- kaspergrubbe@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- ".rspec"
|
66
|
+
- ".travis.yml"
|
67
|
+
- COPYING
|
68
|
+
- Gemfile
|
69
|
+
- LICENSE
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- lib/sudokusolver_ng.rb
|
73
|
+
- spec/boards/easy50.txt
|
74
|
+
- spec/boards/hardest.txt
|
75
|
+
- spec/boards/top95.txt
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/sudoku_spec.rb
|
78
|
+
- sudokusolver_ng.gemspec
|
79
|
+
homepage: ''
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.4.8
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Commandline program and library for solving Sudoku puzzles
|
103
|
+
test_files:
|
104
|
+
- spec/boards/easy50.txt
|
105
|
+
- spec/boards/hardest.txt
|
106
|
+
- spec/boards/top95.txt
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
- spec/sudoku_spec.rb
|