ruby-minisat 1.14.2

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 (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,11 @@
1
+ # quoted from PencilBox (http://pencilbox.sourceforge.jp/)
2
+ 1 . . . . . . . . 2
3
+ . . 3 . . . . 4 . .
4
+ . 2 . . . . . . 5 .
5
+ . . . 6 . . 1 . . .
6
+ . . . 7 . . 8 . . .
7
+ . . . . . . . . . .
8
+ . . 3 . . . . 5 . .
9
+ . . . . . . . . . .
10
+ . . 7 . . . . 8 . .
11
+ 6 . . . . . . . . 4
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- shikaku.rb
4
+ # ref: http://en.wikipedia.org/wiki/Shikaku
5
+
6
+ ##
7
+ ## SAT configuration:
8
+ ## - Variables: assign variable to every possible boxes around each number
9
+ ## - Clauses: exact one variable of possible box at each cell is true
10
+ ##
11
+ ## For example, there are four possible boxes around number 2:
12
+ ##
13
+ ## a b c d
14
+ ## +---+
15
+ ## | . |
16
+ ## | | +-------+ +---+ +-------+
17
+ ## | 2 | | 2 . | | 2 | | . 2 |
18
+ ## +---+ +-------+ | | +-------+
19
+ ## | . |
20
+ ## +---+
21
+ ##
22
+ ## And, every cell must be covered by exact one boxes.
23
+ ##
24
+
25
+ require "minisat"
26
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
27
+
28
+
29
+ def error(msg)
30
+ $stderr.puts msg
31
+ exit 1
32
+ end
33
+
34
+
35
+ def parse_file(file)
36
+ width = nil
37
+ field = []
38
+ File.read(file).split(/\n/).each do |line|
39
+ case line
40
+ when /^\s*#.*$/, /^\s*$/
41
+ else
42
+ line = line.split.map {|n| n[/^\d+$/] && n.to_i }
43
+ width ||= line.size
44
+ unless width == line.size
45
+ error "illegal width: row #{ field.size + 1 }"
46
+ end
47
+ field << line
48
+ end
49
+ end
50
+
51
+ field
52
+ end
53
+
54
+
55
+ def define_sat(solver, field)
56
+ w = field.first.size
57
+ h = field.size
58
+ ary = field.map {|line| line.map { [] } }
59
+
60
+ field.each_with_index do |line, y|
61
+ line.each_with_index do |c, x|
62
+ next unless c
63
+ enum_boxes(c) do |xys|
64
+ catch(:next) do
65
+ xys.each do |xo, yo|
66
+ x2, y2 = x + xo, y + yo
67
+ throw :next if x2 < 0 || x2 >= w || y2 < 0 || y2 >= h
68
+ throw :next if field[y2][x2] && (x != x2 || y != y2)
69
+ end
70
+ v = solver.new_var
71
+ xys.each {|xo, yo| ary[y + yo][x + xo] |= [v] }
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ ary.each do |line|
78
+ line.each do |vs|
79
+ solver << vs
80
+ vs.combination(2) {|v1, v2| solver << [-v1, -v2] }
81
+ end
82
+ end
83
+
84
+ ary
85
+ end
86
+
87
+
88
+ def enum_boxes(n)
89
+ (1..n).each do |m|
90
+ next unless (n / m) * m == n
91
+ a = (0 ... n / m).to_a.product((0...m).to_a)
92
+ a.each {|x, y| yield a.map {|x2, y2| [x2 - x, y2 - y] } }
93
+ end
94
+ end
95
+
96
+
97
+ def solve_sat(solver)
98
+ start = Time.now
99
+ result = solver.solve
100
+ eplise = Time.now - start
101
+ puts "time: %.6f sec." % eplise
102
+ result
103
+ end
104
+
105
+
106
+ def add_constraint(solver, vars)
107
+ solver << vars.flatten.map {|v| solver[v] ? -v : v }
108
+ end
109
+
110
+
111
+ def make_solution(solver, ary)
112
+ ary.map {|line| line.map {|vs| vs.find {|v| solver[v] } } }
113
+ end
114
+
115
+
116
+ def output_field(solution, field)
117
+ w = field.first.size
118
+ h = field.size
119
+
120
+ ary = (0 .. h * 2).map { (0 .. w * 2).map { nil } }
121
+ field.each_with_index do |line, y|
122
+ line.each_with_index do |c, x|
123
+ ary[y * 2 + 1][x * 2 + 1] = c ? c.to_s.rjust(2) : " ."
124
+
125
+ if x == 0 || solution[x - 1][y] != solution[x][y]
126
+ ary[y * 2 + 1][x * 2] = " |"
127
+ end
128
+ ary[y * 2 + 1][x * 2 + 2] = " |" if x == w - 1
129
+ if y == 0 || solution[x][y - 1] != solution[x][y]
130
+ ary[y * 2][x * 2 + 1] = "--"
131
+ end
132
+ ary[y * 2 + 2][x * 2 + 1] = "--" if y == h - 1
133
+ end
134
+ end
135
+
136
+ (0 .. h * 2).step(2) do |y|
137
+ (0 .. w * 2).step(2) do |x|
138
+ u = y > 0 && ary[y - 1][x]
139
+ d = y < h * 2 - 1 && ary[y + 1][x]
140
+ l = x > 0 && ary[y][x - 1]
141
+ r = x < w * 2 - 1 && ary[y][x + 1]
142
+ ary[y][x] ||= "--" if !u && !d && r && l
143
+ ary[y][x] ||= " |" if u && d && !r && !l
144
+ ary[y][x] ||= "-+" if l
145
+ ary[y][x] ||= " +" if u || d || r || l
146
+ end
147
+ end
148
+
149
+ puts ary.map {|line| " " + line.map {|s| s || " " }.join }
150
+ end
151
+
152
+
153
+
154
+ error "usage: shikaku.rb shikaku.sample" if ARGV.empty?
155
+
156
+ ARGV.each do |file|
157
+ field = parse_file(file)
158
+
159
+ solver = MiniSat::Solver.new
160
+
161
+ puts "defining SAT..."
162
+ vars = define_sat(solver, field)
163
+
164
+ puts "solving SAT..."
165
+ result = solve_sat(solver)
166
+ puts "result: " + (result ? "solvable" : "unsolvable")
167
+ puts
168
+ next unless result
169
+
170
+ puts "translating model into solution..."
171
+ solution = make_solution(solver, vars)
172
+ puts "solution found:"
173
+ output_field(solution, field)
174
+ puts
175
+
176
+ puts "checking different solution..."
177
+ add_constraint(solver, vars)
178
+ result = solve_sat(solver)
179
+ puts "result: " +
180
+ (result ? "different solution found" : "different solution not found")
181
+ puts
182
+ next unless result
183
+
184
+ puts "translating model into solution..."
185
+ solution = make_solution(solver, vars)
186
+ puts "different solution:"
187
+ output_field(solution, field)
188
+ puts
189
+ puts
190
+ end
@@ -0,0 +1,11 @@
1
+ # quoted from PencilBox (http://pencilbox.sourceforge.jp/)
2
+ . . . . . 8 . . . .
3
+ . 8 . . 6 . . . 6 .
4
+ . . 3 . . . . . . .
5
+ . . . . . . 5 . . .
6
+ . 4 . . 4 . . . . 6
7
+ 9 . . . . 3 . . 4 .
8
+ . . . 6 . . . . . .
9
+ . . . . . . . 4 . .
10
+ . 4 . . . 8 . . 6 .
11
+ . . . . 6 . . . . .
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- slitherlink.rb
4
+ # ref: http://en.wikipedia.org/wiki/Slitherlink
5
+
6
+ ##
7
+ ## SAT configuration:
8
+ ## - Variables: assign one variable to each edge
9
+ ## - Clauses: some rules of slither link (below)
10
+ ##
11
+ ##
12
+ ## rules of slither link:
13
+ ## - rule 1. at each vertex, zero or two surround edges are drawn
14
+ ## - rule 2. at each cell, specified number of surround edges are drawn
15
+ ## - rule 3. drawn edges make exact one loop
16
+ ##
17
+ ##
18
+ ## We have no good idea how to write the rule 3; SAT solver may find bad
19
+ ## solution in that there are multiple loops. So we use following approach:
20
+ ##
21
+ ## - find any solution that satisfies only the rules 1 and 2,
22
+ ## - count loops of the solution,
23
+ ## - if the number of loop is exact one, it is good solution
24
+ ## - if the number of loop is more than one, add new constraint that prevents
25
+ ## the solution, and retry to solve
26
+ ##
27
+
28
+
29
+ require "minisat"
30
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
31
+
32
+
33
+ def error(msg)
34
+ $stderr.puts msg
35
+ exit 1
36
+ end
37
+
38
+
39
+ def parse_file(file)
40
+ width = nil
41
+ field = []
42
+ File.read(file).split(/\n/).each do |line|
43
+ case line
44
+ when /^\s*#.*$/, /^\s*$/
45
+ else
46
+ line = line.split.map {|x| x[/^\d$/] && x.to_i }
47
+ width ||= line.size
48
+ unless width == line.size
49
+ error "illegal width: row #{ field.size + 1 }"
50
+ end
51
+ field << line
52
+ end
53
+ end
54
+
55
+ field
56
+ end
57
+
58
+
59
+ def define_sat(solver, field)
60
+ w = field.first.size
61
+ h = field.size
62
+
63
+ # define horizontal and vertical edges
64
+ h_vars = (0..h).map { (0...w).map { solver.new_var } }
65
+ v_vars = (0...h).map { (0..w).map { solver.new_var } }
66
+
67
+ # define clauses
68
+ # rule 1
69
+ (0..h).each do |y|
70
+ (0..w).each do |x|
71
+ edges = []
72
+ edges << h_vars[y][x - 1] if x > 0
73
+ edges << h_vars[y][x ] if x < w
74
+ edges << v_vars[y - 1][x] if y > 0
75
+ edges << v_vars[y ][x] if y < h
76
+
77
+ # exact zero or two variables are true
78
+ a, b, c, d = edges
79
+ case edges.size
80
+ when 2
81
+ solver << [a, -b] << [-a, b]
82
+ when 3
83
+ solver << [-a, b, c] << [a, -b, c] << [a, b, -c] << [-a, -b, -c]
84
+ when 4
85
+ solver <<
86
+ [-a, b, c, d] <<
87
+ [ a, -b, c, d] <<
88
+ [ a, b, -c, d] <<
89
+ [ a, b, c, -d] <<
90
+ [-a, -b, -c] <<
91
+ [-b, -c, -d] <<
92
+ [-c, -d, -a] <<
93
+ [-d, -a, -b]
94
+ end
95
+ end
96
+ end
97
+
98
+ ## rule 2
99
+ field.each_with_index do |line, y|
100
+ line.each_with_index do |c, x|
101
+ edges = [h_vars[y][x], h_vars[y + 1][x], v_vars[y][x], v_vars[y][x + 1]]
102
+
103
+ # specified number of variables are true
104
+ case c
105
+ when 0
106
+ edges.each {|v| solver << -v }
107
+ when 1
108
+ solver << edges
109
+ edges.combination(2) {|v1, v2| solver << [-v1, -v2] }
110
+ when 2
111
+ edges.combination(3) do |v1, v2, v3|
112
+ solver << [v1, v2, v3] << [-v1, -v2, -v3]
113
+ end
114
+ when 3
115
+ solver << edges.map {|v| -v }
116
+ edges.combination(2) {|v1, v2| solver << [v1, v2] }
117
+ when 4
118
+ edges.each {|v| solver << v }
119
+ end
120
+ end
121
+ end
122
+
123
+ [h_vars, v_vars]
124
+ end
125
+
126
+
127
+ def count_loops(solver, vars)
128
+ h_vars, v_vars = vars
129
+
130
+ ps = {}
131
+ h_vars.each_with_index do |l, y|
132
+ l.each_with_index {|c, x| ps[[x * 2 + 1, y * 2]] = true if solver[c] }
133
+ end
134
+ v_vars.each_with_index do |l, y|
135
+ l.each_with_index {|c, x| ps[[x * 2, y * 2 + 1]] = true if solver[c] }
136
+ end
137
+ loops = []
138
+ until ps.size == 0
139
+ ary = []
140
+ x, y = ps.keys.first
141
+ loop do
142
+ ary << [x, y]
143
+ ps.delete [x, y]
144
+ if x % 2 == 0
145
+ case
146
+ when ps[[x - 1, y - 1]] then x, y = x - 1, y - 1
147
+ when ps[[x + 1, y - 1]] then x, y = x + 1, y - 1
148
+ when ps[[x , y - 2]] then x, y = x , y - 2
149
+ when ps[[x - 1, y + 1]] then x, y = x - 1, y + 1
150
+ when ps[[x + 1, y + 1]] then x, y = x + 1, y + 1
151
+ when ps[[x , y + 2]] then x, y = x , y + 2
152
+ else break
153
+ end
154
+ else
155
+ case
156
+ when ps[[x - 1, y - 1]] then x, y = x - 1, y - 1
157
+ when ps[[x - 1, y + 1]] then x, y = x - 1, y + 1
158
+ when ps[[x - 2, y ]] then x, y = x - 2, y
159
+ when ps[[x + 1, y - 1]] then x, y = x + 1, y - 1
160
+ when ps[[x + 1, y + 1]] then x, y = x + 1, y + 1
161
+ when ps[[x + 2, y ]] then x, y = x + 2, y
162
+ else break
163
+ end
164
+ end
165
+ end
166
+ loops << ary
167
+ end
168
+ loops
169
+ end
170
+
171
+
172
+ def add_constraint(solver, vars, loops)
173
+ h_vars, v_vars = vars
174
+
175
+ loops.map do |ary|
176
+ ary.map do |x, y|
177
+ v = (x % 2 == 0 ? v_vars : h_vars)[y / 2][x / 2]
178
+ solver[v] ? -v : v
179
+ end
180
+ end.each {|e| solver << e }
181
+ end
182
+
183
+
184
+ def output_field(solver, vars, field)
185
+ w = field.first.size
186
+ h = field.size
187
+ h_vars, v_vars = vars
188
+
189
+ ary = (0 .. h * 2).map { (0 .. w * 2).map { nil } }
190
+ h_vars.each_with_index do |l, y|
191
+ l.each_with_index do |c, x|
192
+ ary[y * 2][x * 2 + 1] = "--" if solver[c]
193
+ end
194
+ end
195
+ v_vars.each_with_index do |l, y|
196
+ l.each_with_index do |c, x|
197
+ ary[y * 2 + 1][x * 2] = " |" if solver[c]
198
+ end
199
+ end
200
+ field.each_with_index do |l, y|
201
+ l.each_with_index do |c, x|
202
+ ary[y * 2 + 1][x * 2 + 1] = c ? c.to_s.rjust(2) : " ."
203
+ end
204
+ end
205
+ (0 .. h * 2).step(2) do |y|
206
+ (0 .. w * 2).step(2) do |x|
207
+ u = y > 0 && ary[y - 1][x]
208
+ d = y < h * 2 && ary[y + 1][x]
209
+ ary[y][x] = " |" if u && d
210
+ r = x > 0 && ary[y][x - 1]
211
+ l = x < w * 2 && ary[y][x + 1]
212
+ ary[y][x] = "--" if r && l
213
+ ary[y][x] ||= " +" if l
214
+ ary[y][x] ||= "-+" if u || d || r || l
215
+ end
216
+ end
217
+ ary.each {|l| puts " " + l.map {|c| c || " " }.join }
218
+ end
219
+
220
+
221
+ def solve(solver, vars, field, prog_msg, found_msg, not_found_msg)
222
+ trial = 0
223
+ loop do
224
+ trial += 1
225
+ puts "#{ prog_msg }... (trial #{ trial })"
226
+ puts "clauses : #{ solver.clause_size }"
227
+
228
+ start = Time.now
229
+ result = solver.solve
230
+ eplise = Time.now - start
231
+ puts "time: %.6f sec." % eplise
232
+ puts
233
+ unless result
234
+ puts not_found_msg
235
+ return false
236
+ end
237
+
238
+ loops = count_loops(solver, vars)
239
+
240
+ error "no loop is needed" if loops.empty?
241
+
242
+ if loops.size == 1
243
+ puts found_msg
244
+ output_field(solver, vars, field)
245
+ puts
246
+ end
247
+
248
+ add_constraint(solver, vars, loops)
249
+
250
+ return true if loops.size == 1
251
+ end
252
+ end
253
+
254
+
255
+
256
+ error "usage: slitherlink.rb slitherlink.sample" if ARGV.empty?
257
+
258
+ ARGV.each do |file|
259
+ field = parse_file(file)
260
+
261
+ solver = MiniSat::Solver.new
262
+
263
+ puts "defining SAT..."
264
+ vars = define_sat(solver, field)
265
+ puts "variables : #{ solver.var_size }"
266
+ puts
267
+
268
+ prog_msg = "solving SAT"
269
+ found_msg = "solution found."
270
+ not_found_msg = "unsolvable."
271
+ solve(solver, vars, field, prog_msg, found_msg, not_found_msg) or exit 1
272
+
273
+ prog_msg = "finding different solution"
274
+ found_msg = "different solution found."
275
+ not_found_msg = "different solution not found."
276
+ solve(solver, vars, field, prog_msg, found_msg, not_found_msg) and exit 1
277
+ puts
278
+ puts
279
+ end