ruby-minisat 1.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +24 -0
  2. data/LICENSE +21 -0
  3. data/README.rdoc +56 -0
  4. data/Rakefile +48 -0
  5. data/VERSION +1 -0
  6. data/examples/compat18.rb +65 -0
  7. data/examples/example.rb +26 -0
  8. data/examples/example2.rb +60 -0
  9. data/examples/kakuro.rb +178 -0
  10. data/examples/kakuro.sample +13 -0
  11. data/examples/lonely7.rb +302 -0
  12. data/examples/nonogram.rb +254 -0
  13. data/examples/nonogram.sample +26 -0
  14. data/examples/numberlink.rb +489 -0
  15. data/examples/numberlink.sample +11 -0
  16. data/examples/shikaku.rb +190 -0
  17. data/examples/shikaku.sample +11 -0
  18. data/examples/slitherlink.rb +279 -0
  19. data/examples/slitherlink.sample +11 -0
  20. data/examples/sudoku.rb +216 -0
  21. data/examples/sudoku.sample +11 -0
  22. data/ext/minisat/extconf.rb +9 -0
  23. data/ext/minisat/minisat-wrap.cpp +88 -0
  24. data/ext/minisat/minisat.c +497 -0
  25. data/ext/minisat/minisat.h +53 -0
  26. data/minisat/MiniSat_v1.14.2006-Aug-29.src.zip +0 -0
  27. data/minisat/MiniSat_v1.14/Global.h +274 -0
  28. data/minisat/MiniSat_v1.14/Heap.h +100 -0
  29. data/minisat/MiniSat_v1.14/LICENSE +20 -0
  30. data/minisat/MiniSat_v1.14/Main.C +244 -0
  31. data/minisat/MiniSat_v1.14/Makefile +88 -0
  32. data/minisat/MiniSat_v1.14/README +30 -0
  33. data/minisat/MiniSat_v1.14/Solver.C +781 -0
  34. data/minisat/MiniSat_v1.14/Solver.h +206 -0
  35. data/minisat/MiniSat_v1.14/Solver.o +0 -0
  36. data/minisat/MiniSat_v1.14/SolverTypes.h +130 -0
  37. data/minisat/MiniSat_v1.14/Sort.h +131 -0
  38. data/minisat/MiniSat_v1.14/TODO +73 -0
  39. data/minisat/MiniSat_v1.14/VarOrder.h +96 -0
  40. data/test/test_minisat.rb +143 -0
  41. metadata +114 -0
@@ -0,0 +1,26 @@
1
+ # quoted from wikipedia (http://en.wikipedia.org/wiki/Nonogram)
2
+ 1 1
3
+ 1 1 3 3
4
+ 3 2 1 3 2 3 5 1 2 3 2 2
5
+ 1 2 2 1 1 2 1 6 9 3 2 1 3 2 1 2 2
6
+ 2 2 3 3 1 1 2 3 4 1 2 2 7 2 4 2 1 2 1 1
7
+ 3 . . . . . . . . . . . . . . . . . . . .
8
+ 5 . . . . . . . . . . . . . . . . . . . .
9
+ 3 1 . . . . . . . . . . . . . . . . . . . .
10
+ 2 1 . . . . . . . . . . . . . . . . . . . .
11
+ 3 3 4 . . . . . . . . . . . . . . . . . . . .
12
+ 2 2 7 . . . . . . . . . . . . . . . . . . . .
13
+ 6 1 1 . . . . . . . . . . . . . . . . . . . .
14
+ 4 2 2 . . . . . . . . . . . . . . . . . . . .
15
+ 1 1 . . . . . . . . . . . . . . . . . . . .
16
+ 3 1 . . . . . . . . . . . . . . . . . . . .
17
+ 6 . . . . . . . . . . . . . . . . . . . .
18
+ 2 7 . . . . . . . . . . . . . . . . . . . .
19
+ 6 3 1 . . . . . . . . . . . . . . . . . . . .
20
+ 1 2 2 1 1 . . . . . . . . . . . . . . . . . . . .
21
+ 4 1 1 3 . . . . . . . . . . . . . . . . . . . .
22
+ 4 2 2 . . . . . . . . . . . . . . . . . . . .
23
+ 3 3 1 . . . . . . . . . . . . . . . . . . . .
24
+ 3 3 . . . . . . . . . . . . . . . . . . . .
25
+ 3 . . . . . . . . . . . . . . . . . . . .
26
+ 2 1 . . . . . . . . . . . . . . . . . . . .
@@ -0,0 +1,489 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- numberlink.rb
4
+ # ref: http://en.wikipedia.org/wiki/Number_Link
5
+
6
+ ##
7
+ ## SAT configuration:
8
+ ## - Variables:
9
+ ## - to each number cell:
10
+ ## - 4 variables for directions
11
+ ## - to each blank cell:
12
+ ## - 7 variables for patterns
13
+ ## - n variables for numbers
14
+ ## - the line of the cell links this numbers
15
+ ## - Clauses:
16
+ ## - exact one directions in each number cell
17
+ ## - exact one pattern in each blank cell
18
+ ## - zero linked numbers in each blank cell if the cell is blank
19
+ ## - exact one linked numbers if each blank cell if the cell has a line
20
+ ## - no blank pattern (if filled solution is required)
21
+ ## - neighbor blank cells are connected or disconnected
22
+ ## - line links continuously the two same numbers
23
+ ## - connectivity optimization (below)
24
+ ## - neighbor blank cells links the same number if they are connected
25
+ ## - patterns are false which makes line out of field
26
+ ## - blank cell in the direction of number cell connects the number cell
27
+ ## - blank cells not in the direction of number cell does not connects the
28
+ ## number cell
29
+ ## - corner optimization (below)
30
+ ##
31
+ ## patterns:
32
+ ## - 0 : vertical line
33
+ ## - 1 : orthogonal line (up and left)
34
+ ## - 2 : orthogonal line (up and right)
35
+ ## - 3 : orthogonal line (down and left)
36
+ ## - 4 : orthogonal line (down and right)
37
+ ## - 5 : horizontal line
38
+ ## - 6 : blank
39
+ ##
40
+ ## | | |
41
+ ## 0 --1 2-- --3 4-- --5--
42
+ ## | | |
43
+ ##
44
+ ##
45
+ ## basic connectivity:
46
+ ## [0, 3, 4]
47
+ ## |
48
+ ## [2, 4, 5]---+---[1, 3, 5]
49
+ ## |
50
+ ## [0, 1, 2]
51
+ ##
52
+ ##
53
+ ## connectivity optimization: prune U-turn situations
54
+ ## For example, left pattern is 2 and right one is 1.
55
+ ## In such situations, the shortcut exists:
56
+ ##
57
+ ## + + +---+
58
+ ## | | =>
59
+ ## 2---1 b b
60
+ ##
61
+ ##
62
+ ## extended horizontal connectivity:
63
+ ## l\r 1 3 5
64
+ ## 2 o o
65
+ ## 4 o o
66
+ ## 5 o o o
67
+ ##
68
+ ## extended veritcal connectivity:
69
+ ## u\d 0 1 2
70
+ ## 0 o o o
71
+ ## 3 o o
72
+ ## 4 o o
73
+ ##
74
+ ##
75
+ ## corner optimization: patterns 1 and 2 exists only `corner of number'
76
+ ## - pattern 1 can be only in the lower right-hand corner of number or 1
77
+ ## - pattern 2 can be only in the lower left-hand corner of number or 2
78
+ ##
79
+ ##
80
+ ## n + + n
81
+ ## | |
82
+ ## +---1 + + 2---+
83
+ ## | |
84
+ ## +---1 + + 2---+
85
+ ## | |
86
+ ## +---1 2---+
87
+ ##
88
+ ## directions:
89
+ ## - 0: up
90
+ ## - 1: down
91
+ ## - 2: left
92
+ ## - 3: right
93
+ ##
94
+
95
+
96
+
97
+ require "minisat"
98
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
99
+
100
+ $filled = true
101
+
102
+
103
+ def error(msg)
104
+ $stderr.puts msg
105
+ exit 1
106
+ end
107
+
108
+
109
+ def parse_file(file)
110
+ width = nil
111
+ field = []
112
+ File.read(file).split(/\n/).each do |line|
113
+ case line
114
+ when /^\s*#.*$/, /^\s*$/
115
+ else
116
+ line = line.split.map {|x| x[/^\d+$/] && x.to_i }
117
+ width ||= line.size
118
+ unless width == line.size
119
+ error "illegal width: row #{ field.size + 1 }"
120
+ end
121
+ field << line
122
+ end
123
+ end
124
+
125
+ h = {}
126
+ field.flatten.compact.sort.each_slice(2) do |x, y|
127
+ error "bad field" if x == 0 || x != y || h[x]
128
+ h[x] = true
129
+ end
130
+
131
+ field
132
+ end
133
+
134
+
135
+ def define_sat(solver, field)
136
+ w = field.first.size
137
+ h = field.size
138
+
139
+ num = field.flatten.compact.uniq.size
140
+
141
+ # define variables:
142
+ # - 4 variables (directions) to each number cell
143
+ # - 7 (patterns) + n (numbers) variables to each blank cell
144
+ vars = field.map do |line|
145
+ line.map do |c|
146
+ if c
147
+ # number cell
148
+ dirs = (0...4).map { solver.new_var }
149
+
150
+ # exact one directions
151
+ exact_one(solver, dirs)
152
+
153
+ dirs
154
+ else
155
+ # blank cell
156
+ pats = (0...7).map { solver.new_var }
157
+ nums = (0...num).map { solver.new_var }
158
+
159
+ # exact one pattern
160
+ exact_one(solver, pats)
161
+
162
+ # zero connected numbers in each blank cell if the cell is blank
163
+ # exact one connected numbers if each blank cell if the cell has a line
164
+ ([pats.last] + nums).combination(2) {|v1, v2| solver << [-v1, -v2] }
165
+
166
+ # no blank pattern (if filled solution is required)
167
+ solver << -pats.last if $filled
168
+
169
+ [pats, nums]
170
+ end
171
+ end
172
+ end
173
+
174
+ # define connection rule
175
+ field.each_with_index do |line, y|
176
+ line.each_with_index do |num, x|
177
+ if num
178
+ number_connectivity(solver, field, vars, x, y, w, h)
179
+ else
180
+ line_connectivity(solver, field, vars, x, y, w, h)
181
+ end
182
+ end
183
+ end
184
+
185
+ # corner optimization
186
+ l_corner, r_corner = {}, {}
187
+ field.each_with_index do |line, y|
188
+ line.each_with_index do |num, x|
189
+ if num
190
+ l_corner[[x - 1, y + 1]] = r_corner[[x + 1, y + 1]] = true
191
+ else
192
+ if l_corner[[x, y]]
193
+ l_corner[[x - 1, y + 1]] = true
194
+ if !field[y - 1][x + 1]
195
+ solver << [-vars[y][x].first[2], vars[y - 1][x + 1].first[2]]
196
+ end
197
+ else
198
+ solver << -vars[y][x].first[2]
199
+ end
200
+ if r_corner[[x, y]]
201
+ r_corner[[x + 1, y + 1]] = true
202
+ if !field[y - 1][x - 1]
203
+ solver << [-vars[y][x].first[1], vars[y - 1][x - 1].first[1]]
204
+ end
205
+ else
206
+ solver << -vars[y][x].first[1]
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ vars
213
+ end
214
+
215
+
216
+ # basic connectivity
217
+ U_CON = [0, 3, 4]
218
+ L_CON, R_CON = [2, 4, 5], [1, 3, 5]
219
+ D_CON = [0, 1, 2]
220
+
221
+
222
+ DIR = [[0, -1], [0, 1], [-1, 0], [1, 0]]
223
+
224
+
225
+ def line_connectivity(solver, field, vars, x, y, w, h)
226
+ if x < w - 1 && !field[y][x + 1]
227
+ l_pats, l_nums = vars[y][x]
228
+ r_pats, r_nums = vars[y][x + 1]
229
+
230
+ # extended horizontal connectivity
231
+ solver << [-l_pats[2], r_pats[3], r_pats[5]]
232
+ solver << [-l_pats[4], r_pats[1], r_pats[5]]
233
+ solver << [-l_pats[5], r_pats[1], r_pats[3], r_pats[5]]
234
+ solver << [-r_pats[1], l_pats[4], l_pats[5]]
235
+ solver << [-r_pats[3], l_pats[2], l_pats[5]]
236
+ solver << [-r_pats[5], l_pats[2], l_pats[4], l_pats[5]]
237
+
238
+ # the same number if connected
239
+ l_nums.zip(r_nums) do |l_num, r_num|
240
+ L_CON.each do |i|
241
+ solver << [-l_pats[i], -l_num, r_num] << [-l_pats[i], l_num, -r_num]
242
+ end
243
+ end
244
+ end
245
+
246
+ if y < h - 1 && !field[y + 1][x]
247
+ u_pats, u_nums = vars[y][x]
248
+ d_pats, d_nums = vars[y + 1][x]
249
+
250
+ # extended vartical connectivity
251
+ solver << [-u_pats[0], d_pats[0], d_pats[1], d_pats[2]]
252
+ solver << [-u_pats[3], d_pats[0], d_pats[2]]
253
+ solver << [-u_pats[4], d_pats[0], d_pats[1] ]
254
+ solver << [-d_pats[0], u_pats[0], u_pats[3], u_pats[4]]
255
+ solver << [-d_pats[1], u_pats[0], u_pats[4]]
256
+ solver << [-d_pats[2], u_pats[0], u_pats[3] ]
257
+
258
+ # the same number if connected
259
+ u_nums.zip(d_nums) do |u_num, d_num|
260
+ U_CON.each do |i|
261
+ solver << [-u_pats[i], -u_num, d_num] << [-u_pats[i], u_num, -d_num]
262
+ end
263
+ end
264
+ end
265
+
266
+ # edges of field
267
+ R_CON.map {|i| solver << -vars[y][x].first[i] } if x == 0
268
+ L_CON.map {|i| solver << -vars[y][x].first[i] } if x == w - 1
269
+ D_CON.map {|i| solver << -vars[y][x].first[i] } if y == 0
270
+ U_CON.map {|i| solver << -vars[y][x].first[i] } if y == h - 1
271
+ end
272
+
273
+
274
+ def number_connectivity(solver, field, vars, x, y, w, h)
275
+ rev = [U_CON, D_CON, L_CON, R_CON].zip(DIR).to_a
276
+ num = field[y][x]
277
+
278
+ 4.times do |i|
279
+ dir = vars[y][x][i]
280
+ con, (xo, yo) = rev[0]
281
+ x2, y2 = x + xo, y + yo
282
+ if x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && !field[y2][x2]
283
+ pats, nums = vars[y2][x2]
284
+ solver << [-dir, nums[num - 1]]
285
+ solver << [-dir] + con.map {|i| pats[i] }
286
+ (pats - con.map {|i| pats[i] }).each {|v| solver << [-dir, -v] }
287
+ (1..3).each do |j|
288
+ con, (xo, yo) = rev[j]
289
+ x2, y2 = x + xo, y + yo
290
+ if x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && !field[y2][x2]
291
+ pats, nums = vars[y2][x2]
292
+ con.each {|i| solver << [-dir, -pats[i]] }
293
+ solver << [-dir] + (pats - con.map {|i| pats[i] })
294
+ end
295
+ end
296
+ elsif x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && num == field[y2][x2]
297
+ solver << dir
298
+ else
299
+ solver << -dir
300
+ end
301
+ rev << rev.shift
302
+ end
303
+ end
304
+
305
+
306
+ def exact_one(solver, vars)
307
+ solver << vars
308
+ vars.combination(2) {|v1, v2| solver << [-v1, -v2] }
309
+ end
310
+
311
+
312
+ def make_solution(solver, vars, field)
313
+ field.zip(vars).map do |fline, vline|
314
+ fline.zip(vline).map do |num, vs|
315
+ if num
316
+ (0...4).find {|i| solver[vs[i]] }
317
+ else
318
+ (0...7).find {|i| solver[vs.first[i]] }
319
+ end
320
+ end
321
+ end
322
+ end
323
+
324
+
325
+ def add_constraint(solver, vars)
326
+ solver << vars.flatten.map {|v| solver[v] ? -v : v }
327
+ end
328
+
329
+
330
+ def output_field(solution, field)
331
+ w = field.first.size
332
+ h = field.size
333
+ ary = (0 ... h).map { (0 ... w).map { nil } }
334
+
335
+ solution.each_with_index do |line, y|
336
+ line.each_with_index do |c, x|
337
+ num = field[y][x]
338
+ ary[y][x] = if num
339
+ case c
340
+ when 0 then [" #{ num }"[-4..-1], " "]
341
+ when 1 then [" #{ num }"[-4..-1], " |"]
342
+ when 2 then ["----#{ num }"[-4..-1], " "]
343
+ when 3 then [" #{ num }"[-4..-1], " "]
344
+ end
345
+ else
346
+ case c
347
+ when 0 then [" +", " |"]
348
+ when 1 then ["---+", " "]
349
+ when 2 then [" +", " "]
350
+ when 3 then ["---+", " |"]
351
+ when 4 then [" +", " |"]
352
+ when 5 then ["---+", " "]
353
+ when 6 then [" ", " "]
354
+ end
355
+ end
356
+ end
357
+ end
358
+ ary.map {|line| line.transpose }.flatten(1)[0..-2].each do |line|
359
+ puts line.join.rstrip[1..-1]
360
+ end
361
+ end
362
+
363
+ def find_loops(solution, field)
364
+ solution = solution.map {|line| line.dup }
365
+ field.each_with_index do |line, y|
366
+ line.each_with_index do |num, x|
367
+ next unless num
368
+ xo, yo = DIR[solution[y][x]]
369
+ solution[y][x] = nil
370
+ find_loops_aux(solution, field, x + xo, y + yo)
371
+ end
372
+ end
373
+
374
+ loops = []
375
+ solution.each_with_index do |line, y|
376
+ line.each_with_index do |c, x|
377
+ next if !c || c == 6
378
+ loops << find_loops_aux(solution, field, x, y)
379
+ end
380
+ end
381
+
382
+ loops
383
+ end
384
+
385
+
386
+ def find_loops_aux(solution, field, x, y)
387
+ ary = []
388
+ while !field[y][x]
389
+ num = solution[y][x]
390
+ ary << [x, y, num] if num
391
+ solution[y][x] = nil
392
+ case num
393
+ when 0 then solution[y - 1][x] ? y -= 1 : y += 1
394
+ when 1 then solution[y - 1][x] ? y -= 1 : x -= 1
395
+ when 2 then solution[y - 1][x] ? y -= 1 : x += 1
396
+ when 3 then solution[y + 1][x] ? y += 1 : x -= 1
397
+ when 4 then solution[y + 1][x] ? y += 1 : x += 1
398
+ when 5 then solution[y][x - 1] ? x -= 1 : x += 1
399
+ else break
400
+ end
401
+ end
402
+ ary
403
+ end
404
+
405
+
406
+ def add_loop_constraint(solver, vars, loops)
407
+ loops.each do |ary|
408
+ solver << ary.map {|x, y, c| vars[y][x].first[c] }
409
+ end
410
+ end
411
+
412
+
413
+ def solve(solver, vars, field, prog_msg, found_msg, not_found_msg)
414
+ trial = 0
415
+ loop do
416
+ trial += 1
417
+ puts "#{ prog_msg }... (trial #{ trial })"
418
+ puts "clauses : #{ solver.clause_size }"
419
+
420
+ start = Time.now
421
+ result = solver.solve
422
+ eplise = Time.now - start
423
+ puts "time: %.6f sec." % eplise
424
+ puts
425
+ unless result
426
+ puts not_found_msg
427
+ return false
428
+ end
429
+
430
+ puts "translating model into solution..."
431
+ solution = make_solution(solver, vars, field)
432
+ loops = find_loops(solution, field)
433
+ add_loop_constraint(solver, vars, loops)
434
+
435
+ if loops.empty?
436
+ puts found_msg
437
+ output_field(solution, field)
438
+ puts
439
+ return true
440
+ end
441
+ end
442
+ end
443
+
444
+
445
+ if ARGV.first == "-f"
446
+ $filled = true
447
+ ARGV.shift
448
+ end
449
+ if ARGV.first == "-b"
450
+ $filled = false
451
+ ARGV.shift
452
+ end
453
+
454
+ if ARGV.empty?
455
+ $stderr.puts <<END
456
+ usage: numberlink.rb [-f or -b] numberlink.sample
457
+ options:
458
+ -f search only filled solution (any blank cell has line) [default]
459
+ -b search any solution (blank cell may remain) (slowish)
460
+ END
461
+ exit 0
462
+ end
463
+
464
+ ARGV.each do |file|
465
+ field = parse_file(file)
466
+
467
+ solver = MiniSat::Solver.new
468
+
469
+ puts "defining SAT..."
470
+ vars = define_sat(solver, field)
471
+ puts "variables : #{ solver.var_size }"
472
+ puts
473
+
474
+ str = $filled ? "(filled) solution" : "solution"
475
+
476
+ prog_msg = "solving SAT"
477
+ found_msg = "#{ str } found."
478
+ not_found_msg = "unsolvable."
479
+ solve(solver, vars, field, prog_msg, found_msg, not_found_msg) or exit 1
480
+
481
+ add_constraint(solver, vars)
482
+
483
+ prog_msg = "finding different #{ str }"
484
+ found_msg = "different #{ str } found."
485
+ not_found_msg = "different #{ str } not found."
486
+ solve(solver, vars, field, prog_msg, found_msg, not_found_msg) and exit 1
487
+ puts
488
+ puts
489
+ end