phg_sudoku_solver 0.0.2 → 0.0.3
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.
- checksums.yaml +5 -13
- data/.gitignore +1 -0
- data/.idea/phg_sudoku_solver.iml +150 -0
- data/.idea/workspace.xml +426 -144
- data/README.md +65 -63
- data/lib/phg_sudoku_solver.rb +22 -18
- data/lib/phg_sudoku_solver/version.rb +1 -1
- data/test/sudoku_test.rb +30 -11
- metadata +5 -5
data/README.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# PhgSudokuSolver
|
2
2
|
|
3
|
-
|
3
|
+
##A simple little gem for solving sudoku puzzles.##
|
4
4
|
|
5
|
-
Miles Porter
|
6
|
-
Senior Software Consultant
|
7
|
-
Painted Harmony Group, Inc
|
8
|
-
mporter
|
5
|
+
**Miles Porter**<br>
|
6
|
+
**Senior Software Consultant**<br>
|
7
|
+
**Painted Harmony Group, Inc.**<br>
|
8
|
+
**mporter@paintedharmony.com**<br>
|
9
9
|
|
10
|
-
|
10
|
+
|
11
|
+
# Background:
|
12
|
+
I first started doing Sudoku puzzles a few years ago while on a 3 hour plane flight. I was hooked. Eventually, I came
|
11
13
|
across a Sudoku that I could not do. I was completely frustrated, and the puzzle that I was working on was printed in a
|
12
14
|
weekly newspaper and I didn't want to wait an entire week to see the solution. So, I did what any good software engineer
|
13
15
|
would do... I decided to write a program/algorithm to solve the puzzle. At first, I tried the brute-force method of
|
@@ -34,84 +36,84 @@ Or install it yourself as:
|
|
34
36
|
|
35
37
|
The following snipit illustrates show to use this gem (taken from tests, mind you.)
|
36
38
|
|
37
|
-
± irb
|
38
|
-
1.9.3p194 :001 > require 'phg_sudoku_solver'
|
39
|
-
=> true
|
40
|
-
1.9.3p194 :002 >
|
41
|
-
1.9.3p194 :003 > a = [ "5xx4x67xx", "xxx5xx9xx", "2xxx17x4x", "xxx72xx1x", "9xxxxxxx8", "x7xx68xxx", "x3x27xxx5", "xx4xx3xxx","xx26x4xx3"]
|
42
|
-
=> ["5xx4x67xx", "xxx5xx9xx", "2xxx17x4x", "xxx72xx1x", "9xxxxxxx8", "x7xx68xxx", "x3x27xxx5", "xx4xx3xxx", "xx26x4xx3"]
|
43
|
-
1.9.3p194 :004 >
|
44
|
-
1.9.3p194 :005 > s = Sudoku.new(a)
|
45
39
|
|
46
|
-
|
40
|
+
Create a new Sudoku instance by passing in an array of 9 strings. Each string needs to be 9 characters. Any non-
|
41
|
+
numeric character (1-9) is considered to be an unsolved cell. You can use spaces, Xs or whatever you like.
|
47
42
|
|
48
|
-
|
43
|
+
Call the `.solve` method on the sudoku instance that you create.
|
49
44
|
|
50
|
-
|
45
|
+
To get the results of the solved sudoku, you can
|
46
|
+
1) Call the '.dump_known_cells_str' method, which will return a formatted string that represents the solved puzzle.
|
47
|
+
2) Iterate over the '.get_fixed_value(r,c)' method, where r and c represent the row and column. The data returned
|
48
|
+
will be the value found for that cell.
|
51
49
|
|
52
|
-
x.dump_known_cells_str
|
53
|
-
=> "\\n 5 1 8 | 4 9 6 | 7 3 2 \\n
|
54
|
-
6 4 7 | 5 3 2 | 9 8 1 \\n
|
55
|
-
2 9 3 | 8 1 7 | 5 4 6 \\n
|
56
|
-
----------------------------------------\\n
|
57
|
-
3 8 5 | 7 2 9 | 6 1 4 \\n
|
58
|
-
9 2 6 | 1 4 5 | 3 7 8 \\n
|
59
|
-
4 7 1 | 3 6 8 | 2 5 9 \\n
|
60
|
-
----------------------------------------\\n
|
61
|
-
8 3 9 | 2 7 1 | 4 6 5 \\n
|
62
|
-
1 6 4 | 9 5 3 | 8 2 7 \\n
|
63
|
-
7 5 2 | 6 8 4 | 1 9 3 \\n"
|
64
50
|
|
65
|
-
|
51
|
+
Example from irb...
|
66
52
|
|
67
|
-
|
68
|
-
|
69
|
-
|
53
|
+
<pre>
|
54
|
+
require 'phg_sudoku_solver'
|
55
|
+
a = [ "5xx4x67xx",
|
56
|
+
"xxx5xx9xx",
|
57
|
+
"2xxx17x4x",
|
58
|
+
"xxx72xx1x",
|
59
|
+
"9xxxxxxx8",
|
60
|
+
"x7xx68xxx",
|
61
|
+
"x3x27xxx5",
|
62
|
+
"xx4xx3xxx",
|
63
|
+
"xx26x4xx3"]
|
70
64
|
|
71
|
-
|
65
|
+
s = Sudoku.new(a)
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
will be the value found for that cell.
|
67
|
+
x = s.solve
|
68
|
+
|
69
|
+
x.dump_known_cells_str
|
77
70
|
|
78
|
-
|
71
|
+
5 1 8 | 4 9 6 | 7 3 2
|
72
|
+
6 4 7 | 5 3 2 | 9 8 1
|
73
|
+
2 9 3 | 8 1 7 | 5 4 6
|
74
|
+
----------------------------------------
|
75
|
+
3 8 5 | 7 2 9 | 6 1 4
|
76
|
+
9 2 6 | 1 4 5 | 3 7 8
|
77
|
+
4 7 1 | 3 6 8 | 2 5 9
|
78
|
+
----------------------------------------
|
79
|
+
8 3 9 | 2 7 1 | 4 6 5
|
80
|
+
1 6 4 | 9 5 3 | 8 2 7
|
81
|
+
7 5 2 | 6 8 4 | 1 9 3
|
82
|
+
</pre>
|
83
|
+
Note: The display has been cleaned up a bit above.
|
79
84
|
|
80
|
-
|
85
|
+
## WHEN SOMETHING GOES WRONG... AND SOMETHING ALWAYS GOES WRONG...
|
81
86
|
|
82
|
-
|
83
|
-
=> ["123123123"]
|
84
|
-
1.9.3p194 :016 > s = Sudoku.new(a)
|
85
|
-
Exception: Sudoku entered appears to be invalid.
|
86
|
-
from /Users/miles_r_porter/.rvm/gems/ruby-1.9.3-p194/gems/phg_sudoku_solver-0.0.2/lib/phg_sudoku_solver.rb:47:in `initialize'
|
87
|
-
from (irb):16:in `new'
|
88
|
-
from (irb):16
|
89
|
-
from /Users/miles_r_porter/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
|
90
|
-
1.9.3p194 :017 >
|
87
|
+
1. If the sudoku entered is invalid, the solve method will return an error.
|
91
88
|
|
92
|
-
|
89
|
+
<pre>
|
90
|
+
a = ['123123123']
|
91
|
+
s = Sudoku.new(a)
|
92
|
+
Exception: Sudoku entered appears to be invalid.
|
93
|
+
...
|
94
|
+
</pre>
|
93
95
|
|
94
|
-
|
95
|
-
=> ["123456789", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx", "xxxxxxxxx"]
|
96
|
-
1.9.3p194 :019 > s = Sudoku.new(a)
|
96
|
+
2. Some sudoku are just to complex for the engine to compute a solution in the given maximum iterations
|
97
97
|
|
98
|
-
|
98
|
+
<pre>
|
99
|
+
a = ["123456789","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx","xxxxxxxxx"]
|
100
|
+
s = Sudoku.new(a)
|
101
|
+
x = s.solve
|
102
|
+
Exception: Solution taking too long!
|
103
|
+
</pre>
|
99
104
|
|
100
|
-
1.9.3p194 :020 > x = s.solve()
|
101
105
|
|
102
|
-
1.9.3p194 :012 > x = s.solve
|
103
|
-
Exception: Solution taking too long!\n\n
|
104
106
|
|
105
|
-
|
106
|
-
|
107
|
+
Note: The number of iterations are checked after each recursion, so there total iterations may exceed the max
|
108
|
+
iterations set. You can set the max iterations like this:
|
107
109
|
|
108
|
-
|
110
|
+
`s.set_max_iterations(500)`
|
109
111
|
|
110
|
-
|
112
|
+
I have created a sample app running on Heroku (that includes a link to source code) that uses the gem...
|
111
113
|
|
112
|
-
|
114
|
+
[Sample App Running On Heroku](https://sudoku-solver-2.herokuapp.com/)
|
113
115
|
|
114
|
-
|
116
|
+
More features will be released at some point. Enjoy!
|
115
117
|
|
116
118
|
|
117
119
|
## Contributing
|
data/lib/phg_sudoku_solver.rb
CHANGED
@@ -83,11 +83,13 @@ class Sudoku
|
|
83
83
|
def solve
|
84
84
|
iteration=0
|
85
85
|
no_progress_count = 0
|
86
|
+
print_debug 'Max Iterations = %s' % @max_iterations
|
86
87
|
|
87
88
|
while @solved_cell_count!=81
|
88
89
|
iteration+=1
|
89
90
|
@total_iterations+=1
|
90
91
|
print_debug 'Iteration: %s SolvedCells: %s\n' % [iteration, @solved_cell_count]
|
92
|
+
puts dump_known_cells_str
|
91
93
|
compute_possible_values()
|
92
94
|
|
93
95
|
begin
|
@@ -311,24 +313,26 @@ class Sudoku
|
|
311
313
|
|
312
314
|
def recurse
|
313
315
|
recurse = copy()
|
314
|
-
(
|
315
|
-
(0..8).each { |
|
316
|
-
|
317
|
-
(
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
316
|
+
(2..8).each { |guesses|
|
317
|
+
(0..8).each { |r|
|
318
|
+
(0..8).each { |c|
|
319
|
+
if (recurse.get_possible_values(r, c).count==guesses) #Staring with cells that have the fewest to solve.
|
320
|
+
(0..recurse.get_possible_values(r, c).count).each { |j|
|
321
|
+
unless recurse.get_possible_values(r, c)[j].nil?
|
322
|
+
print_debug '\nStarting recursion with (%s,%s) set to %s\n' % [r, c, recurse.get_possible_values(r, c)[j]]
|
323
|
+
recurse.set_fixed_value(r, c, recurse.get_possible_values(r, c)[j])
|
324
|
+
print_debug('Recursion starting...')
|
325
|
+
recurse, iterations = recurse.solve
|
326
|
+
@total_iterations = @total_iterations + iterations
|
327
|
+
if recurse!=nil
|
328
|
+
return recurse
|
329
|
+
else
|
330
|
+
recurse = copy()
|
331
|
+
end
|
328
332
|
end
|
329
|
-
|
330
|
-
|
331
|
-
|
333
|
+
}
|
334
|
+
end
|
335
|
+
}
|
332
336
|
}
|
333
337
|
}
|
334
338
|
print_debug('Dead end found.\n')
|
@@ -483,7 +487,7 @@ class Sudoku
|
|
483
487
|
|
484
488
|
def print_debug(message)
|
485
489
|
if @debug
|
486
|
-
|
490
|
+
puts message + '\n'
|
487
491
|
end
|
488
492
|
end
|
489
493
|
|
data/test/sudoku_test.rb
CHANGED
@@ -19,7 +19,7 @@ class PhgSudokuSolverTest < Test::Unit::TestCase
|
|
19
19
|
|
20
20
|
# Cell initialize
|
21
21
|
def test_initialize_sudoku
|
22
|
-
a =
|
22
|
+
a = ["5xx4x67xx",
|
23
23
|
"xxx5xx9xx",
|
24
24
|
"2xxx17x4x",
|
25
25
|
"xxx72xx1x",
|
@@ -386,22 +386,41 @@ class PhgSudokuSolverTest < Test::Unit::TestCase
|
|
386
386
|
end
|
387
387
|
|
388
388
|
def test_too_long_solve
|
389
|
-
a = [ "
|
390
|
-
"
|
391
|
-
"
|
389
|
+
a = [ "123789456",
|
390
|
+
"456123789",
|
391
|
+
"789456123",
|
392
392
|
|
393
|
-
"
|
394
|
-
"
|
395
|
-
"
|
393
|
+
"xxxxxxxxx",
|
394
|
+
"xxxxxxxxx",
|
395
|
+
"xxxxxx231",
|
396
396
|
|
397
|
-
"
|
398
|
-
"
|
399
|
-
"
|
397
|
+
"xxxxxxxxx",
|
398
|
+
"xxxxxxxxx",
|
399
|
+
"xxxxxxxxx"]
|
400
400
|
s1 = Sudoku.new(a)
|
401
|
-
s1.set_max_iterations(
|
401
|
+
s1.set_max_iterations(10)
|
402
402
|
self.assert_raise(Exception) {
|
403
403
|
s1.solve() }
|
404
404
|
end
|
405
405
|
|
406
|
+
def test_solve_super_hard
|
407
|
+
a = [ "xx8xx3xx5",
|
408
|
+
"xxxx4x9xx",
|
409
|
+
"x4x8xx7xx",
|
410
|
+
"xx3x5xx68",
|
411
|
+
"xxx462xxx",
|
412
|
+
"27xx3x4xx",
|
413
|
+
"xx4xx6x2x",
|
414
|
+
"xx1x9xxxx",
|
415
|
+
"8xx3xx6xx"]
|
416
|
+
|
417
|
+
s = Sudoku.new(a)
|
418
|
+
s.set_max_iterations(10000)
|
419
|
+
s.set_debug(true)
|
420
|
+
solved, total_iterations = s.solve()
|
421
|
+
puts "Got to here."
|
422
|
+
puts solved.dump_to_str
|
423
|
+
self.assert(solved.validate_sudoku, "Solved sudoku is invalid!!!")
|
424
|
+
end
|
406
425
|
|
407
426
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phg_sudoku_solver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Porter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A simple sudoku solving utility that uses two dimensional arrays and
|
14
14
|
recursion.
|
@@ -51,17 +51,17 @@ require_paths:
|
|
51
51
|
- lib
|
52
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - '>='
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
requirements: []
|
63
63
|
rubyforge_project:
|
64
|
-
rubygems_version: 2.
|
64
|
+
rubygems_version: 2.4.5
|
65
65
|
signing_key:
|
66
66
|
specification_version: 4
|
67
67
|
summary: A simple sudoku solving utility
|