nsudoku 0.1.2 → 0.1.6
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/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
|