sudoku_bardi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE.md +20 -0
- data/README.md +80 -0
- data/lib/sudoku_bardi.rb +272 -0
- data/spec/sudoku_bardi_spec.rb +18 -0
- data/spec/support/no_should_rspec.rb +9 -0
- data/spec/support/sudoku_bardi_test.rb +19 -0
- data/spec/support/sudoku_fixture.rb +76 -0
- data/sudoku_bardi.gemspec +27 -0
- metadata +72 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Bardi Einarsson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
sudoku\_bardi
|
2
|
+
=============
|
3
|
+
|
4
|
+
## Summary
|
5
|
+
|
6
|
+
Sudoku solver, validator and generator
|
7
|
+
|
8
|
+
## Description
|
9
|
+
|
10
|
+
sudoku\_bardi (see websudoku.com) is a set of tools for solving, validating and generating Sudoku puzzles.
|
11
|
+
|
12
|
+
## Why should one the sudoku\_bardi tools?
|
13
|
+
|
14
|
+
The Sudoku solution algorithm does not use recursion -- runs in limited memory and can find all solutions of incorrect puzzles.
|
15
|
+
|
16
|
+
## Version
|
17
|
+
|
18
|
+
v0.1.0 with comprehensive tests, was developed using ruby 1.9.3 and rspec 2.13.0. It does not have any specific Sudoku puzzle generators yet; however it provides a good foundation for any generator as it provides validation of solution uniqueness and shows the operations used in the puzzle solution(s).
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Get the v0.1.0 gem or clone the repository.
|
23
|
+
|
24
|
+
## Usage and documentation
|
25
|
+
|
26
|
+
Study the programs in the spec/ directory. See below for more information.
|
27
|
+
|
28
|
+
Try the software in irb:
|
29
|
+
|
30
|
+
irb prompt> load 'lib/sudoku_bardi.rb'
|
31
|
+
irb prompt> load 'spec/support/sudoku_fixture.rb'
|
32
|
+
irb prompt> include SudokuBardi
|
33
|
+
irb prompt> include SudokuFixture
|
34
|
+
irb prompt> s = Solver.new EXAMPLE2
|
35
|
+
irb prompt> s.take 1
|
36
|
+
|
37
|
+
After -- s.take 1 -- one will see a listing of an array of size 2. The 0 element is an array of operations made to get the solution. The 1 element is the solution.
|
38
|
+
|
39
|
+
[75, SudokuBardi::Five] means assign Five to square 75.
|
40
|
+
["start"] ends the operations to construct the puzzle.
|
41
|
+
["mark", 2, [SudokuBardi::Seven, SudokuBardi::Two], 1]
|
42
|
+
shows a backtrack mark for square 2. That type of thing
|
43
|
+
occurs when there is no longer any square with only one
|
44
|
+
possible value. The solver had to try Seven and Two to
|
45
|
+
get all possible solutions. The 1 points at Two as the
|
46
|
+
current value chosen.
|
47
|
+
|
48
|
+
0 1 2 3 4 5 6 7 8
|
49
|
+
9 10 11 12 13 14 15 16 17
|
50
|
+
18 19 20 21 22 23 24 25 26
|
51
|
+
27 28 29 30 31 32 33 34 35
|
52
|
+
36 37 38 39 40 41 42 43 44
|
53
|
+
45 46 47 48 49 50 51 52 53
|
54
|
+
54 55 56 57 58 59 60 61 62
|
55
|
+
63 64 65 66 67 68 69 70 71
|
56
|
+
72 73 74 75 76 77 78 79 80
|
57
|
+
|
58
|
+
## Requirements
|
59
|
+
|
60
|
+
Most likely any recent version of 1.9 ruby works.
|
61
|
+
|
62
|
+
## Test with rspec ~> 2.11, rspec -fd
|
63
|
+
|
64
|
+
Try the software in irb:
|
65
|
+
|
66
|
+
irb prompt> load 'spec/support/sudoku_bardi_test.rb'
|
67
|
+
irb prompt> load 'spec/support/sudoku_fixture.rb'
|
68
|
+
|
69
|
+
Note, to find the gem installation directory:
|
70
|
+
|
71
|
+
irb prompt> require 'sudoku_bardi'
|
72
|
+
irb prompt> $".grep(/sudoku_bardi/)[0]
|
73
|
+
irb prompt> Sudoku::Solver.new.method(:each).source_location
|
74
|
+
irb prompt> exit
|
75
|
+
|
76
|
+
## License
|
77
|
+
|
78
|
+
Copyright (c) 2013 Bardi Einarsson. Released under the MIT License. See the [LICENSE][license] file for further details.
|
79
|
+
|
80
|
+
[license]: https://github.com/bardibardi/sudoku_bardi/blob/master/LICENSE.md
|
data/lib/sudoku_bardi.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
module SudokuBardi
|
2
|
+
|
3
|
+
# row, column, box
|
4
|
+
RCB = [
|
5
|
+
[0,0,0],[0,1,0],[0,2,0],[0,3,1],[0,4,1],[0,5,1],[0,6,2],[0,7,2],[0,8,2],
|
6
|
+
[1,0,0],[1,1,0],[1,2,0],[1,3,1],[1,4,1],[1,5,1],[1,6,2],[1,7,2],[1,8,2],
|
7
|
+
[2,0,0],[2,1,0],[2,2,0],[2,3,1],[2,4,1],[2,5,1],[2,6,2],[2,7,2],[2,8,2],
|
8
|
+
[3,0,3],[3,1,3],[3,2,3],[3,3,4],[3,4,4],[3,5,4],[3,6,5],[3,7,5],[3,8,5],
|
9
|
+
[4,0,3],[4,1,3],[4,2,3],[4,3,4],[4,4,4],[4,5,4],[4,6,5],[4,7,5],[4,8,5],
|
10
|
+
[5,0,3],[5,1,3],[5,2,3],[5,3,4],[5,4,4],[5,5,4],[5,6,5],[5,7,5],[5,8,5],
|
11
|
+
[6,0,6],[6,1,6],[6,2,6],[6,3,7],[6,4,7],[6,5,7],[6,6,8],[6,7,8],[6,8,8],
|
12
|
+
[7,0,6],[7,1,6],[7,2,6],[7,3,7],[7,4,7],[7,5,7],[7,6,8],[7,7,8],[7,8,8],
|
13
|
+
[8,0,6],[8,1,6],[8,2,6],[8,3,7],[8,4,7],[8,5,7],[8,6,8],[8,7,8],[8,8,8]
|
14
|
+
]
|
15
|
+
|
16
|
+
class Piece; end
|
17
|
+
class One < Piece; end
|
18
|
+
class Two < Piece; end
|
19
|
+
class Three < Piece; end
|
20
|
+
class Four < Piece; end
|
21
|
+
class Five < Piece; end
|
22
|
+
class Six < Piece; end
|
23
|
+
class Seven < Piece; end
|
24
|
+
class Eight < Piece; end
|
25
|
+
class Nine < Piece; end
|
26
|
+
|
27
|
+
TO_S = { nil => '.',
|
28
|
+
One => '1', Two => '2', Three => '3',
|
29
|
+
Four => '4', Five => '5', Six => '6',
|
30
|
+
Seven => '7', Eight => '8', Nine => '9'
|
31
|
+
}
|
32
|
+
|
33
|
+
NINE = [One, Two, Three, Four, Five, Six, Seven, Eight, Nine]
|
34
|
+
|
35
|
+
class Solver
|
36
|
+
|
37
|
+
include Enumerable
|
38
|
+
|
39
|
+
def self.piece_from_char c
|
40
|
+
i = c.bytes.first - 49 # 49 == '0'.bytes.first + 1
|
41
|
+
return nil if 0 > i
|
42
|
+
NINE[i]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.constraint c
|
46
|
+
a = []
|
47
|
+
c.size.times do
|
48
|
+
a << c.dup
|
49
|
+
end
|
50
|
+
a
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize board_str = ''
|
54
|
+
start
|
55
|
+
process board_str
|
56
|
+
push ['start']
|
57
|
+
end
|
58
|
+
|
59
|
+
def start
|
60
|
+
@board = [nil] * (NINE.size * NINE.size)
|
61
|
+
@rows = self.class.constraint NINE
|
62
|
+
@columns = self.class.constraint NINE
|
63
|
+
@boxes = self.class.constraint NINE
|
64
|
+
@ops = []
|
65
|
+
end
|
66
|
+
|
67
|
+
def board_to_s
|
68
|
+
a = @board.map do |piece|
|
69
|
+
TO_S[piece]
|
70
|
+
end
|
71
|
+
a.join ''
|
72
|
+
end
|
73
|
+
|
74
|
+
def possible i
|
75
|
+
r, c, b = RCB[i]
|
76
|
+
@rows[r] & @columns[c] & @boxes[b]
|
77
|
+
end
|
78
|
+
|
79
|
+
def do_op op
|
80
|
+
i, piece = op
|
81
|
+
r, c, b = RCB[i]
|
82
|
+
@rows[r] -= [piece]
|
83
|
+
@columns[c] -= [piece]
|
84
|
+
@boxes[b] -= [piece]
|
85
|
+
@board[i] = piece
|
86
|
+
op
|
87
|
+
end
|
88
|
+
|
89
|
+
def undo_op op
|
90
|
+
i, piece = op
|
91
|
+
r, c, b = RCB[i]
|
92
|
+
@rows[r] += [piece]
|
93
|
+
@columns[c] += [piece]
|
94
|
+
@boxes[b] += [piece]
|
95
|
+
@board[i] = nil
|
96
|
+
op
|
97
|
+
end
|
98
|
+
|
99
|
+
def pop_start
|
100
|
+
@ops.pop
|
101
|
+
end
|
102
|
+
|
103
|
+
def push_start
|
104
|
+
push ['start']
|
105
|
+
end
|
106
|
+
|
107
|
+
def pop
|
108
|
+
return nil if 'start' == @ops[-1][0]
|
109
|
+
@ops.pop
|
110
|
+
end
|
111
|
+
|
112
|
+
def push op
|
113
|
+
@ops << op
|
114
|
+
end
|
115
|
+
|
116
|
+
def process_str str_as_ops
|
117
|
+
ops = []
|
118
|
+
str_as_ops.each_char.with_index do |char, i|
|
119
|
+
piece = self.class.piece_from_char char
|
120
|
+
if piece
|
121
|
+
ps = possible i
|
122
|
+
raise "impossible #{char} at #{i}" unless ps.include? piece
|
123
|
+
push do_op([i, piece])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
ops
|
127
|
+
end
|
128
|
+
|
129
|
+
def play ops
|
130
|
+
ops.each do |op|
|
131
|
+
push do_op(op)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def process board_str
|
136
|
+
play process_str(board_str)
|
137
|
+
end
|
138
|
+
|
139
|
+
def single i
|
140
|
+
processed_single = false
|
141
|
+
backtrack = false
|
142
|
+
ps = possible i
|
143
|
+
if 0 == ps.length
|
144
|
+
backtrack = true
|
145
|
+
end
|
146
|
+
if 1 == ps.length
|
147
|
+
processed_single = true
|
148
|
+
push do_op([i, ps[0]])
|
149
|
+
end
|
150
|
+
[processed_single, backtrack]
|
151
|
+
end
|
152
|
+
|
153
|
+
def singles
|
154
|
+
had_singles = true
|
155
|
+
finished = true
|
156
|
+
while had_singles
|
157
|
+
finished = true
|
158
|
+
single_found = false
|
159
|
+
@board.each_with_index do |piece, i|
|
160
|
+
if !piece
|
161
|
+
finished = false
|
162
|
+
processed_single, backtrack = single i
|
163
|
+
single_found ||= processed_single
|
164
|
+
if backtrack
|
165
|
+
push ['backtrack', i]
|
166
|
+
return finished
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
had_singles = single_found
|
171
|
+
end
|
172
|
+
finished
|
173
|
+
end
|
174
|
+
|
175
|
+
def min_choices
|
176
|
+
j = nil
|
177
|
+
count = 10
|
178
|
+
@board.each_with_index do |piece, i|
|
179
|
+
if !piece
|
180
|
+
len = possible(i).size
|
181
|
+
if len < count
|
182
|
+
j = i
|
183
|
+
count = len
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
j
|
188
|
+
end
|
189
|
+
|
190
|
+
def gen_choices
|
191
|
+
a = []
|
192
|
+
@board.each_with_index do |piece, i|
|
193
|
+
if !piece
|
194
|
+
ps = possible(i)
|
195
|
+
if 1 < ps.size
|
196
|
+
a += ps.map {|e| [i, e]}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
a
|
201
|
+
end
|
202
|
+
|
203
|
+
def add_mark i
|
204
|
+
push ['mark', i, possible(i), -1]
|
205
|
+
end
|
206
|
+
|
207
|
+
def try
|
208
|
+
mark = pop.dup
|
209
|
+
mark[3] += 1
|
210
|
+
piece = mark[2][mark[3]]
|
211
|
+
push mark
|
212
|
+
if piece
|
213
|
+
push do_op([mark[1], piece])
|
214
|
+
else
|
215
|
+
push ['backtrack', mark[1]]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def do_backtrack
|
220
|
+
pop if 'mark' == @ops[-1][0]
|
221
|
+
op = pop
|
222
|
+
if op
|
223
|
+
while 'mark' != op[0]
|
224
|
+
undo_op op if op[0].kind_of? Numeric
|
225
|
+
op = pop
|
226
|
+
return @ops unless op
|
227
|
+
end
|
228
|
+
end
|
229
|
+
push op if op
|
230
|
+
end
|
231
|
+
|
232
|
+
def solve_one
|
233
|
+
solved = false
|
234
|
+
until solved
|
235
|
+
if 'backtrack' == @ops[-1][0]
|
236
|
+
pop
|
237
|
+
do_backtrack
|
238
|
+
if 'start' == @ops[-1][0]
|
239
|
+
return nil
|
240
|
+
end
|
241
|
+
try
|
242
|
+
else
|
243
|
+
solved = singles
|
244
|
+
if !solved
|
245
|
+
i = min_choices
|
246
|
+
add_mark i
|
247
|
+
try
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
[@ops, board_to_s]
|
252
|
+
end
|
253
|
+
|
254
|
+
def each
|
255
|
+
first = true
|
256
|
+
finished = false
|
257
|
+
until finished
|
258
|
+
push ['backtrack', 81] unless first
|
259
|
+
first = false
|
260
|
+
result = solve_one
|
261
|
+
if result
|
262
|
+
yield [result[0].dup, result[1]]
|
263
|
+
else
|
264
|
+
finished = true
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end # Solver
|
270
|
+
|
271
|
+
end # SudokuBardi
|
272
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'support/no_should_rspec'
|
2
|
+
require 'support/sudoku_bardi_test'
|
3
|
+
require 'support/sudoku_fixture'
|
4
|
+
include SudokuFixture
|
5
|
+
|
6
|
+
describe SudokuBardi::Solver do
|
7
|
+
|
8
|
+
it "should solve puzzles with unique solutions" do
|
9
|
+
expect(SudokuBardi.unique_solution?(EXAMPLE, SOLUTION)).to be_true
|
10
|
+
expect(SudokuBardi.unique_solution?(EXAMPLE2, SOLUTION2)).to be_true
|
11
|
+
expect(SudokuBardi.unique_solution?(EXAMPLE3, SOLUTION3)).to be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should solve puzzles with multiple solutions" do
|
15
|
+
expect(SudokuBardi.empty_puzzle_has_at_least_100_solutions?).to be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../../lib/sudoku_bardi'
|
2
|
+
|
3
|
+
module SudokuBardi
|
4
|
+
|
5
|
+
def self.unique_solution? puzzle_board, solution_board
|
6
|
+
s = Solver.new puzzle_board
|
7
|
+
solutions = s.take 2
|
8
|
+
return nil unless 1 == solutions.size
|
9
|
+
solutions[0][1] == solution_board
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.empty_puzzle_has_at_least_100_solutions?
|
13
|
+
s = Solver.new
|
14
|
+
solutions = s.take 100
|
15
|
+
100 == solutions.size
|
16
|
+
end
|
17
|
+
|
18
|
+
end # SudokuBardi
|
19
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module SudokuFixture
|
2
|
+
|
3
|
+
EXAMPLE = %w(
|
4
|
+
...3...1.
|
5
|
+
8...167.2
|
6
|
+
1....93..
|
7
|
+
7.3...645
|
8
|
+
5.68.49.7
|
9
|
+
294...8.1
|
10
|
+
..17....8
|
11
|
+
3.792...4
|
12
|
+
.6...1...
|
13
|
+
).join ''
|
14
|
+
|
15
|
+
SOLUTION = %w(
|
16
|
+
672385419
|
17
|
+
839416752
|
18
|
+
145279386
|
19
|
+
783192645
|
20
|
+
516834927
|
21
|
+
294657831
|
22
|
+
421763598
|
23
|
+
357928164
|
24
|
+
968541273
|
25
|
+
).join ''
|
26
|
+
|
27
|
+
EXAMPLE2 = %w(
|
28
|
+
.....8..6
|
29
|
+
9.17.53.4
|
30
|
+
.....4.1.
|
31
|
+
1..94..5.
|
32
|
+
49..5..67
|
33
|
+
.2..71..3
|
34
|
+
.3.4.....
|
35
|
+
2.53.97.1
|
36
|
+
7..5.....
|
37
|
+
).join ''
|
38
|
+
|
39
|
+
SOLUTION2 = %w(
|
40
|
+
342198576
|
41
|
+
961725384
|
42
|
+
857634912
|
43
|
+
173946258
|
44
|
+
498253167
|
45
|
+
526871493
|
46
|
+
639417825
|
47
|
+
285369741
|
48
|
+
714582639
|
49
|
+
).join ''
|
50
|
+
|
51
|
+
EXAMPLE3 = %w(
|
52
|
+
68.35....
|
53
|
+
....1..2.
|
54
|
+
..7.6.9..
|
55
|
+
1........
|
56
|
+
3.5.9.1.2
|
57
|
+
........9
|
58
|
+
..3.2.8..
|
59
|
+
.6..7....
|
60
|
+
....45.31
|
61
|
+
).join ''
|
62
|
+
|
63
|
+
SOLUTION3 = %w(
|
64
|
+
689352417
|
65
|
+
534917628
|
66
|
+
217468953
|
67
|
+
192584376
|
68
|
+
345796182
|
69
|
+
876231549
|
70
|
+
753129864
|
71
|
+
461873295
|
72
|
+
928645731
|
73
|
+
).join ''
|
74
|
+
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'sudoku_bardi'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.date = '2013-05-23'
|
5
|
+
s.summary = "Sudoku solver, validator and generator"
|
6
|
+
s.description = "sudoku_bardi (see websudoku.com) is a set of tools for solving, validating and generating Sudoku puzzles."
|
7
|
+
|
8
|
+
s.authors = ['Bardi Einarsson']
|
9
|
+
s.email = ['bardi@hotmail.com']
|
10
|
+
s.homepage = 'https://github.com/bardibardi/sudoku_bardi'
|
11
|
+
s.required_ruby_version = '>= 1.9.2'
|
12
|
+
s.add_development_dependency('rspec', '~> 2.11')
|
13
|
+
|
14
|
+
s.files = %w(
|
15
|
+
.gitignore
|
16
|
+
Gemfile
|
17
|
+
LICENSE.md
|
18
|
+
README.md
|
19
|
+
sudoku_bardi.gemspec
|
20
|
+
lib/sudoku_bardi.rb
|
21
|
+
spec/sudoku_bardi_spec.rb
|
22
|
+
spec/support/no_should_rspec.rb
|
23
|
+
spec/support/sudoku_bardi_test.rb
|
24
|
+
spec/support/sudoku_fixture.rb
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sudoku_bardi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bardi Einarsson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.11'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.11'
|
30
|
+
description: sudoku_bardi (see websudoku.com) is a set of tools for solving, validating
|
31
|
+
and generating Sudoku puzzles.
|
32
|
+
email:
|
33
|
+
- bardi@hotmail.com
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- LICENSE.md
|
41
|
+
- README.md
|
42
|
+
- sudoku_bardi.gemspec
|
43
|
+
- lib/sudoku_bardi.rb
|
44
|
+
- spec/sudoku_bardi_spec.rb
|
45
|
+
- spec/support/no_should_rspec.rb
|
46
|
+
- spec/support/sudoku_bardi_test.rb
|
47
|
+
- spec/support/sudoku_fixture.rb
|
48
|
+
homepage: https://github.com/bardibardi/sudoku_bardi
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 1.9.2
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.25
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Sudoku solver, validator and generator
|
72
|
+
test_files: []
|