ruby-minisat 1.14.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +24 -0
- data/LICENSE +21 -0
- data/README.rdoc +56 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/examples/compat18.rb +65 -0
- data/examples/example.rb +26 -0
- data/examples/example2.rb +60 -0
- data/examples/kakuro.rb +178 -0
- data/examples/kakuro.sample +13 -0
- data/examples/lonely7.rb +302 -0
- data/examples/nonogram.rb +254 -0
- data/examples/nonogram.sample +26 -0
- data/examples/numberlink.rb +489 -0
- data/examples/numberlink.sample +11 -0
- data/examples/shikaku.rb +190 -0
- data/examples/shikaku.sample +11 -0
- data/examples/slitherlink.rb +279 -0
- data/examples/slitherlink.sample +11 -0
- data/examples/sudoku.rb +216 -0
- data/examples/sudoku.sample +11 -0
- data/ext/minisat/extconf.rb +9 -0
- data/ext/minisat/minisat-wrap.cpp +88 -0
- data/ext/minisat/minisat.c +497 -0
- data/ext/minisat/minisat.h +53 -0
- data/minisat/MiniSat_v1.14.2006-Aug-29.src.zip +0 -0
- data/minisat/MiniSat_v1.14/Global.h +274 -0
- data/minisat/MiniSat_v1.14/Heap.h +100 -0
- data/minisat/MiniSat_v1.14/LICENSE +20 -0
- data/minisat/MiniSat_v1.14/Main.C +244 -0
- data/minisat/MiniSat_v1.14/Makefile +88 -0
- data/minisat/MiniSat_v1.14/README +30 -0
- data/minisat/MiniSat_v1.14/Solver.C +781 -0
- data/minisat/MiniSat_v1.14/Solver.h +206 -0
- data/minisat/MiniSat_v1.14/Solver.o +0 -0
- data/minisat/MiniSat_v1.14/SolverTypes.h +130 -0
- data/minisat/MiniSat_v1.14/Sort.h +131 -0
- data/minisat/MiniSat_v1.14/TODO +73 -0
- data/minisat/MiniSat_v1.14/VarOrder.h +96 -0
- data/test/test_minisat.rb +143 -0
- metadata +114 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
# quoted from PencilBox (http://pencilbox.sourceforge.jp/)
|
2
|
+
**\** **\** **\** 03\** 05\** 15\** **\** **\** 20\** 16\** **\** **\**
|
3
|
+
**\** **\** 07\07 . . . 08\** **\17 . . 35\** **\**
|
4
|
+
**\** 04\15 . . . . . 19\24 . . . 24\**
|
5
|
+
**\05 . . 10\** **\10 . . . . **\17 . .
|
6
|
+
**\07 . . . 06\10 . . . 12\** 12\15 . .
|
7
|
+
**\** **\** 15\06 . . . **\35 . . . . .
|
8
|
+
**\** 10\06 . . . 08\** **\** 35\10 . . . **\**
|
9
|
+
**\15 . . . . . 20\09 . . . 12\** 06\**
|
10
|
+
**\07 . . **\** 06\18 . . . **\06 . . .
|
11
|
+
**\08 . . 03\20 . . . . 15\** 16\07 . .
|
12
|
+
**\** **\08 . . . **\33 . . . . . **\**
|
13
|
+
**\** **\** **\04 . . **\** **\23 . . . **\** **\**
|
data/examples/lonely7.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ruby-minisat example -- lonely7.rb
|
4
|
+
|
5
|
+
|
6
|
+
require "minisat"
|
7
|
+
require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
|
8
|
+
|
9
|
+
|
10
|
+
###############################################################################
|
11
|
+
|
12
|
+
|
13
|
+
class Digit
|
14
|
+
def initialize
|
15
|
+
@vars = (0..9).map { $solver.new_var }
|
16
|
+
$solver << vars
|
17
|
+
vars.combination(2) {|v1, v2| $solver << [-v1, -v2] }
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :vars
|
21
|
+
|
22
|
+
def is(n)
|
23
|
+
case n
|
24
|
+
when Digit
|
25
|
+
vars.zip(n.vars) {|v1, v2| $solver << [v1, -v2] << [-v1, v2] }
|
26
|
+
when Numeric
|
27
|
+
$solver << vars[n]
|
28
|
+
else raise TypeError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_not(n)
|
33
|
+
case n
|
34
|
+
when Digit
|
35
|
+
vars.zip(n.vars) {|v1, v2| $solver << [-v1, -v2] }
|
36
|
+
when Numeric
|
37
|
+
$solver << -vars[n]
|
38
|
+
else raise TypeError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sum(a, b, f = nil)
|
43
|
+
sumdiff(a, b, f) {|na, nb| na + nb }
|
44
|
+
end
|
45
|
+
|
46
|
+
def diff(a, b, f = nil)
|
47
|
+
sumdiff(a, b, f) {|na, nb| na - nb }
|
48
|
+
end
|
49
|
+
|
50
|
+
def sumdiff(a, b, f = nil)
|
51
|
+
c = f ? Digit.new : self
|
52
|
+
f2 = pair(a, b, c) {|na, nb| n = yield(na, nb); [n % 10, n % 10 != n] }
|
53
|
+
f ? flag(c, f, f2) {|n| n = yield(n, 1); [n % 10, n % 10 != n] } : f2
|
54
|
+
end
|
55
|
+
|
56
|
+
def prod(a, b, d)
|
57
|
+
if b.is_a?(Numeric)
|
58
|
+
b2 = Digit.new
|
59
|
+
b2.is b
|
60
|
+
b = b2
|
61
|
+
end
|
62
|
+
a.vars.each_with_index do |va, na|
|
63
|
+
b.vars.each_with_index do |vb, nb|
|
64
|
+
nd, nc = (na * nb).divmod(10)
|
65
|
+
$solver << [-va, -vb, vars[nc]] << [-va, -vb, d.vars[nd]]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def less(b, f)
|
71
|
+
fs = (0..9).map { $solver.new_var }
|
72
|
+
fr = $solver.new_var
|
73
|
+
b.vars.each_with_index do |vb, nb|
|
74
|
+
va = vars[nb]
|
75
|
+
vf = fs[nb]
|
76
|
+
$solver << [-f, -vb] + vars[0..nb]
|
77
|
+
$solver << [-vb, -va, vf] << [-vf, va] << [-vf, vb] << [-vf, fr]
|
78
|
+
end
|
79
|
+
$solver << [-fr, f] << [-fr] + fs
|
80
|
+
fr
|
81
|
+
end
|
82
|
+
|
83
|
+
def pair(a, b, c)
|
84
|
+
f = $solver.new_var
|
85
|
+
a.vars.each_with_index do |va, na|
|
86
|
+
b.vars.each_with_index do |vb, nb|
|
87
|
+
nc, f2 = yield(na, nb)
|
88
|
+
$solver << [-va, -vb, c.vars[nc]] << [-va, -vb, f2 ? f : -f]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
f
|
92
|
+
end
|
93
|
+
|
94
|
+
def flag(a, f0, f1)
|
95
|
+
f2 = $solver.new_var
|
96
|
+
$solver << [f0, -f2]
|
97
|
+
a.vars.each_with_index do |v, n|
|
98
|
+
$solver << [-v, f0, vars[n]]
|
99
|
+
n2, f = yield n
|
100
|
+
$solver << [-v, -f0, vars[n2]] << [-v, -f0, f ? f2 : -f2]
|
101
|
+
end
|
102
|
+
|
103
|
+
f3 = $solver.new_var
|
104
|
+
$solver << [-f1, f3] << [-f2, f3] << [-f3, f1, f2]
|
105
|
+
f3
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_i
|
109
|
+
(0..9).find {|n| $solver[vars[n]] }.to_i
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Digits
|
114
|
+
def initialize(n)
|
115
|
+
@digits = n.is_a?(Numeric) ? (0...n).map { Digit.new } : n
|
116
|
+
end
|
117
|
+
|
118
|
+
attr_reader :digits
|
119
|
+
|
120
|
+
def is(num)
|
121
|
+
case num
|
122
|
+
when Adder, Subtracter
|
123
|
+
f = nil
|
124
|
+
z = zero
|
125
|
+
ms = [num.left, num.right, self].
|
126
|
+
max {|x, y| x.digits.size <=> y.digits.size }
|
127
|
+
as = ms.extend(num.left.digits, z)
|
128
|
+
bs = ms.extend(num.right.digits, z)
|
129
|
+
cs = ms.extend(digits, z)
|
130
|
+
as.zip(bs, cs) do |a, b, c|
|
131
|
+
f = num.is_a?(Adder) ? c.sum(a, b, f) : c.diff(a, b, f)
|
132
|
+
end
|
133
|
+
$solver << -f
|
134
|
+
when Multiplier
|
135
|
+
f = d = nil
|
136
|
+
b = num.right
|
137
|
+
as = extend(num.left.digits, zero)
|
138
|
+
as.zip(digits) do |a, c|
|
139
|
+
c2 = d ? Digit.new : c
|
140
|
+
d2 = Digit.new
|
141
|
+
c2.prod(a, b, d2)
|
142
|
+
f = c.sum(c2, d, f) if d
|
143
|
+
d = d2
|
144
|
+
end
|
145
|
+
$solver << -f
|
146
|
+
d.is 0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def less(b)
|
151
|
+
z = zero
|
152
|
+
bs = extend(b.digits, z)
|
153
|
+
as = b.extend(digits, z)
|
154
|
+
f = $solver.new_var
|
155
|
+
$solver << f
|
156
|
+
as.reverse.zip(bs.reverse).each do |a, b|
|
157
|
+
f = a.less(b, f)
|
158
|
+
end
|
159
|
+
$solver << -f
|
160
|
+
end
|
161
|
+
|
162
|
+
def extend(as, z)
|
163
|
+
digits.size > as.size ? as + [z] * (digits.size - as.size) : as
|
164
|
+
end
|
165
|
+
|
166
|
+
def top
|
167
|
+
digits.last
|
168
|
+
end
|
169
|
+
|
170
|
+
def *(d); Multiplier.new(self, d); end
|
171
|
+
def -(d); Subtracter.new(self, d); end
|
172
|
+
def +(d); Adder.new(self, d); end
|
173
|
+
|
174
|
+
def [](*arg)
|
175
|
+
case arg.size
|
176
|
+
when 1 then digits.reverse[arg.first]
|
177
|
+
when 2 then Digits.new(digits.reverse[arg.first, arg.last].reverse)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_i
|
182
|
+
digits.map {|v| v.to_i }.reverse.inject(0) {|z, x| z * 10 + x }
|
183
|
+
end
|
184
|
+
|
185
|
+
Adder = Struct.new(:left, :right)
|
186
|
+
Subtracter = Struct.new(:left, :right)
|
187
|
+
Multiplier = Struct.new(:left, :right)
|
188
|
+
end
|
189
|
+
|
190
|
+
def zero
|
191
|
+
d = Digit.new
|
192
|
+
d.is 0
|
193
|
+
d
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
###############################################################################
|
198
|
+
|
199
|
+
|
200
|
+
$solver = MiniSat::Solver.new
|
201
|
+
|
202
|
+
str = <<END
|
203
|
+
x7yzw
|
204
|
+
---------
|
205
|
+
aaa )bbbbbbbb
|
206
|
+
cccc
|
207
|
+
-----
|
208
|
+
ddd
|
209
|
+
eee
|
210
|
+
----
|
211
|
+
ffff
|
212
|
+
ggg
|
213
|
+
-----
|
214
|
+
hhhh
|
215
|
+
hhhh
|
216
|
+
----
|
217
|
+
0
|
218
|
+
END
|
219
|
+
|
220
|
+
puts "problem:"
|
221
|
+
puts str.gsub(/[a-z]/m, "#")
|
222
|
+
|
223
|
+
a = Digits.new(3); a.top.is_not 0
|
224
|
+
b = Digits.new(8); b.top.is_not 0
|
225
|
+
c = Digits.new(4); c.top.is_not 0
|
226
|
+
d = Digits.new(3); d.top.is_not 0
|
227
|
+
e = Digits.new(3); e.top.is_not 0
|
228
|
+
f = Digits.new(4); f.top.is_not 0
|
229
|
+
g = Digits.new(3); g.top.is_not 0
|
230
|
+
h = Digits.new(4); h.top.is_not 0
|
231
|
+
i = Digits.new(3)
|
232
|
+
j = Digits.new(4)
|
233
|
+
x, y, z, w = (0..3).map { Digit.new }
|
234
|
+
x.is_not 0
|
235
|
+
|
236
|
+
c.is a * x; d[0, 2].is b[0, 4] - c; d[0, 2].less a
|
237
|
+
e.is a * 7; d[2].is b[4]; f[0, 3].is d - e; f[0, 3].less a
|
238
|
+
g.is a * y; f[3].is b[5]; h[0, 2].is f - g; h[0, 2].less a
|
239
|
+
z.is 0; h[0, 3].less a
|
240
|
+
h.is a * w; h[2].is b[6]; h[3].is b[7]
|
241
|
+
|
242
|
+
$solver.solve
|
243
|
+
|
244
|
+
puts "answer:"
|
245
|
+
puts str.
|
246
|
+
gsub(/a+/, a.to_i.to_s).
|
247
|
+
gsub(/b+/, b.to_i.to_s).
|
248
|
+
gsub(/c+/, c.to_i.to_s).
|
249
|
+
gsub(/d+/, d.to_i.to_s).
|
250
|
+
gsub(/e+/, e.to_i.to_s).
|
251
|
+
gsub(/f+/, f.to_i.to_s).
|
252
|
+
gsub(/g+/, g.to_i.to_s).
|
253
|
+
gsub(/h+/, h.to_i.to_s).
|
254
|
+
gsub(/x+/, x.to_i.to_s).
|
255
|
+
gsub(/y+/, y.to_i.to_s).
|
256
|
+
gsub(/z+/, z.to_i.to_s).
|
257
|
+
gsub(/w+/, w.to_i.to_s)
|
258
|
+
puts
|
259
|
+
puts
|
260
|
+
|
261
|
+
|
262
|
+
###############################################################################
|
263
|
+
|
264
|
+
|
265
|
+
$solver = MiniSat::Solver.new
|
266
|
+
|
267
|
+
str = <<END
|
268
|
+
send
|
269
|
+
+ more
|
270
|
+
------
|
271
|
+
money
|
272
|
+
END
|
273
|
+
puts "problem:"
|
274
|
+
puts str
|
275
|
+
puts
|
276
|
+
|
277
|
+
as = Digits.new(4); as.top.is_not 0
|
278
|
+
bs = Digits.new(4); bs.top.is_not 0
|
279
|
+
cs = Digits.new(5); cs.top.is_not 0
|
280
|
+
|
281
|
+
h = {}
|
282
|
+
|
283
|
+
as.digits.reverse.zip(%w(s e n d)) {|d, c| (h[c] ||= []) << d }
|
284
|
+
bs.digits.reverse.zip(%w(m o r e)) {|d, c| (h[c] ||= []) << d }
|
285
|
+
cs.digits.reverse.zip(%w(m o n e y)) {|d, c| (h[c] ||= []) << d }
|
286
|
+
cs.is as + bs
|
287
|
+
|
288
|
+
h.each do |c, ds|
|
289
|
+
ds[1..-1].each {|d| ds.first.is d }
|
290
|
+
h.each do |c2, ds2|
|
291
|
+
next if c == c2
|
292
|
+
ds2.each {|d2| ds.first.is_not d2 }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
$solver.solve
|
297
|
+
|
298
|
+
puts "answer:"
|
299
|
+
h.each {|c, d| str = str.gsub(c) { d.first.to_i } }
|
300
|
+
puts str
|
301
|
+
puts
|
302
|
+
puts
|
@@ -0,0 +1,254 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ruby-minisat example -- nonogram.rb
|
4
|
+
# ref: http://en.wikipedia.org/wiki/Nonogram
|
5
|
+
|
6
|
+
##
|
7
|
+
## SAT configuration:
|
8
|
+
## - Variables: assign variables to each cell and each number in rule (below)
|
9
|
+
## - Clauses: sudoku rules (below)
|
10
|
+
##
|
11
|
+
##
|
12
|
+
## Variables:
|
13
|
+
## - one variable to each cell
|
14
|
+
## - n * m variables to each rule (length n)
|
15
|
+
## - or, m variables to each number
|
16
|
+
## - where m = n - rule.sum - rule.size + 2
|
17
|
+
##
|
18
|
+
## example: rule is [1, 2], and six cells
|
19
|
+
## - cells : a, b, c, d, e, f
|
20
|
+
## - number 1: p, q, r
|
21
|
+
## - number 2: s, t, u
|
22
|
+
##
|
23
|
+
## abcdef pqr stu
|
24
|
+
## 1.22.. <=> 1.. 2..
|
25
|
+
## 1..22. <=> 1.. .2.
|
26
|
+
## 1...22 <=> 1.. ..2
|
27
|
+
## .1.22. <=> .1. .2.
|
28
|
+
## .1..22 <=> .1. ..2
|
29
|
+
## ..1.22 <=> ..1 ..2
|
30
|
+
##
|
31
|
+
## # p means `1 block starts from a', q means b, and r means c
|
32
|
+
## # s means `2 blocks starts from c', t means d, and u means e
|
33
|
+
##
|
34
|
+
##
|
35
|
+
## Constraints:
|
36
|
+
##
|
37
|
+
## - constraint type 1. exact one variable in each number is true
|
38
|
+
## - constraint type 2. order of rule variables
|
39
|
+
## - constraint type 3. correspondence between cell variables and rule ones
|
40
|
+
##
|
41
|
+
## for above example:
|
42
|
+
##
|
43
|
+
## - constraint type 1.
|
44
|
+
## - p, q, r <- exact one
|
45
|
+
## - s, t, u <- exact one
|
46
|
+
##
|
47
|
+
## - constraint type 2.
|
48
|
+
## - q => t or u
|
49
|
+
## - r => u
|
50
|
+
## - s => p (option)
|
51
|
+
## - t => p or q (option)
|
52
|
+
##
|
53
|
+
## - constraint type 3.
|
54
|
+
## a b c d e f
|
55
|
+
## - a <=> p
|
56
|
+
## - b <=> q
|
57
|
+
## - c <=> r|s
|
58
|
+
## - d <=> s|t
|
59
|
+
## - e <=> t|u
|
60
|
+
## - f <=> u
|
61
|
+
##
|
62
|
+
## Encoding:
|
63
|
+
## - encoding `if': a => x1 or x2 or ... or xn:
|
64
|
+
## (-a | x1 | x2 | ... | xn)
|
65
|
+
##
|
66
|
+
## - encoding `iff': a <=> x1 or x2 or ... or xn:
|
67
|
+
## (a | -x1) & (a | -x2) & ... & (a | -xn) & (-a | x1 | x2 | ... | xn)
|
68
|
+
##
|
69
|
+
|
70
|
+
require "minisat"
|
71
|
+
require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
|
72
|
+
|
73
|
+
|
74
|
+
def error(msg)
|
75
|
+
$stderr.puts msg
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def parse_file(file)
|
81
|
+
lines = []
|
82
|
+
base = nil
|
83
|
+
rows = []
|
84
|
+
File.read(file).split(/\n/).each do |line|
|
85
|
+
next if line[/^\s*#/]
|
86
|
+
lines << line
|
87
|
+
next unless line.index(".")
|
88
|
+
|
89
|
+
rows << line.split.map {|x| x.to_i }.select {|x| x > 0 }
|
90
|
+
|
91
|
+
ary = []
|
92
|
+
line.scan(/\s*\./) {|s| ary << [$`.size, s.size] }
|
93
|
+
base ||= ary
|
94
|
+
error "corrupt field" unless base == ary
|
95
|
+
end
|
96
|
+
|
97
|
+
cols = base.map { [] }
|
98
|
+
lines.each do |line|
|
99
|
+
cols.zip(base) do |c, (i, j)|
|
100
|
+
n = line[i, j].to_i
|
101
|
+
c << n if n > 0
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
[rows, cols]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def define_sat(solver, rows, cols)
|
110
|
+
# define variables
|
111
|
+
v_cells = rows.map { cols.map { solver.new_var } }
|
112
|
+
v_rows = rows.map {|r| define_rule_vars(solver, cols.size, r) }
|
113
|
+
v_cols = cols.map {|r| define_rule_vars(solver, rows.size, r) }
|
114
|
+
|
115
|
+
# define constraints type 1 and 2
|
116
|
+
(v_rows + v_cols).each do |r|
|
117
|
+
define_constraint_type_1(solver, r)
|
118
|
+
define_constraint_type_2(solver, r)
|
119
|
+
end
|
120
|
+
|
121
|
+
# define constraints type 3
|
122
|
+
v_cells.zip(rows, v_rows) do |l, n, r|
|
123
|
+
define_constraint_type_3(solver, l, n, r)
|
124
|
+
end
|
125
|
+
v_cells.transpose.zip(cols, v_cols) do |l, n, r|
|
126
|
+
define_constraint_type_3(solver, l, n, r)
|
127
|
+
end
|
128
|
+
|
129
|
+
[v_cells, v_rows, v_cols]
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
# define rule variables : 6 -> [1, 2] -> [p, q, r]
|
134
|
+
def define_rule_vars(solver, size, rule)
|
135
|
+
num = size - rule.inject(0) {|z, x| z + x + 1 } + 2
|
136
|
+
rule.map { (0...num).map { solver.new_var } }
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# constraint type 1. exact one variable in each number is true
|
141
|
+
def define_constraint_type_1(solver, r)
|
142
|
+
# r : [[p,q,r], [s,t,u]]
|
143
|
+
r.each do |r2|
|
144
|
+
solver << r2
|
145
|
+
r2.combination(2) {|v1, v2| solver << [-v1, -v2] }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# define constraint type 2: order of rule variables
|
151
|
+
def define_constraint_type_2(solver, r)
|
152
|
+
# r : [[p,q,r], [s,t,u]]
|
153
|
+
r.each_cons(2) do |r2, r3|
|
154
|
+
(1...r2.size).each do |i|
|
155
|
+
solver << ([-r2[i]] + r3[i..-1])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
# define constraint type 3: correspondence between cell variables and rule ones
|
162
|
+
def define_constraint_type_3(solver, l, n, r)
|
163
|
+
# l : a,b,c,d,e,f
|
164
|
+
# n : [1, 2]
|
165
|
+
# r : [[p,q,r], [s,t,u]]
|
166
|
+
t = l.map { [] }
|
167
|
+
r.each_with_index do |r2, i|
|
168
|
+
off = n[0, i].inject(0) {|z, x| z + x + 1 }
|
169
|
+
n[i].times do |j|
|
170
|
+
r2.each_with_index do |v, k|
|
171
|
+
t[off + j + k] |= [v]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end; t
|
175
|
+
# t : [[p], [q], [r,s], [s,t], [t,u], [u]]
|
176
|
+
l.zip(t) do |v, c|
|
177
|
+
c.each {|v2| solver << [v, -v2] }
|
178
|
+
solver << ([-v] + c)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def solve_sat(solver)
|
184
|
+
start = Time.now
|
185
|
+
result = solver.solve
|
186
|
+
eplise = Time.now - start
|
187
|
+
puts "time: %.6f sec." % eplise
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def make_solution(solver, v_cells, v_rows, v_cols)
|
193
|
+
v_cells.map {|l| l.map {|v| solver[v] } }
|
194
|
+
end
|
195
|
+
|
196
|
+
def output_field(field, rows, cols)
|
197
|
+
w = rows.map {|cs| cs.size }.max
|
198
|
+
r = rows.map do |rs|
|
199
|
+
([" "] * w + rs.map {|r| r.to_s.rjust(2) }).last(w).join
|
200
|
+
end
|
201
|
+
h = cols.map {|cs| cs.size }.max
|
202
|
+
c = cols.map do |cs|
|
203
|
+
([" "] * h + cs.map {|c| c.to_s.rjust(2) }).last(h)
|
204
|
+
end.transpose.map {|l| " " * (r.first.size + 1) + l.join }
|
205
|
+
puts c
|
206
|
+
field.zip(r) do |line, prefix|
|
207
|
+
puts prefix + " " + line.map {|x| x ? "##" : " ." }.join
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
def add_constraint(solver, vars)
|
213
|
+
solver << vars.flatten.map {|v| solver[v] ? -v : v }
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
error "usage: nonogram.rb nonogram.sample" if ARGV.empty?
|
219
|
+
|
220
|
+
ARGV.each do |file|
|
221
|
+
nonogram = parse_file(file)
|
222
|
+
|
223
|
+
solver = MiniSat::Solver.new
|
224
|
+
|
225
|
+
puts "defining SAT..."
|
226
|
+
vars = define_sat(solver, *nonogram)
|
227
|
+
|
228
|
+
puts "solving SAT..."
|
229
|
+
result = solve_sat(solver)
|
230
|
+
puts "result: " + (result ? "solvable" : "unsolvable")
|
231
|
+
puts
|
232
|
+
next unless result
|
233
|
+
|
234
|
+
puts "translating model into solution..."
|
235
|
+
solution = make_solution(solver, *vars)
|
236
|
+
puts "solution found:"
|
237
|
+
output_field(solution, *nonogram)
|
238
|
+
puts
|
239
|
+
|
240
|
+
puts "checking different solution..."
|
241
|
+
add_constraint(solver, vars)
|
242
|
+
result = solve_sat(solver)
|
243
|
+
puts "result: " +
|
244
|
+
(result ? "different solution found" : "different solution not found")
|
245
|
+
puts
|
246
|
+
next unless result
|
247
|
+
|
248
|
+
puts "translating model into solution..."
|
249
|
+
solution = make_solution(solver, *vars)
|
250
|
+
puts "different solution:"
|
251
|
+
output_field(solution, *nonogram)
|
252
|
+
puts
|
253
|
+
puts
|
254
|
+
end
|