sudokusolver 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1 @@
1
+ See the file called LICENSE
@@ -0,0 +1 @@
1
+ 2007-04-09 Version 0.1 => initial release
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.
@@ -0,0 +1,20 @@
1
+ This sofware was translated from the python source code
2
+ obtained from Peter Norvig's website:
3
+
4
+ http://www.norvig.com/sudoku.html
5
+ http://www.norvig.com/sudo.py
6
+
7
+ Thank you to Peter Norvig for the original python source code,
8
+ algorithms and exceptionally clear explanations.
9
+
10
+ example:
11
+ ========
12
+
13
+ require 'sudoku_solver'
14
+
15
+ # The puzzle representation is simply the 9 rows of the Sudoku grid stringed together
16
+ # from top to bottom (periods representing blank squares)
17
+
18
+ puzzle = "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......"
19
+ s = SudokuSolver.new
20
+ s.print_grid(s.search(s.parse_grid(puzzle)))
@@ -0,0 +1,256 @@
1
+ # Rakefile
2
+ require "rake/testtask"
3
+ require "rake/clean"
4
+ require "rake/rdoctask"
5
+ require "rake/gempackagetask"
6
+
7
+ #---
8
+ # The name of your project
9
+ PROJECT = "SudokuSolver"
10
+
11
+ # Your name, used in packaging.
12
+ MY_NAME = "Martin-Louis Bright"
13
+
14
+ # Your email address, used in packaging.
15
+ MY_EMAIL = "mlbright@gmail.com"
16
+
17
+ # Short summary of your project, used in packaging.
18
+ PROJECT_SUMMARY = "Commandline program and library for solving Sudoku puzzles"
19
+
20
+ # The project's package name (as opposed to its display name). Used for
21
+ # RubyForge connectivity and packaging.
22
+ UNIX_NAME = "sudokusolver"
23
+
24
+ # Your RubyForge user name.
25
+ RUBYFORGE_USER = ENV["RUBYFORGE_USER"] || "mlbright"
26
+
27
+ # Directory on RubyForge where your website's files should be uploaded.
28
+ WEBSITE_DIR = "sudokusolver"
29
+
30
+ # Output directory for the rdoc html files.
31
+ # If you don't have a custom homepage, and want to use the RDoc
32
+ # index.html as homepage, just set it to WEBSITE_DIR.
33
+ RDOC_HTML_DIR = "#{WEBSITE_DIR}/rdoc"
34
+
35
+ #---
36
+ # Variable settings for extension support.
37
+ EXT_DIR = "ext"
38
+ HAVE_EXT = File.directory?(EXT_DIR)
39
+ EXTCONF_FILES = FileList["#{EXT_DIR}/**/extconf.rb"]
40
+ EXT_SOURCES = FileList["#{EXT_DIR}/**/*.{c,h}"]
41
+ # Eventually add other files from EXT_DIR, like "MANIFEST"
42
+ EXT_DIST_FILES = EXT_SOURCES + EXTCONF_FILES
43
+
44
+ #---
45
+ REQUIRE_PATHS = ["lib"]
46
+ REQUIRE_PATHS << EXT_DIR if HAVE_EXT
47
+ $LOAD_PATH.concat(REQUIRE_PATHS)
48
+ # This library file defines the MyProject::VERSION constant.
49
+ require "#{UNIX_NAME}"
50
+ PROJECT_VERSION = eval("#{PROJECT}::VERSION") # e.g. "1.0.2"
51
+
52
+ #---
53
+ # Clobber object files and Makefiles generated by extconf.rb.
54
+ CLOBBER.include("#{EXT_DIR}/**/*.{so,dll,o}", "#{EXT_DIR}/**/Makefile")
55
+ # Clobber .config generated by setup.rb.
56
+ CLOBBER.include(".config")
57
+
58
+ #---
59
+ # Options common to RDocTask AND Gem::Specification.
60
+ # The --main argument specifies which file appears on the index.html page
61
+ GENERAL_RDOC_OPTS = {
62
+ "--title" => "#{PROJECT} API documentation",
63
+ "--main" => "README.rdoc"
64
+ }
65
+
66
+ # Additional RDoc formatted files, besides the Ruby source files.
67
+ RDOC_FILES = FileList["README.rdoc", "Changes.rdoc"]
68
+ # Remove the following line if you don't want to extract RDoc from
69
+ # the extension C sources.
70
+ RDOC_FILES.include(EXT_SOURCES)
71
+
72
+ # Ruby library code.
73
+ LIB_FILES = FileList["lib/**/*.rb"]
74
+
75
+ # Filelist with Test::Unit test cases.
76
+ TEST_FILES = FileList["test/**/tc_*.rb"]
77
+
78
+ # Executable scripts, all non-garbage files under bin/.
79
+ BIN_FILES = FileList["bin/*"]
80
+
81
+ # This filelist is used to create source packages.
82
+ # Include all Ruby and RDoc files.
83
+ DIST_FILES = FileList["**/*.rb", "**/*.rdoc"]
84
+ DIST_FILES.include("Rakefile", "COPYING", "LICENSE")
85
+ DIST_FILES.include(BIN_FILES)
86
+ DIST_FILES.include("data/**/*", "test/data/**/*")
87
+ DIST_FILES.include("#{WEBSITE_DIR}/**/*.{html,css}", "man/*.[0-9]")
88
+ # Don't package files which are autogenerated by RDocTask
89
+ DIST_FILES.exclude(/^(\.\/)?#{RDOC_HTML_DIR}(\/|$)/)
90
+ # Include extension source files.
91
+ DIST_FILES.include(EXT_DIST_FILES)
92
+ # Don't package temporary files, perhaps created by tests.
93
+ DIST_FILES.exclude("**/temp_*", "**/*.tmp")
94
+ # Don't get into recursion...
95
+ DIST_FILES.exclude(/^(\.\/)?pkg(\/|$)/)
96
+
97
+ #---
98
+ # Run the tests if rake is invoked without arguments.
99
+ task "default" => ["test"]
100
+
101
+ test_task_name = HAVE_EXT ? "run-tests" : "test"
102
+ Rake::TestTask.new(test_task_name) do |t|
103
+ t.test_files = TEST_FILES
104
+ t.libs = REQUIRE_PATHS
105
+ end
106
+
107
+ #---
108
+ # Set an environment variable with any configuration options you want to
109
+ # be passed through to "setup.rb config".
110
+ CONFIG_OPTS = ENV["CONFIG"]
111
+ if HAVE_EXT
112
+ file_create ".config" do
113
+ ruby "setup.rb config #{CONFIG_OPTS}"
114
+ end
115
+
116
+ desc "Configure and make extension. " +
117
+ "The CONFIG variable is passed to `setup.rb config'"
118
+ task "make-ext" => ".config" do
119
+ # The -q option suppresses messages from setup.rb.
120
+ ruby "setup.rb -q setup"
121
+ end
122
+
123
+ desc "Run tests after making the extension."
124
+ task "test" do
125
+ Rake::Task["make-ext"].invoke
126
+ Rake::Task["run-tests"].invoke
127
+ end
128
+ end
129
+
130
+ #---
131
+ # The "rdoc" task generates API documentation.
132
+ Rake::RDocTask.new("rdoc") do |t|
133
+ t.rdoc_files = RDOC_FILES + LIB_FILES
134
+ t.title = GENERAL_RDOC_OPTS["--title"]
135
+ t.main = GENERAL_RDOC_OPTS["--main"]
136
+ t.rdoc_dir = RDOC_HTML_DIR
137
+ end
138
+
139
+ #---
140
+ GEM_SPEC = Gem::Specification.new do |s|
141
+ s.name = UNIX_NAME
142
+ s.version = PROJECT_VERSION
143
+ s.summary = PROJECT_SUMMARY
144
+ s.rubyforge_project = UNIX_NAME
145
+ s.homepage = "http://#{UNIX_NAME}.rubyforge.org/"
146
+ s.author = MY_NAME
147
+ s.email = MY_EMAIL
148
+ s.files = DIST_FILES
149
+ s.test_files = TEST_FILES
150
+ s.executables = BIN_FILES.map { |fn| File.basename(fn) }
151
+ s.has_rdoc = true
152
+ s.extra_rdoc_files = RDOC_FILES
153
+ s.rdoc_options = GENERAL_RDOC_OPTS.to_a.flatten
154
+ if HAVE_EXT
155
+ s.extensions = EXTCONF_FILES
156
+ s.require_paths << EXT_DIR
157
+ end
158
+ end
159
+
160
+ # Now we can generate the package-related tasks.
161
+ Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
162
+ pkg.need_zip = true
163
+ pkg.need_tar = true
164
+ end
165
+
166
+ #---
167
+ desc "Upload website to RubyForge. " +
168
+ "scp will prompt for your RubyForge password."
169
+ task "publish-website" => ["rdoc"] do
170
+ rubyforge_path = "/var/www/gforge-projects/#{UNIX_NAME}/"
171
+ sh "scp -r #{WEBSITE_DIR}/* " +
172
+ "#{RUBYFORGE_USER}@rubyforge.org:#{rubyforge_path}",
173
+ :verbose => true
174
+ end
175
+
176
+ #---
177
+ task "rubyforge-setup" do
178
+ unless File.exist?(File.join(ENV["HOME"], ".rubyforge"))
179
+ puts "rubyforge will ask you to edit its config.yml now."
180
+ puts "Please set the `username' and `password' entries"
181
+ puts "to your RubyForge username and RubyForge password!"
182
+ puts "Press ENTER to continue."
183
+ $stdin.gets
184
+ sh "rubyforge setup", :verbose => true
185
+ end
186
+ end
187
+
188
+ task "rubyforge-login" => ["rubyforge-setup"] do
189
+ # Note: We assume that username and password were set in
190
+ # rubyforge's config.yml.
191
+ sh "rubyforge login", :verbose => true
192
+ end
193
+
194
+ task "publish-packages" => ["package", "rubyforge-login"] do
195
+ # Upload packages under pkg/ to RubyForge
196
+ # This task makes some assumptions:
197
+ # * You have already created a package on the "Files" tab on the
198
+ # RubyForge project page. See pkg_name variable below.
199
+ # * You made entries under package_ids and group_ids for this
200
+ # project in rubyforge's config.yml. If not, eventually read
201
+ # "rubyforge --help" and then run "rubyforge setup".
202
+ pkg_name = ENV["PKG_NAME"] || UNIX_NAME
203
+ cmd = "rubyforge add_release #{UNIX_NAME} #{pkg_name} " +
204
+ "#{PROJECT_VERSION} #{UNIX_NAME}-#{PROJECT_VERSION}"
205
+ cd "pkg" do
206
+ sh(cmd + ".gem", :verbose => true)
207
+ sh(cmd + ".tgz", :verbose => true)
208
+ sh(cmd + ".zip", :verbose => true)
209
+ end
210
+ end
211
+
212
+ #---
213
+ # The "prepare-release" task makes sure your tests run, and then generates
214
+ # files for a new release.
215
+ desc "Run tests, generate RDoc and create packages."
216
+ task "prepare-release" => ["clobber"] do
217
+ puts "Preparing release of #{PROJECT} version #{VERSION}"
218
+ Rake::Task["test"].invoke
219
+ Rake::Task["rdoc"].invoke
220
+ Rake::Task["package"].invoke
221
+ end
222
+
223
+ # The "publish" task is the overarching task for the whole project. It
224
+ # builds a release and then publishes it to RubyForge.
225
+ desc "Publish new release of #{PROJECT}"
226
+ task "publish" => ["prepare-release"] do
227
+ puts "Uploading documentation..."
228
+ Rake::Task["publish-website"].invoke
229
+ puts "Checking for rubyforge command..."
230
+ `rubyforge --help`
231
+ if $? == 0
232
+ puts "Uploading packages..."
233
+ Rake::Task["publish-packages"].invoke
234
+ puts "Release done!"
235
+ else
236
+ puts "Can't invoke rubyforge command."
237
+ puts "Either install rubyforge with 'gem install rubyforge'"
238
+ puts "and retry or upload the package files manually!"
239
+ end
240
+ end
241
+
242
+ #---
243
+ # $ rake -T
244
+ # rake clean # Remove any temporary products.
245
+ # rake clobber # Remove any generated file.
246
+ # rake clobber_package # Remove package products
247
+ # rake clobber_rdoc # Remove rdoc products
248
+ # rake package # Build all the packages
249
+ # rake prepare-release # Run tests, generate RDoc and create packages.
250
+ # rake publish # Publish new release of MyProject
251
+ # rake publish-website # Upload website to RubyForge. scp will prompt for your RubyForge password.
252
+ # rake rdoc # Build the rdoc HTML Files
253
+ # rake repackage # Force a rebuild of the package files
254
+ # rake rerdoc # Force a rebuild of the RDOC files
255
+ # rake test # Run tests for test
256
+ #---
@@ -0,0 +1,197 @@
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
+ class SudokuSolver
17
+ VERSION = "1.0"
18
+ attr_reader :rows, :cols, :squares, :unitlist, :peers, :units
19
+
20
+ def cross(a, b)
21
+ cp = Array.new # cross product
22
+ a.each do |x|
23
+ b.each do |y|
24
+ cp << x+y
25
+ end
26
+ end
27
+ return cp
28
+ end
29
+
30
+ def initialize()
31
+ @rows = ('A'..'I').to_a
32
+ @cols = ('1'..'9').to_a
33
+ @squares = cross(@rows, @cols)
34
+ @unitlist = Array.new
35
+ cols.each { |c| @unitlist.push(cross(rows, c)) }
36
+ rows.each { |r| @unitlist.push(cross(r, cols)) }
37
+ for rb in ['ABC','DEF','GHI'] do
38
+ for cb in ['123','456','789'] do
39
+ @unitlist << cross(rb.split(''),cb.split(''))
40
+ end
41
+ end
42
+
43
+ @units = Hash.new
44
+ squares.each do |s|
45
+ @units[s] = Array.new
46
+ unitlist.each do |u|
47
+ u.each do |x|
48
+ @units[s].push(u) if s == x
49
+ end
50
+ end
51
+ end
52
+
53
+ @peers = Hash.new
54
+ squares.each do |s|
55
+ @peers[s] = Array.new
56
+ units[s].each do |u|
57
+ u.each { |s2| @peers[s] << s2 if s2 != s }
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ def parse_grid(g)
64
+ g = g.chomp
65
+ g = g.split('')
66
+ values = Hash.new
67
+ # Initially any square can be anything.
68
+ squares.each { |s| values[s] = "123456789" }
69
+ for s,d in squares.zip(g)
70
+ return false unless assign(values, s, d) if d =~ /\d/
71
+ end
72
+ return values
73
+ end
74
+
75
+ def assign(values, s, d)
76
+ values[s].split('').each do |d2|
77
+ unless d2 == d
78
+ return false if eliminate(values, s, d2) == false
79
+ end
80
+ end
81
+ return values
82
+ end
83
+
84
+ def eliminate(values, s, d)
85
+ return values unless values[s].include?(d) ## Already eliminated.
86
+
87
+ values[s] = values[s].sub(d,'') ## Remove the digit from the string of possibilities
88
+ ## values[s].sub!(d,'') => why doesn't sub!() work?
89
+
90
+ return false if values[s].length == 0 ## Contradiction: no more values (no more digits can be assigned)
91
+
92
+ ## Remove digit from all peers
93
+ peers[s].each { |s2| return false unless eliminate(values, s2, values[s]) } if values[s].length == 1
94
+
95
+ ## Assign digit if, by elimination, there is only one square left
96
+ ## in the units for this square that can hold the digit
97
+ units[s].each do |u|
98
+ dplaces = Array.new
99
+ u.each { |s2| dplaces << s2 if values[s2].include?(d) }
100
+ return false if dplaces.length == 0 # bad
101
+ return false if assign(values, dplaces[0], d) == false if dplaces.length == 1
102
+ end
103
+ return values
104
+ end
105
+
106
+ def search(values)
107
+ return false if values == false
108
+
109
+ solved = true ## assumption
110
+ squares.each do |s|
111
+ unless values[s].length == 1
112
+ solved = false
113
+ break
114
+ end
115
+ end
116
+ return values if solved == true ## Solved!
117
+
118
+ min = 10
119
+ start = nil
120
+ squares.each do |s| ## Chose the undetermined square s with the fewest possibilities
121
+ l = values[s].length
122
+ if l > 1 && l < min
123
+ min = l
124
+ start = s
125
+ end
126
+ end
127
+
128
+ values[start].split('').each do |d|
129
+ solution = search(assign(values.clone,start,d))
130
+ return solution unless solution == false
131
+ end
132
+ return false
133
+ end
134
+
135
+ def print_grid(values)
136
+ return if values == false
137
+ max = 0
138
+ squares.each { |s| max = values[s].length if values[s].length > max }
139
+ width = 1 + max
140
+ a = Array.new
141
+ 3.times do |c|
142
+ tmp = ""
143
+ (3*width).times do
144
+ tmp = tmp + '-'
145
+ end
146
+ tmp += "-" if c == 1
147
+ a.push(tmp)
148
+ end
149
+ line = "\n" + a.join('+')
150
+
151
+ tmp = ""
152
+ for r in rows
153
+ for c in cols
154
+ tmp = tmp + values[r+c].center(width)
155
+ if c == '3' or c == '6'
156
+ tmp = tmp + '| '
157
+ end
158
+ end
159
+ tmp = tmp + line if r == 'C' or r == 'F'
160
+ tmp = tmp + "\n"
161
+ end
162
+ puts tmp + "\n"
163
+ return values
164
+ end
165
+
166
+ def string_solution(values)
167
+ solution = ""
168
+ squares.each do |s|
169
+ solution += values[s]
170
+ end
171
+ return solution
172
+ end
173
+
174
+ def check_solution(solution)
175
+ values = Hash.new
176
+ for s,d in squares.zip(solution.split(''))
177
+ values[s] = d
178
+ end
179
+
180
+ unitlist.each do |u|
181
+ tmp = Hash.new
182
+ u.each do |s|
183
+ tmp[values[s]] = true
184
+ end
185
+ return false unless tmp.keys.length == 9
186
+ end
187
+ return true
188
+ end
189
+
190
+ end
191
+
192
+ ## Algorithm by Peter Norvig @ http://www.norvig.com/sudoku.html
193
+
194
+ ## More constraints:
195
+ ## http://www.scanraid.com/BasicStrategies.htm
196
+ ## http://www.krazydad.com/blog/2005/09/29/an-index-of-sudoku-strategies/
197
+ ## http://www2.warwick.ac.uk/fac/sci/moac/currentstudents/peter_cock/python/sudoku/
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Martin-Louis Bright on 2007-04-10.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'sudokusolver'
7
+
8
+ puzzle = "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......"
9
+ s = SudokuSolver.new
10
+ s.print_grid(s.search(s.parse_grid(puzzle)))
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Martin-Louis Bright on 2007-03-21.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'sudokusolver')
7
+ require 'test/unit'
8
+
9
+ class SudokuTest < Test::Unit::TestCase
10
+
11
+ def setup
12
+ @path = File.join(File.dirname(__FILE__), '..')
13
+ @easy = File.read(@path + '/test/easy_puzzles.txt').split("\n")
14
+ @hard = File.read(@path + '/test/top95.txt').split("\n")
15
+ @s = SudokuSolver.new
16
+ end
17
+
18
+ def test_empty_puzzle
19
+ e = "................................................................................."
20
+ sol = @s.string_solution(@s.search(@s.parse_grid(e)))
21
+ puts sol
22
+ assert(@s.check_solution(sol))
23
+ end
24
+
25
+ def test_sanity
26
+ puts "Easy puzzle (constraint satisfaction only): "
27
+ puts
28
+ @s.print_grid(@s.search(@s.parse_grid(@easy[0])))
29
+ puts "Hard puzzle (constraint satisfaction + search): "
30
+ puts
31
+ @s.print_grid(@s.search(@s.parse_grid(@hard[0])))
32
+ end
33
+
34
+ def test_easy
35
+ multiple(@easy)
36
+ end
37
+
38
+ def test_hard
39
+ multiple(@hard)
40
+ end
41
+
42
+ def test_bmark_hard
43
+ @hard.each do |g|
44
+ puts @s.string_solution(@s.search(@s.parse_grid(g)))
45
+ end
46
+ end
47
+
48
+ def multiple(puzzles)
49
+ puzzles.each do |g|
50
+ assert(@s.check_solution(@s.string_solution(@s.search(@s.parse_grid(g)))))
51
+ end
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: sudokusolver
5
+ version: !ruby/object:Gem::Version
6
+ version: "1.0"
7
+ date: 2007-04-15 00:00:00 -04:00
8
+ summary: Commandline program and library for solving Sudoku puzzles
9
+ require_paths:
10
+ - lib
11
+ email: mlbright@gmail.com
12
+ homepage: http://sudokusolver.rubyforge.org/
13
+ rubyforge_project: sudokusolver
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Martin-Louis Bright
31
+ files:
32
+ - lib/sudokusolver.rb
33
+ - test/driver.rb
34
+ - test/tc_sudoku.rb
35
+ - Changes.rdoc
36
+ - README.rdoc
37
+ - Rakefile
38
+ - COPYING
39
+ - LICENSE
40
+ test_files:
41
+ - test/tc_sudoku.rb
42
+ rdoc_options:
43
+ - --title
44
+ - SudokuSolver API documentation
45
+ - --main
46
+ - README.rdoc
47
+ extra_rdoc_files:
48
+ - README.rdoc
49
+ - Changes.rdoc
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ requirements: []
55
+
56
+ dependencies: []
57
+