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 CHANGED
@@ -20,7 +20,7 @@ Or install it yourself as:
20
20
 
21
21
  Usage class NSudoku for example:
22
22
 
23
- EXAMPLE = [
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(EXAMPLE).solve #=> "712460850394852671685170420070208596926715384050906712540320968269584137030690245"
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(EXAMPLE).repeat_in #=> nil
43
-
43
+ > NSudoku::Checker.new(SUDOKU).repeat_in #=> nil
@@ -6,6 +6,12 @@ class NSudoku
6
6
  @sudoku = sudoku
7
7
  @width = Math.sqrt(@sudoku.length).to_i
8
8
  @block_width = Math.sqrt(@width).to_i
9
+ # puts @block_width
10
+ end
11
+
12
+ def correct?
13
+ return true if repeat_in.nil?
14
+ false
9
15
  end
10
16
 
11
17
  def repeat_in
@@ -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
@@ -1,3 +1,3 @@
1
1
  class NSudoku
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.6"
3
3
  end
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(data)
7
- @revers_table = create_revers_table(data.split(""))
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-10-09"
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"
@@ -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.2
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-10-09 00:00:00.000000000 Z
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