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.
- 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
|