nsudoku 0.1.2 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +4 -4
- data/lib/nsudoku/checker.rb +6 -0
- data/lib/nsudoku/logic.rb +187 -0
- data/lib/nsudoku/version.rb +1 -1
- data/lib/nsudoku.rb +36 -7
- data/nsudoku.gemspec +1 -1
- data/spec/logic_spec.rb +85 -0
- data/spec/nsudoku_spec.rb +10 -0
- metadata +5 -2
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
Usage class NSudoku for example:
|
22
22
|
|
23
|
-
|
23
|
+
SUDOKU = [
|
24
24
|
"012000000",
|
25
25
|
"304052000",
|
26
26
|
"605170000",
|
@@ -34,10 +34,10 @@ Usage class NSudoku for example:
|
|
34
34
|
Solve game the sudoku
|
35
35
|
|
36
36
|
> require 'nsudoku'
|
37
|
-
> NSudoku.new(
|
37
|
+
> NSudoku.new(SUDOKU).solve #=> "712460850394852671685170420070208596926715384050906712540320968269584137030690245"
|
38
|
+
> NSudoku.new(SUDOKU).solution #=> "712463859394852671685179423173248596926715384458936712541327968269584137837691245"
|
38
39
|
|
39
40
|
Check whether your array has got that same values in rows, columns or sub blocks. When your array is correct you should expect result nil.
|
40
41
|
In other situation you should received array of position with that same value.
|
41
42
|
|
42
|
-
> NSudoku::Checker.new(
|
43
|
-
|
43
|
+
> NSudoku::Checker.new(SUDOKU).repeat_in #=> nil
|
data/lib/nsudoku/checker.rb
CHANGED
@@ -0,0 +1,187 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class NSudoku
|
4
|
+
class Logic
|
5
|
+
|
6
|
+
attr_accessor :revers
|
7
|
+
|
8
|
+
def initialize(sudoku)
|
9
|
+
@sudoku = sudoku
|
10
|
+
@width = Math.sqrt(sudoku.length).to_i
|
11
|
+
@block_width = Math.sqrt(@width).to_i
|
12
|
+
@revers = create_revers(@sudoku.split(""))
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_revers(data)
|
16
|
+
@revers, index = raw_revers, 0
|
17
|
+
@revers.each_with_index do |row, row_index|
|
18
|
+
row.each_with_index do |col, col_index|
|
19
|
+
cell = data[index]
|
20
|
+
@revers[row_index][col_index] = [cell.to_i] if cell =~ /^[1-9]$/
|
21
|
+
index += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# revers table @revers_table into string where cell [n] return n and [a1, a2, ..., ak] return 0
|
27
|
+
def sudoku
|
28
|
+
result = ""
|
29
|
+
@revers.flatten(1).each do |element|
|
30
|
+
result << (element.size == 1 ? element.first.to_s : "0")
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
# change positions: external position of block for sudoku and
|
36
|
+
# internal position of block into array [row_position, column_position]
|
37
|
+
def block_position(external, internal)
|
38
|
+
[
|
39
|
+
(external / @block_width) * @block_width + internal / @block_width,
|
40
|
+
(external % @block_width) * @block_width + internal % @block_width
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
def block_positions_values(external)
|
45
|
+
result = {}
|
46
|
+
@width.times do |internal|
|
47
|
+
row, column = block_position(external, internal)
|
48
|
+
@revers[row][column].each { |value| (result[value] ||= []) << [row, column] }
|
49
|
+
end
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
def row_positions_values(row)
|
54
|
+
result = {}
|
55
|
+
@width.times do |column|
|
56
|
+
@revers[row][column].each { |value| (result[value] ||= []) << column }
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def column_positions_values(column)
|
62
|
+
result = {}
|
63
|
+
@width.times do |row|
|
64
|
+
@revers[row][column].each { |value| (result[value] ||= []) << row }
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
# erase values in vertical, which are that same like value from positnio row, column
|
70
|
+
def one_element_vertical(column)
|
71
|
+
column_positions_values(column).each do |value, positions|
|
72
|
+
@revers[positions.first][column] = [value] if positions.size == 1
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# erase values in horizontal, which are that same like value from positnio row, column
|
78
|
+
def one_element_horizontal(row)
|
79
|
+
row_positions_values(row).each do |value, positions|
|
80
|
+
@revers[row][positions.first] = [value] if positions.size == 1
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# erase values in block, which are that same like value from positnio row, column
|
86
|
+
def one_element_block(row)
|
87
|
+
block_positions_values(row).each do |value, positions|
|
88
|
+
horizontal = positions[0][0]
|
89
|
+
vertical = positions[0][1]
|
90
|
+
@revers[horizontal][vertical] = [value] if positions.size == 1
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# erase values in block, which are that same like value from positnio row, column
|
96
|
+
def two_elements_block(index)
|
97
|
+
result = {}
|
98
|
+
only_two_positions = block_positions_values(index).select {|k,v| v.length == 2}
|
99
|
+
only_two_positions.each { |value, positions| (result[positions] ||= []) << value }
|
100
|
+
result.each do |positions, value|
|
101
|
+
next unless value.size == 2
|
102
|
+
@revers[positions[0][0]][positions[0][1]] = value.clone
|
103
|
+
@revers[positions[1][0]][positions[1][1]] = value.clone
|
104
|
+
end
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# erase values in vertical, which are that same like value from positnio row, column
|
109
|
+
def two_elements_column(column)
|
110
|
+
result = {}
|
111
|
+
only_two_positions = row_positions_values(column).select {|k,v| v.length == 2}
|
112
|
+
only_two_positions.each { |value, positions| (result[positions] ||= []) << value }
|
113
|
+
result.each do |positions, value|
|
114
|
+
next unless value.size == 2
|
115
|
+
@revers[positions[0]][column] = value.clone
|
116
|
+
@revers[positions[1]][column] = value.clone
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
# erase values in row, which are that same like value from positnio row, column
|
122
|
+
def two_elements_row(row)
|
123
|
+
result = {}
|
124
|
+
only_two_positions = column_positions_values(row).select {|k,v| v.length == 2}
|
125
|
+
only_two_positions.each { |value, positions| (result[positions] ||= []) << value }
|
126
|
+
result.each do |positions, value|
|
127
|
+
next unless value.size == 2
|
128
|
+
@revers[row][positions[0]] = value.clone
|
129
|
+
@revers[row][positions[1]] = value.clone
|
130
|
+
end
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
# erase values in vertical, which are that same like value from positnio row, column
|
135
|
+
def erase_elements_row(row, column)
|
136
|
+
return unless @revers[row][column].size == 1
|
137
|
+
cell = @revers[row][column][0]
|
138
|
+
@width.times do |index|
|
139
|
+
@revers[index][column].delete(cell) unless index == row
|
140
|
+
end
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# erase values in hozizontal, which are that same like value from positnio row, column
|
145
|
+
def erase_elements_column(row, column)
|
146
|
+
return unless @revers[row][column].size == 1
|
147
|
+
cell = @revers[row][column][0]
|
148
|
+
@width.times do |index|
|
149
|
+
@revers[row][index].delete(cell) unless index == column
|
150
|
+
end
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
# erase values in block, which are that same like value from positnio row, column
|
155
|
+
def erase_elements_block(row, column)
|
156
|
+
return unless @revers[row][column].size == 1
|
157
|
+
cell = @revers[row][column][0]
|
158
|
+
block_positions(row, column).each do |horizontal, vertical|
|
159
|
+
@revers[horizontal][vertical].delete(cell) unless horizontal == row and vertical == column
|
160
|
+
end
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
def block_positions(row, column)
|
165
|
+
row_block = (row / @block_width) * @block_width
|
166
|
+
column_block = (column / @block_width) * @block_width
|
167
|
+
(0...@width).to_a.map { |index| [row_block + index / @block_width, column_block + index % @block_width] }
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
# @width = 4
|
173
|
+
# cell = [1, 2, 3, 4]
|
174
|
+
# return [[cell, cell, cell, cell],
|
175
|
+
# [cell, cell, cell, cell],
|
176
|
+
# [cell, cell, cell, cell],
|
177
|
+
# [cell, cell, cell, cell]]
|
178
|
+
def raw_revers
|
179
|
+
(1..@width).to_a.map do
|
180
|
+
(1..@width).to_a.map do
|
181
|
+
(1..@width).to_a
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
data/lib/nsudoku/version.rb
CHANGED
data/lib/nsudoku.rb
CHANGED
@@ -1,12 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require_relative "nsudoku/version"
|
2
3
|
require_relative "nsudoku/checker"
|
4
|
+
require_relative "nsudoku/logic"
|
3
5
|
|
4
6
|
class NSudoku
|
5
7
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
+
def initialize(sudoku)
|
9
|
+
@data = sudoku
|
10
|
+
@sudoku = sudoku
|
11
|
+
@revers_table = create_revers_table(sudoku.split(""))
|
12
|
+
@length = @sudoku.length
|
13
|
+
@width = Math.sqrt(@sudoku.length).to_i
|
14
|
+
@block_width = Math.sqrt(@width).to_i
|
8
15
|
end
|
9
16
|
|
17
|
+
# return first correct solution for variable @sudoku
|
18
|
+
def solution
|
19
|
+
@solution = nil
|
20
|
+
data = @sudoku.clone + "0"
|
21
|
+
solver(data, -1, "0")
|
22
|
+
@solution[0, @length] if @solution
|
23
|
+
end
|
24
|
+
|
25
|
+
# return result which match to all solutions of sudoku
|
10
26
|
def solve
|
11
27
|
revers_table = nil
|
12
28
|
while not revers_table == @revers_table
|
@@ -31,13 +47,26 @@ class NSudoku
|
|
31
47
|
revers_table_to_result
|
32
48
|
end
|
33
49
|
|
34
|
-
def show_table_pretty
|
35
|
-
@revers_table.each do |cell|
|
36
|
-
puts cell.inspect
|
37
|
-
end
|
38
|
-
end
|
39
50
|
private
|
40
51
|
|
52
|
+
# Recursive solving sudoku game
|
53
|
+
#
|
54
|
+
# solver(data, -1, "0")
|
55
|
+
def solver(data, position, value)
|
56
|
+
return if @solution
|
57
|
+
data[position] = value.to_s
|
58
|
+
return unless NSudoku::Checker.new(data).correct?
|
59
|
+
@solution = data.clone if position == @length - 1
|
60
|
+
if @data[position + 1] == "0"
|
61
|
+
(1..@width).each do |value|
|
62
|
+
solver(data, position + 1, value)
|
63
|
+
data[position + 1] = "0"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
solver(data, position + 1, @data[position + 1])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
41
70
|
# table = [[[1, 2, 3, 4, 5], [], ..., []],
|
42
71
|
# [[], ..., []],
|
43
72
|
# ...
|
data/nsudoku.gemspec
CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
|
|
5
5
|
gem.platform = Gem::Platform::RUBY
|
6
6
|
gem.authors = ["Michał Szyma"]
|
7
7
|
gem.email = ["raglub.ruby@gmail.com"]
|
8
|
-
gem.date = "2012-
|
8
|
+
gem.date = "2012-11-13"
|
9
9
|
gem.description = %q{This gem solve puzzle game sudoku 9x9}
|
10
10
|
gem.summary = %q{This gem solve puzzle game sudoku}
|
11
11
|
gem.homepage = "http://github.com/raglub/nsudoku"
|
data/spec/logic_spec.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'nsudoku'
|
4
|
+
|
5
|
+
EXAMPLE = [
|
6
|
+
"1004",
|
7
|
+
"0201",
|
8
|
+
"0143",
|
9
|
+
"2010"].join
|
10
|
+
|
11
|
+
# REVERS = [
|
12
|
+
# [[1], [1, 2, 3, 4], [1, 2, 3, 4], [4]],
|
13
|
+
# [[1, 2, 3, 4], [2], [1, 2, 3, 4], [1]],
|
14
|
+
# [[1, 2, 3, 4], [1], [4], [3]],
|
15
|
+
# [[2], [1, 2, 3, 4], [1], [1, 2, 3, 4]]
|
16
|
+
# ]
|
17
|
+
|
18
|
+
REVERS = [
|
19
|
+
[[3], [1, 2, 3, 4], [1, 2, 4], [4]],
|
20
|
+
[[1, 2, 4], [2], [1, 2, 3, 4], [1]],
|
21
|
+
[[1, 2, 3], [1, 2, 4], [4], [3]],
|
22
|
+
[[4], [3, 4], [1], [1, 2, 3, 4]]
|
23
|
+
]
|
24
|
+
|
25
|
+
describe NSudoku do
|
26
|
+
|
27
|
+
before do
|
28
|
+
@logic = NSudoku::Logic.new(EXAMPLE)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can generate reverse of sudoku" do
|
32
|
+
@logic.revers[0][1].should eql([1, 2, 3, 4])
|
33
|
+
@logic.revers[3][3].inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can get result" do
|
37
|
+
@logic.sudoku.should eql(EXAMPLE)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can erase that same elements in column" do
|
41
|
+
@logic.erase_elements_column(0, 0).revers[0][1].should eql([2, 3, 4])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can erase that same elements in row" do
|
45
|
+
@logic.erase_elements_row(2, 2).revers[1][2].should eql([1, 2, 3])
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can erase that same elements in block" do
|
49
|
+
@logic.erase_elements_block(2, 2).revers[3][3].should eql([1, 2, 3])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can choos only one value in elements for vertical situation" do
|
53
|
+
@logic.revers = REVERS
|
54
|
+
@logic.one_element_vertical(2).revers[1][2].should eql([3])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "can choos only one value in elements for horizontal situation" do
|
58
|
+
@logic.revers = REVERS
|
59
|
+
@logic.one_element_horizontal(2).revers[1][2].should eql([3])
|
60
|
+
end
|
61
|
+
|
62
|
+
it "can choos only one value in elements for block situation" do
|
63
|
+
@logic.revers = REVERS
|
64
|
+
@logic.one_element_block(2).revers[1][2].should eql([3])
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can choos only two values in elements for block situation" do
|
68
|
+
@logic.revers = REVERS
|
69
|
+
@logic.two_elements_block(2).revers[2][0].should eql([1, 2])
|
70
|
+
@logic.revers[2][1].should eql([1, 2])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "can choos only two values in elements for vertical situation" do
|
74
|
+
@logic.revers = REVERS
|
75
|
+
@logic.two_elements_column(0).revers[1][0].should eql([1, 2])
|
76
|
+
@logic.revers[2][0].should eql([1, 2])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can choos only two values in elements for horizontal situation" do
|
80
|
+
@logic.revers = REVERS
|
81
|
+
@logic.two_elements_row(2).revers[2][0].should eql([1, 2])
|
82
|
+
@logic.revers[2][1].should eql([1, 2])
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/spec/nsudoku_spec.rb
CHANGED
@@ -24,8 +24,18 @@ TABLE_EMPTY = [
|
|
24
24
|
"000000000",
|
25
25
|
"000000000"].join
|
26
26
|
|
27
|
+
SUDOKU44 = [
|
28
|
+
"1030",
|
29
|
+
"4000",
|
30
|
+
"0140",
|
31
|
+
"3001"].join
|
32
|
+
|
27
33
|
describe NSudoku do
|
28
34
|
|
35
|
+
it "should properly resolved game sudoku" do
|
36
|
+
NSudoku.new(SUDOKU44).solution.should eq("1234431221433421")
|
37
|
+
end
|
38
|
+
|
29
39
|
it "should properly reverse base namber of table" do
|
30
40
|
sudoku = NSudoku.new(TABLE_EMPTY)
|
31
41
|
sudoku.send(:cell_revers_table, 1, 1, [1])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nsudoku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -42,10 +42,12 @@ files:
|
|
42
42
|
- Rakefile
|
43
43
|
- lib/nsudoku.rb
|
44
44
|
- lib/nsudoku/checker.rb
|
45
|
+
- lib/nsudoku/logic.rb
|
45
46
|
- lib/nsudoku/version.rb
|
46
47
|
- lib/sudoku.rb~
|
47
48
|
- nsudoku.gemspec
|
48
49
|
- spec/checker_spec.rb
|
50
|
+
- spec/logic_spec.rb
|
49
51
|
- spec/nsudoku_spec.rb
|
50
52
|
homepage: http://github.com/raglub/nsudoku
|
51
53
|
licenses: []
|
@@ -73,4 +75,5 @@ specification_version: 3
|
|
73
75
|
summary: This gem solve puzzle game sudoku
|
74
76
|
test_files:
|
75
77
|
- spec/checker_spec.rb
|
78
|
+
- spec/logic_spec.rb
|
76
79
|
- spec/nsudoku_spec.rb
|