retoo-rudoku 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +7 -0
  2. data/lib/rudoku.rb +351 -0
  3. metadata +54 -0
@@ -0,0 +1,7 @@
1
+ = rudoku
2
+
3
+ Rudoku is a simple Sudoku solver.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Reto Schüttel
@@ -0,0 +1,351 @@
1
+ # Copyright (c) 2007-2009 by Reto Schüttel <reto (ät) schuettel (dut) ch>
2
+
3
+ # Please contact me if you like this released under a OSS license!
4
+
5
+ class Rudoku
6
+ NRS = { 1 => true, 2 => true, 3 => true,
7
+ 4 => true, 5 => true, 6 => true,
8
+ 7 => true, 8 => true, 9 => true, }
9
+
10
+ class Board
11
+ attr_reader :fields, :blocks, :rows, :cols
12
+
13
+ def initialize(board)
14
+ @fields = []
15
+ @board = []
16
+ @rows = []
17
+ @cols = []
18
+ @blocks = []
19
+ @missing = []
20
+ @stats = {
21
+ :missing => 0,
22
+ :solved_by_only_one_possible => 0,
23
+ :solved_by_no_other_possible => 0,
24
+ :solved_by_presolve => 0
25
+ }
26
+
27
+ # initialize rows, columns & blocks
28
+ 0.upto(8) do |i|
29
+ @rows[i] = Row.new(self, i)
30
+ @cols[i] = Col.new(self, i)
31
+ @blocks[i] = Block.new(self, i)
32
+ end
33
+
34
+ i = 0
35
+ board.each_with_index do |row, y|
36
+ @board[y] = []
37
+
38
+ row.each_with_index do |value, x|
39
+ f = Field.new(self, i, x, y)
40
+ f.value = value
41
+
42
+ @board[y][x] = f
43
+ @fields << f
44
+ @missing << f if f.missing?
45
+ i += 1
46
+ end
47
+ end
48
+
49
+ # initialize the helper constructs
50
+ @blocks.each do |block|
51
+ block.initialize_area()
52
+ end
53
+
54
+ @stats[:missing] = @missing.length
55
+
56
+ @counter = 0
57
+
58
+ raise "Invalid field" if not valid_field?
59
+ end
60
+
61
+
62
+ def pre_solve
63
+ begin
64
+ #puts "try"
65
+ changed = @missing.reject! do |f|
66
+ # create a list of all fields which are wmissing
67
+ # in the same block, not including t the current field f
68
+ nrs = f.available_nrs
69
+
70
+
71
+ #puts "#{f} hat #{nrs.length} möglichkeiten: #{nrs.join(" ")}" if nrs.length == 2
72
+ #f.mark if nrs.length ==2
73
+ if nrs.length == 1
74
+ @stats[:solved_by_only_one_possible] += 1
75
+ #f.mark
76
+ f.value = nrs.first
77
+ true
78
+ else
79
+ # use the scanning method for possible other solutions
80
+ # returns false or true
81
+
82
+ if v = f.nr_only_possible_in_this_field
83
+ # f.mark
84
+ @stats[:solved_by_no_other_possible] += 1
85
+ f.value = v
86
+ #f.mark
87
+ #return
88
+ true
89
+ else
90
+ false
91
+ end
92
+ end
93
+ end
94
+ end while changed
95
+
96
+ @stats[:solved_by_presolve] =
97
+ @stats[:solved_by_no_other_possible] +
98
+ @stats[:solved_by_only_one_possible]
99
+ end
100
+
101
+ # returns false if there's no possible next move, else return the valid path
102
+ def solve(level = 0)
103
+ fm = @missing.shift
104
+
105
+ if fm.nil?
106
+ return true
107
+ end
108
+
109
+ available_nrs = fm.available_nrs
110
+
111
+ unless available_nrs.empty?
112
+ # try all the available nrs on this field
113
+ available_nrs.each do |nr|
114
+
115
+ raise "nr is nil??" if nr.nil?
116
+ # set it
117
+ fm.value = nr
118
+
119
+ # and try to fill the next field
120
+ result = solve(level + 1)
121
+
122
+ # return true if we found a solution
123
+ return true if result
124
+ end
125
+ end
126
+
127
+ # seems like we are in a 'sackgasse', reset the value and
128
+ # go back to the next stepp
129
+ fm.value = nil
130
+ @missing.unshift(fm)
131
+
132
+ return false
133
+ end
134
+
135
+ def print_field(p = [])
136
+ p = [p] unless p.kind_of?(Array)
137
+
138
+ @board.each_with_index do |row, y|
139
+ #row.map{|f| f.value}.map{|f| f || "_"}.each_with_index do |f, x|
140
+ row.each_with_index do |f, x|
141
+ t = ( f.value ? f.value.to_s : "_" ) + ( f.marked? ? "<" : " " )
142
+
143
+ print "#{t} "
144
+
145
+ #if p.any?{|o| x == o.x && y == o.y}
146
+ # print "#{f}<"
147
+ #else
148
+ # print "#{f} "
149
+ #end
150
+ print " " if x % 3 == 2
151
+ end
152
+ puts
153
+ puts if y % 3 == 2
154
+ end
155
+
156
+ #puts "Lösung"
157
+ #0.upto(8) do |i|
158
+ # print "#{@board[i][i].value} "
159
+ #end
160
+ #puts
161
+
162
+ #puts
163
+ puts "Stats"
164
+ @stats.each do |key, value|
165
+ puts "#{key}: #{value}"
166
+ end
167
+
168
+ end
169
+
170
+ def valid_field?
171
+ true
172
+ end
173
+
174
+ def get(x, y)
175
+ @board[y][x]
176
+ end
177
+
178
+ def get_row(y)
179
+ @rows[y]
180
+ end
181
+
182
+ def get_col(x)
183
+ @cols[x]
184
+ end
185
+
186
+ def get_block(x, y)
187
+ block_nr = x / 3 + y/3*3
188
+ @blocks[block_nr] or raise "Unitialized block at #{x}/#{y} block_nr #{block_nr}"
189
+ end
190
+
191
+ def find_first_missing
192
+ @fields.find{|f| f.missing?}
193
+ end
194
+ end
195
+
196
+ class Field
197
+ attr_reader :index, :x, :y, :value, :block
198
+
199
+ def initialize(board, i, x, y)
200
+ @board = board
201
+ @index = i
202
+ @x = x
203
+ @y = y
204
+ @marked = false
205
+
206
+ @row = @board.get_row(y) or raise
207
+ @col = @board.get_col(x) or raise
208
+ @block = @board.get_block(x, y) or raise
209
+ end
210
+
211
+ def mark
212
+ @marked = true
213
+ end
214
+
215
+ def marked?
216
+ @marked
217
+ end
218
+
219
+ def value=(v)
220
+ unless value.nil?
221
+ add(value)
222
+ end
223
+
224
+ unless v.nil?
225
+ remove(v)
226
+ end
227
+ @value = v
228
+ end
229
+
230
+ def remove(v)
231
+ @block.remove(v)
232
+ @row.remove(v)
233
+ @col.remove(v)
234
+ end
235
+
236
+ def add(v)
237
+ @block.add(v)
238
+ @row.add(v)
239
+ @col.add(v)
240
+ end
241
+
242
+ def missing?
243
+ @value.nil?
244
+ end
245
+
246
+ def available_nrs
247
+ @row.available_nrs & @col.available_nrs & @block.available_nrs
248
+ end
249
+
250
+ def to_s
251
+ x.to_s + ":" + y.to_s
252
+ end
253
+
254
+ def nr_only_possible_in_this_field
255
+ neighbours = block.missing_fields.reject{|m| m == self}
256
+
257
+ available_nrs.each do |nr|
258
+ # iterate over all neighbours and check if theres a Nr (nr)
259
+ # which can't be anywere else
260
+ if neighbours.all?{|n| not n.available_nrs.include?(nr) }
261
+ # okay, nr isn't possible in all the other missing
262
+ # fields in the neighbourhood, that means it can only be
263
+ # on f
264
+ return nr
265
+ end
266
+ end
267
+
268
+ nil
269
+ end
270
+
271
+ end
272
+
273
+ class Area
274
+ attr_reader :board
275
+
276
+ def initialize(b, coord)
277
+ @available_nrs = NRS.dup
278
+ @board = b or raise "b not defined"
279
+ end
280
+
281
+ def available_nrs
282
+ @available_nrs.keys
283
+ end
284
+
285
+ def remove(v)
286
+ raise if v == true
287
+
288
+ @available_nrs.delete(v)
289
+ raise "bb" if v == true
290
+ end
291
+
292
+ def add(v)
293
+ @available_nrs[v] = true
294
+ end
295
+
296
+ def info(mode, v)
297
+ #puts "#{type} #{mode} #{v} available_nrs contains #{@available_nrs.join(", ")}"
298
+ end
299
+
300
+ # TODO: this could be cached/resued/
301
+ def missing_fields
302
+ fields.reject{|f| not f.missing?}
303
+ end
304
+ end
305
+
306
+ class Row < Area
307
+ def initialize(b, y)
308
+ @y = y
309
+ super
310
+ end
311
+
312
+ def type
313
+ "row #{@y}"
314
+ end
315
+ end
316
+
317
+ class Col < Area
318
+ def initialize(b, x)
319
+ @x = x
320
+ super
321
+ end
322
+
323
+ def type
324
+ "col #{@x}"
325
+ end
326
+ end
327
+
328
+ class Block < Area
329
+ attr_accessor :fields
330
+
331
+ def initialize(b, n)
332
+ @n = n
333
+ @x = n % 3
334
+ @y = n / 3
335
+ super
336
+ end
337
+
338
+ def initialize_area
339
+ f_x = @x * 3
340
+ f_y = @y * 3
341
+
342
+ @fields = []
343
+
344
+ f_y.upto(f_y + 2) do |y|
345
+ f_x.upto(f_x + 2) do |x|
346
+ @fields << board.get(x, y)
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: retoo-rudoku
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - "Reto Sch\xC3\xBCttel"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-23 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Rudoku is a simple Sudoku solver.
17
+ email: reto <hugh> at <yoh!> schuettel doto ch
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - lib/rudoku.rb
26
+ - README.rdoc
27
+ has_rdoc: true
28
+ homepage: http://github.com/retoo/rudoku
29
+ post_install_message:
30
+ rdoc_options:
31
+ - --charset=UTF-8
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: "0"
39
+ version:
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ requirements:
47
+ - none
48
+ rubyforge_project:
49
+ rubygems_version: 1.2.0
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Sudoku Engine.
53
+ test_files: []
54
+