sudokude 1.0.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/lib/sudokude.rb +141 -0
- metadata +62 -0
data/lib/sudokude.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
module Sudokude
|
2
|
+
class Sudoku
|
3
|
+
def initialize(sudoku)
|
4
|
+
raise "This is not the proper size" if sudoku.map {|e| e.size } != [9,9,9,9,9,9,9,9,9]
|
5
|
+
@sudoku = {}
|
6
|
+
j = 0
|
7
|
+
|
8
|
+
# This code creates a hash table with key indexes for each value.
|
9
|
+
# The key is made up of three digits and shows the exact location
|
10
|
+
# of the number.
|
11
|
+
sudoku.each do |row|
|
12
|
+
9.times do |i|
|
13
|
+
if i <= 2 && j <= 2
|
14
|
+
b = 0
|
15
|
+
elsif i <= 5 && j <= 2
|
16
|
+
b = 1
|
17
|
+
elsif i > 5 && j <= 2
|
18
|
+
b = 2
|
19
|
+
elsif i <= 2 && j <= 5
|
20
|
+
b = 3
|
21
|
+
elsif i <= 5 && j <= 5
|
22
|
+
b = 4
|
23
|
+
elsif i > 5 && j <= 5
|
24
|
+
b = 5
|
25
|
+
elsif i <= 2 && j > 5
|
26
|
+
b = 6
|
27
|
+
elsif i <= 5 && j > 5
|
28
|
+
b = 7
|
29
|
+
else
|
30
|
+
b = 8
|
31
|
+
end
|
32
|
+
@sudoku[i.to_s + j.to_s + b.to_s] = row[i]
|
33
|
+
end
|
34
|
+
j += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def print
|
39
|
+
@sudoku.values.each_slice(9).to_a.each do |row|
|
40
|
+
row.map! do |e|
|
41
|
+
if e.nil?
|
42
|
+
"n"
|
43
|
+
else
|
44
|
+
e
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts row.join(" ")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def solve!
|
52
|
+
unsolved = {}
|
53
|
+
@keysizes = []
|
54
|
+
@sudoku.each do |key, value|
|
55
|
+
if value.nil?
|
56
|
+
unsolved[key] = [1,2,3,4,5,6,7,8,9]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
until solved
|
61
|
+
|
62
|
+
# Simple elimination method
|
63
|
+
unsolved.each do |key, value|
|
64
|
+
value.reject! do |number|
|
65
|
+
(@sudoku.row(key[1].to_i) + @sudoku.column(key[0].to_i) + @sudoku.box(key[2].to_i)).include?(number)
|
66
|
+
end
|
67
|
+
@sudoku[key] = value[0] if value.size == 1
|
68
|
+
unsolved.delete_if { |key, value| value.empty? }
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# Naked multiples method
|
73
|
+
unsolved.each do |key, value|
|
74
|
+
if unsolved.row(key[1].to_i).has_naked_multiple?(value)
|
75
|
+
unsolved.each do |nkey, nvalue|
|
76
|
+
nvalue.reject! { |n| value.include?(n) } if (!(nvalue-value).empty? && key[1] == nkey[1])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if unsolved.column(key[0].to_i).has_naked_multiple?(value)
|
80
|
+
unsolved.each do |nkey, nvalue|
|
81
|
+
nvalue.reject! { |n| value.include?(n) } if (!(nvalue-value).empty? && key[0] == nkey[0])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
if unsolved.box(key[2].to_i).has_naked_multiple?(value)
|
85
|
+
unsolved.each do |nkey, nvalue|
|
86
|
+
nvalue.reject! { |n| value.include?(n) } if (!(nvalue-value).empty? && key[2] == nkey[2])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
@sudoku[key] = value[0] if value.size == 1
|
91
|
+
unsolved.delete_if { |key, value| value.empty? }
|
92
|
+
end
|
93
|
+
|
94
|
+
if unsolvable
|
95
|
+
puts "Impossible to solve. Best solution (n if no solution):"
|
96
|
+
break
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
print
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Array#compact removes all nils. A solved puzzle has no nils to remove.
|
107
|
+
def solved
|
108
|
+
@sudoku.values.compact.size == 81
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# This evaluates the number of solved keys in the puzzle. If no new blanks have been
|
113
|
+
# filled in over multiple passes through the solving methods, then the puzzle cannot
|
114
|
+
# be solved.
|
115
|
+
def unsolvable
|
116
|
+
@keysizes << @sudoku.values.compact.size
|
117
|
+
@keysizes[-1] == @keysizes[-3]
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
class Hash
|
123
|
+
def row(i)
|
124
|
+
self.select { |key, value| key[1].to_i == i }.values
|
125
|
+
end
|
126
|
+
|
127
|
+
def column(i)
|
128
|
+
self.select { |key, value| key[0].to_i == i }.values
|
129
|
+
end
|
130
|
+
|
131
|
+
def box(i)
|
132
|
+
self.select { |key, value| key[2].to_i == i }.values
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Array
|
137
|
+
def has_naked_multiple?(item)
|
138
|
+
self.select { |e| (e - item).empty? }.size == item.size
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sudokude
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Dan Chao
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2013-09-19 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: This program solves Sudoku puzzles in the form of nested arrays
|
22
|
+
email: daniel.h.chao@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/sudokude.rb
|
31
|
+
has_rdoc: true
|
32
|
+
homepage:
|
33
|
+
licenses: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.3.6
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: Solve sudoku puzzles
|
61
|
+
test_files: []
|
62
|
+
|