sudoku_wizard 1.0.2 → 1.1.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.
Files changed (3) hide show
  1. checksums.yaml +5 -5
  2. data/lib/sudoku_wizard.rb +106 -19
  3. metadata +3 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ea6dbb8a08374d88e71ff0fd4df196405aed9796
4
- data.tar.gz: e36e4a7bcd2df738efde63028f8ee73d5cb5f4e9
2
+ SHA256:
3
+ metadata.gz: 2163e26041c92743bf1f60d8ee716aa538187de8118d1595f08d2d18923c2197
4
+ data.tar.gz: 4ece90b2e7af5b7198f839ea9060be89af8a04a89ce5771517097a20ffb51e6b
5
5
  SHA512:
6
- metadata.gz: a95ca5590169c33fe40b0932b790d74bf435f1e260f87924a1afb978e2e99a6727f18d0daf0ebf641390d79dd4898527a3bdb4624a8a01cdbab2752f2a4bb6f3
7
- data.tar.gz: 15aaa232a7b86c8821ecaabd197937df6aadb43aa03c9bfe1e595ba1ca75493bc0686616092c25949a967b0041b48c06fbc51a7cfae6c5c805558716764957b5
6
+ metadata.gz: 4949821c3b59cfcf655f7976c6a6839ab06b03d26e898979d7cbd8f55c27b49bae521d98eab6e7c2e805a9db9cc691de5eb3e2050da2879d9b1c457838928306
7
+ data.tar.gz: be87b9700ce51e10b95900a572713cc2794474296c0690270478b3d0261717c56d7ad0fe524f8feddc0acbd8992c17da38bee4fdc40c828abac24594f7deb7df
data/lib/sudoku_wizard.rb CHANGED
@@ -17,11 +17,11 @@ class Sudoku
17
17
 
18
18
  def initialize( holes = 40, status_messages = false )
19
19
  start_time = Time.now
20
- holes > 64 ? 64 : holes
20
+ self.difficulty = holes > 54 ? 54 : holes
21
21
  @status_messages = status_messages
22
22
 
23
23
  puts "Generating Game..." if @status_messages
24
- generate_game(holes)
24
+ generate_game
25
25
 
26
26
  return if !@status_messages
27
27
  puts "Board Generated in"
@@ -29,16 +29,17 @@ class Sudoku
29
29
  puts " #{ Time.now - start_time } seconds"
30
30
  end
31
31
 
32
- def generate_game(holes)
32
+ def generate_game
33
33
  begin
34
34
  # @start_time = Time.now
35
35
  @iteration_counter = 0
36
+ @poke_counter = 0
36
37
  self.solution = new_solved_board
37
- self.removed_values, self.starting_board = poke_holes(self.solution.map(&:clone), holes)
38
- self.difficulty = holes
39
- rescue
38
+ self.starting_board = poke_holes(self.solution.map(&:clone))
39
+ rescue => error
40
+ puts error
40
41
  puts "#{ format_number(@iteration_counter) } iterations, Restarting" if @status_messages
41
- generate_game(holes)
42
+ generate_game
42
43
  end
43
44
  end
44
45
 
@@ -55,7 +56,7 @@ class Sudoku
55
56
  # Fill in the empty cell
56
57
  for num in (1..9).to_a.shuffle do
57
58
  @iteration_counter += 1
58
- raise if (@iteration_counter > 1_000_000)
59
+ raise "too make buil iterations" if (@iteration_counter > 1_000_000)
59
60
  if safe(puzzle_matrix, empty_cell, num) # For a number, check if it safe to place that number in the empty cell
60
61
  puzzle_matrix[empty_cell[:row_i]][empty_cell[:col_i]] = num # if safe, place number
61
62
  return puzzle_matrix if solve(puzzle_matrix) # Recursively call solve method again.
@@ -105,24 +106,26 @@ class Sudoku
105
106
  end
106
107
 
107
108
 
108
- def poke_holes(puzzle_matrix, holes)
109
- removed_values = []
109
+ def poke_holes(puzzle_matrix)
110
+ self.removed_values = []
111
+ val = (0...81).to_a.shuffle
110
112
 
111
- while removed_values.length < holes
112
- row_i = (0..8).to_a.sample
113
- col_i = (0..8).to_a.sample
113
+ while self.removed_values.length < self.difficulty
114
+ next_val = val.pop
115
+ raise "impossible game" if !next_val
114
116
 
117
+ row_i = next_val / 9
118
+ col_i = next_val % 9
119
+
115
120
  next if (puzzle_matrix[row_i][col_i] == 0)
116
- removed_values.push({row_i: row_i, col_i: col_i, val: puzzle_matrix[row_i][col_i] })
121
+ self.removed_values.push({row_i: row_i, col_i: col_i, val: puzzle_matrix[row_i][col_i] })
117
122
  puzzle_matrix[row_i][col_i] = 0
118
-
123
+
119
124
  proposed_board = puzzle_matrix.map(&:clone)
120
- if !solve( proposed_board )
121
- puzzle_matrix[row_i][col_i] = removed_values.pop[:val]
122
- end
125
+ puzzle_matrix[row_i][col_i] = self.removed_values.pop[:val] if multiple_solutions?( proposed_board )
123
126
  end
124
127
 
125
- [removed_values, puzzle_matrix]
128
+ puzzle_matrix
126
129
  end
127
130
 
128
131
  def render(board_name)
@@ -151,6 +154,63 @@ class Sudoku
151
154
  end
152
155
 
153
156
  private
157
+ def bring_index_to_front(index, array)
158
+ starting_point = array.slice(index)
159
+ array.delete_at(index)
160
+ array.unshift(starting_point)
161
+ end
162
+
163
+
164
+ ###############
165
+ def multiple_solutions?(board_to_check)
166
+ possible_solutions = []
167
+ empty_cell_array = empty_cell_coords(board_to_check)
168
+ empty_cell_array.each_with_index do |coord, index|
169
+ board_clone = board_to_check.map{|row| row.map{|col| col}}
170
+ empty_cell_array_clone = empty_cell_array.map{|coord| coord}
171
+ bring_index_to_front(index, empty_cell_array_clone)
172
+ this_solution = fill_from_array( board_clone, empty_cell_array_clone )
173
+ possible_solutions.push(this_solution)
174
+
175
+ return true if possible_solutions.uniq.length > 1
176
+ end
177
+ return false
178
+ end
179
+
180
+ def fill_from_array(board, empty_cell_array)
181
+ empty_cell = next_still_empty_cell(board, empty_cell_array)
182
+ return board if !empty_cell
183
+ for num in (1..9).to_a.shuffle do
184
+ @poke_counter += 1
185
+ raise "too many pokes: #{@poke_counter}, #{board}, removed: #{@removed_values.length}" if (@poke_counter > 10_000_000)
186
+ if safe(board, empty_cell, num)
187
+ board[empty_cell[:row_i]][empty_cell[:col_i]] = num
188
+ return board if fill_from_array(board, empty_cell_array)
189
+ board[empty_cell[:row_i]][empty_cell[:col_i]] = 0
190
+ end
191
+ end
192
+ false
193
+ end
194
+
195
+ def next_still_empty_cell(board, empty_cell_array)
196
+ empty_cell_array.each do |coords|
197
+ next if board[ coords[:row] ][ coords[:col] ] != 0
198
+ return {row_i: coords[:row], col_i: coords[:col] }
199
+ end
200
+ false
201
+ end
202
+
203
+ def empty_cell_coords(board)
204
+ (0..8).to_a.each_with_object( [] ) do |row, empty_cells|
205
+ (0..8).to_a.each do |col|
206
+ next if board[row][col] != 0
207
+ empty_cells << {row:row, col:col}
208
+ end
209
+ end
210
+ end
211
+
212
+ #################
213
+
154
214
 
155
215
  def format_number(integer)
156
216
  number_of_digits = Math.log(integer, 10).floor + 1
@@ -169,5 +229,32 @@ class Sudoku
169
229
 
170
230
  end
171
231
 
232
+ # SOLVED_BOARD = [
233
+ # [2, 8, 4, 7, 9, 1, 5, 3, 6],
234
+ # [7, 3, 6, 5, 8, 2, 4, 9, 1],
235
+ # [9, 1, 5, 3, 4, 6, 2, 7, 8],
236
+ # [5, 4, 1, 2, 7, 3, 8, 6, 9],
237
+ # [8, 6, 7, 9, 5, 4, 3, 1, 2],
238
+ # [3, 9, 2, 1, 6, 8, 7, 5, 4],
239
+ # [1, 7, 3, 4, 2, 9, 6, 8, 5],
240
+ # [6, 2, 9, 8, 3, 5, 1, 4, 7],
241
+ # [4, 5, 8, 6, 1, 7, 9, 2, 3]
242
+ # ]
243
+
244
+ # MULTIPLE_SOLUTION = [
245
+ # [0,1,7,5,6,0,0,0,8],
246
+ # [0,5,0,0,0,1,3,0,7],
247
+ # [0,9,2,0,0,0,6,0,1],
248
+ # [0,7,0,2,3,0,8,4,0],
249
+ # [4,3,0,6,1,0,0,7,9],
250
+ # [0,6,8,0,0,7,0,3,0],
251
+ # [7,0,0,1,5,0,9,8,4],
252
+ # [0,0,1,7,0,3,5,0,0],
253
+ # [0,0,6,0,2,0,7,0,0]
254
+ # ]
255
+
256
+
257
+
258
+
172
259
  # binding.pry
173
260
  # false
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sudoku_wizard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Sasse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-18 00:00:00.000000000 Z
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Uses backtracking algorithm to generate random boards of specified difficulty
14
14
  and find solutions
@@ -37,8 +37,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
37
37
  - !ruby/object:Gem::Version
38
38
  version: '0'
39
39
  requirements: []
40
- rubyforge_project:
41
- rubygems_version: 2.6.1
40
+ rubygems_version: 3.0.9
42
41
  signing_key:
43
42
  specification_version: 4
44
43
  summary: Sudoku board generator & solver