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.
- checksums.yaml +5 -5
- data/lib/sudoku_wizard.rb +106 -19
- metadata +3 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2163e26041c92743bf1f60d8ee716aa538187de8118d1595f08d2d18923c2197
|
|
4
|
+
data.tar.gz: 4ece90b2e7af5b7198f839ea9060be89af8a04a89ce5771517097a20ffb51e6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 >
|
|
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
|
|
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
|
|
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.
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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
|
|
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 <
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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-
|
|
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
|
-
|
|
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
|