z3 0.0.20180624 → 0.0.20211213
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -4
- data/Rakefile +15 -8
- data/examples/abc_path +187 -0
- data/examples/abc_path-1.txt +7 -0
- data/examples/algebra_problems +12 -12
- data/examples/aquarium +133 -0
- data/examples/aquarium-1.txt +11 -0
- data/examples/bridges +2 -2
- data/examples/cats_organized_neatly +133 -0
- data/examples/cats_organized_neatly-10.txt +15 -0
- data/examples/cats_organized_neatly-3.txt +8 -0
- data/examples/cats_organized_neatly-48.txt +32 -0
- data/examples/circuit_problems +4 -4
- data/examples/clogic_puzzle +2 -2
- data/examples/color_nonogram +150 -0
- data/examples/color_nonogram-1.txt +23 -0
- data/examples/crossflip +2 -4
- data/examples/dominion +153 -0
- data/examples/dominion-1.txt +8 -0
- data/examples/dominosa +133 -0
- data/examples/dominosa-1.txt +8 -0
- data/examples/eulero +99 -0
- data/examples/eulero-1.txt +5 -0
- data/examples/four_hackers_puzzle +2 -2
- data/examples/futoshiki +128 -0
- data/examples/futoshiki-1.txt +17 -0
- data/examples/kakurasu +73 -0
- data/examples/kakurasu-1.txt +2 -0
- data/examples/kakuro +2 -2
- data/examples/killer_sudoku +88 -0
- data/examples/killer_sudoku-1.txt +17 -0
- data/examples/killer_sudoku-2.txt +53 -0
- data/examples/kinematics_problems +20 -20
- data/examples/knights_puzzle +2 -2
- data/examples/kropki +100 -0
- data/examples/kropki-1.txt +13 -0
- data/examples/letter_connections +2 -2
- data/examples/light_up +2 -2
- data/examples/minisudoku +2 -2
- data/examples/miracle_sudoku +135 -0
- data/examples/miracle_sudoku-1.txt +9 -0
- data/examples/mortal_coil_puzzle +2 -2
- data/examples/nanro +245 -0
- data/examples/nanro-1.txt +8 -0
- data/examples/nine_clocks +106 -0
- data/examples/nonogram +2 -2
- data/examples/pyramid_nonogram +2 -2
- data/examples/regexp_crossword_solver +2 -2
- data/examples/regexp_solver +2 -2
- data/examples/renzoku +124 -0
- data/examples/renzoku-1.txt +17 -0
- data/examples/sandwich_sudoku +101 -0
- data/examples/sandwich_sudoku-1.txt +10 -0
- data/examples/selfref +2 -2
- data/examples/simple_regexp_parser.rb +58 -56
- data/examples/skyscrapers +118 -0
- data/examples/skyscrapers-1.txt +6 -0
- data/examples/skyscrapers-2.txt +11 -0
- data/examples/star_battle +134 -0
- data/examples/star_battle-1.txt +10 -0
- data/examples/stitches +180 -0
- data/examples/stitches-1.txt +11 -0
- data/examples/sudoku +2 -2
- data/examples/suguru +199 -0
- data/examples/suguru-1.txt +17 -0
- data/examples/verbal_arithmetic +2 -2
- data/examples/yajilin +268 -0
- data/examples/yajilin-1.txt +10 -0
- data/lib/z3/ast.rb +8 -0
- data/lib/z3/expr/bitvec_expr.rb +10 -0
- data/lib/z3/expr/expr.rb +16 -15
- data/lib/z3/low_level.rb +6 -2
- data/lib/z3/low_level_auto.rb +180 -36
- data/lib/z3/optimize.rb +5 -4
- data/lib/z3/printer.rb +29 -1
- data/lib/z3/sort/bitvec_sort.rb +1 -0
- data/lib/z3/sort/sort.rb +9 -5
- data/lib/z3/very_low_level.rb +8 -5
- data/lib/z3/very_low_level_auto.rb +45 -9
- data/spec/bitvec_expr_spec.rb +35 -21
- data/spec/bitvec_sort_spec.rb +4 -0
- data/spec/expr_spec.rb +62 -0
- data/spec/integration/abc_path_spec.rb +21 -0
- data/spec/integration/aquarium_spec.rb +27 -0
- data/spec/integration/cats_organized_neatly_spec.rb +14 -0
- data/spec/integration/color_nonogram_spec.rb +28 -0
- data/spec/integration/dominion_spec.rb +14 -0
- data/spec/integration/dominosa_spec.rb +21 -0
- data/spec/integration/eulero_spec.rb +11 -0
- data/spec/integration/futoshiki_spec.rb +23 -0
- data/spec/integration/kakurasu_spec.rb +18 -0
- data/spec/integration/killer_sudoku_spec.rb +10 -0
- data/spec/integration/knights_puzzle_spec.rb +11 -11
- data/spec/integration/kropki_spec.rb +19 -0
- data/spec/integration/miracle_sudoku_spec.rb +15 -0
- data/spec/integration/mortal_coil_puzzle_spec.rb +8 -6
- data/spec/integration/nanro_spec.rb +39 -0
- data/spec/integration/nine_clocks_spec.rb +30 -0
- data/spec/integration/oneofus_spec.rb +7 -15
- data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
- data/spec/integration/renzoku_spec.rb +23 -0
- data/spec/integration/sandwich_sudoku_spec.rb +15 -0
- data/spec/integration/skyscraper_spec.rb +10 -0
- data/spec/integration/star_battle_spec.rb +27 -0
- data/spec/integration/stitches_spec.rb +25 -0
- data/spec/integration/suguru_spec.rb +23 -0
- data/spec/integration/yajilin_spec.rb +25 -0
- data/spec/interface_spec.rb +18 -0
- data/spec/optimize_spec.rb +6 -4
- data/spec/printer_spec.rb +30 -0
- data/spec/set_expr_spec.rb +14 -8
- data/spec/solver_spec.rb +4 -5
- data/spec/spec_helper.rb +15 -0
- metadata +104 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c73d508e9644e0c32681754a8f33959bc54d838c21d13f896223225e8b275a76
|
4
|
+
data.tar.gz: 12e017b721b97aaec2d8e1a75145f0c5d3f0769eee2badc89e08e2b0ff11fef1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4993e91a238ad4e1c70e78bfe12301e1bd83759d9ad610fca64f89267868b9c8959c6e51ba98f175836235702c66f9cde8b19d73ddcd2cb760da75aed1f564d
|
7
|
+
data.tar.gz: cca1389567fb61df3d4dc2fa6281b833edcc3c3a2c5872fec2b71089c9f9c927ba0d3e2d164127d5be167c2bb1cc4fd86f2360718631e0a8fbc473bda68ec90b
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
This is Ruby interface for Z3 [ https://github.com/Z3Prover/z3 ].
|
2
2
|
|
3
|
-
Minimum required version is Z3 4.
|
3
|
+
Minimum required version is Z3 4.8.
|
4
4
|
|
5
5
|
It's in very early stages of development. Pull requests always welcome.
|
6
6
|
|
@@ -12,7 +12,7 @@ You can use most Ruby operators to construct Z3 expressions, but use `| &` inste
|
|
12
12
|
|
13
13
|
The interface is potentially unstable, and can change in the future.
|
14
14
|
|
15
|
-
`Z3::
|
15
|
+
`Z3::VeryLowLevel` and `Z3::LowLevel` are FFI interfaces for internal use, and they shouldn't be used directly. Also don't use any method starting with `_`. Doing this is likely to lead to segmentation faults unless extreme care is taken.
|
16
16
|
|
17
17
|
A utility at `api/gen_api` will loop through a .h file and generate Ruby definitions. This will update the API when upstream changes `z3_api.h`
|
18
18
|
|
@@ -25,8 +25,6 @@ bundle install
|
|
25
25
|
rake spec
|
26
26
|
```
|
27
27
|
|
28
|
-
*NB: On Linux, since FFI will look for `libz3.so`, you might need to install `libz3-dev`using your usual package manager.*
|
29
|
-
|
30
28
|
### Known Issues
|
31
29
|
|
32
30
|
As Z3 is a C library, doing anything weird with it will segfault your process. Ruby API tries its best to prevent such problems and turn them into exceptions instead, but if you do anything weird (especially touch any method prefixed with `_` or `Z3::LowLevel` interface), crashes are possible. If you have reproducible crash on reasonable looking code, definitely submit it as a bug, and I'll try to come up with a workaround.
|
data/Rakefile
CHANGED
@@ -5,45 +5,52 @@ task "test" => "spec"
|
|
5
5
|
task "test:integration" => "spec:integration"
|
6
6
|
task "test:unit" => "spec:unit"
|
7
7
|
|
8
|
+
desc "Regenerate api/definitions.h"
|
9
|
+
task "definitions" do
|
10
|
+
headers = `brew list -v z3`.split("\n").grep(/\.h\z/)
|
11
|
+
# Need to bump it to latest version
|
12
|
+
sh "./api/gen_definitions", *headers
|
13
|
+
end
|
14
|
+
|
8
15
|
desc "Regenerate API"
|
9
16
|
task "api" do
|
10
|
-
|
17
|
+
sh "./api/gen_api api/definitions.h"
|
11
18
|
end
|
12
19
|
|
13
20
|
desc "Clean up"
|
14
21
|
task "clean" do
|
15
|
-
|
22
|
+
sh "trash z3-*.gem coverage"
|
16
23
|
end
|
17
24
|
|
18
25
|
desc "Run tests"
|
19
26
|
task "spec" do
|
20
|
-
|
27
|
+
sh "rspec"
|
21
28
|
end
|
22
29
|
|
23
30
|
desc "Run unit tests"
|
24
31
|
task "spec:unit" do
|
25
|
-
|
32
|
+
sh "rspec spec/*_spec.rb"
|
26
33
|
end
|
27
34
|
|
28
35
|
desc "Run integration tests"
|
29
36
|
task "spec:integration" do
|
30
|
-
|
37
|
+
sh "rspec spec/integration/*_spec.rb"
|
31
38
|
end
|
32
39
|
|
33
40
|
desc "Build gem"
|
34
41
|
task "gem:build" do
|
35
|
-
|
42
|
+
sh "gem build z3.gemspec"
|
36
43
|
end
|
37
44
|
|
38
45
|
desc "Upload gem"
|
39
46
|
task "gem:push" => "gem:build" do
|
40
47
|
gem_file = Dir["z3-*.gem"][-1] or raise "No gem found"
|
41
|
-
|
48
|
+
sh "gem", "push", gem_file
|
42
49
|
end
|
43
50
|
|
44
51
|
desc "Report missing APIs"
|
45
52
|
task "coverage:missing" do
|
46
|
-
|
53
|
+
sh "COVERAGE=1 rake test"
|
47
54
|
data = JSON.load(open("coverage/.resultset.json"))["RSpec"]["coverage"]
|
48
55
|
lla_path = data.keys.find{|k| k.end_with?("lib/z3/low_level_auto.rb")}
|
49
56
|
coverage = data[lla_path].zip(File.readlines(lla_path).map(&:strip))
|
data/examples/abc_path
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "set"
|
5
|
+
require_relative "../lib/z3"
|
6
|
+
|
7
|
+
# https://www.janko.at/Raetsel/Abc-Pfad/index.htm
|
8
|
+
|
9
|
+
class ABCPath
|
10
|
+
def initialize(path)
|
11
|
+
@data = Pathname(path).readlines.map(&:chomp).map{|line| line.split.map{|c| c == "." ? nil : c}}
|
12
|
+
raise unless @data.size == 7
|
13
|
+
raise unless @data.all?{|line| line.size == 7}
|
14
|
+
@solver = Z3::Solver.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def var(x,y)
|
18
|
+
return unless on_board?(x,y)
|
19
|
+
Z3.Int("c#{x},#{y}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def each_xy
|
23
|
+
5.times do |y|
|
24
|
+
5.times do |x|
|
25
|
+
yield(x,y)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_board?(x,y)
|
31
|
+
x >= 0 and y >= 0 and x <= 4 and y <= 4
|
32
|
+
end
|
33
|
+
|
34
|
+
def neighbours(x,y)
|
35
|
+
[
|
36
|
+
[x-1, y-1],
|
37
|
+
[x , y-1],
|
38
|
+
[x+1, y-1],
|
39
|
+
[x-1, y ],
|
40
|
+
[x+1, y ],
|
41
|
+
[x-1, y+1],
|
42
|
+
[x , y+1],
|
43
|
+
[x+1, y+1],
|
44
|
+
].select{|nx,ny| on_board?(nx,ny)}
|
45
|
+
end
|
46
|
+
|
47
|
+
def col_vars(x)
|
48
|
+
(0..4).map{|y| var(x,y)}
|
49
|
+
end
|
50
|
+
|
51
|
+
def row_vars(y)
|
52
|
+
(0..4).map{|x| var(x,y)}
|
53
|
+
end
|
54
|
+
|
55
|
+
def diag_vars
|
56
|
+
(0..4).map{|x| var(x,x)}
|
57
|
+
end
|
58
|
+
|
59
|
+
def counter_diag_vars
|
60
|
+
(0..4).map{|x| var(x,4-x)}
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_letters
|
64
|
+
each_xy do |x,y|
|
65
|
+
@solver.assert var(x,y) >= 0
|
66
|
+
@solver.assert var(x,y) <= 24
|
67
|
+
end
|
68
|
+
@solver.assert Z3.Distinct(*enum_for(:each_xy).map{|x,y| var(x,y)})
|
69
|
+
end
|
70
|
+
|
71
|
+
def assert_path
|
72
|
+
each_xy do |x,y|
|
73
|
+
@solver.assert Z3.Or((var(x,y) == 0), *neighbours(x,y).map{|nx,ny| var(x,y) == var(nx,ny) + 1 })
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def assert_has_letter(vars, letter)
|
78
|
+
u = letter.ord - 65
|
79
|
+
@solver.assert Z3.Or(*vars.map{|v| v == u})
|
80
|
+
end
|
81
|
+
|
82
|
+
def assert_cols
|
83
|
+
5.times do |x|
|
84
|
+
assert_has_letter col_vars(x), @data[0][x+1]
|
85
|
+
assert_has_letter col_vars(x), @data[6][x+1]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def assert_rows
|
90
|
+
5.times do |y|
|
91
|
+
assert_has_letter row_vars(y), @data[y+1][0]
|
92
|
+
assert_has_letter row_vars(y), @data[y+1][6]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def assert_diagonals
|
97
|
+
assert_has_letter diag_vars, @data[0][0]
|
98
|
+
assert_has_letter diag_vars, @data[6][6]
|
99
|
+
assert_has_letter counter_diag_vars, @data[0][6]
|
100
|
+
assert_has_letter counter_diag_vars, @data[6][0]
|
101
|
+
end
|
102
|
+
|
103
|
+
def call
|
104
|
+
assert_letters
|
105
|
+
assert_path
|
106
|
+
assert_cols
|
107
|
+
assert_rows
|
108
|
+
assert_diagonals
|
109
|
+
|
110
|
+
if @solver.satisfiable?
|
111
|
+
@model = @solver.model
|
112
|
+
print_solution
|
113
|
+
else
|
114
|
+
puts "There is no solution"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def connected?(x1,y1,x2,y2)
|
119
|
+
v1 = var(x1,y1)
|
120
|
+
v2 = var(x2,y2)
|
121
|
+
return false unless v1 and v2
|
122
|
+
c1 = @model[v1].to_i
|
123
|
+
c2 = @model[v2].to_i
|
124
|
+
(c1-c2).abs == 1
|
125
|
+
end
|
126
|
+
|
127
|
+
def print_cell(x,y)
|
128
|
+
if on_board?(x,y)
|
129
|
+
c = @model[var(x,y)].to_i
|
130
|
+
print (65+32+c).chr
|
131
|
+
elsif x == -1 or y == -1 or x == 5 or y == 5
|
132
|
+
print @data[y+1][x+1]
|
133
|
+
else
|
134
|
+
print " "
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_hcon(x,y)
|
139
|
+
if connected?(x,y,x+1,y)
|
140
|
+
print "-"
|
141
|
+
else
|
142
|
+
print " "
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def print_vcon(x,y)
|
147
|
+
if connected?(x,y,x,y+1)
|
148
|
+
print "|"
|
149
|
+
else
|
150
|
+
print " "
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def print_xcon(x,y)
|
155
|
+
d = connected?(x,y,x+1,y+1)
|
156
|
+
cd = connected?(x,y+1,x+1,y)
|
157
|
+
if d and cd
|
158
|
+
print "x"
|
159
|
+
elsif d
|
160
|
+
print "\\"
|
161
|
+
elsif cd
|
162
|
+
print "/"
|
163
|
+
else
|
164
|
+
print " "
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def print_solution
|
169
|
+
(-1..5).each do |y|
|
170
|
+
(-1..5).each do |x|
|
171
|
+
print_cell(x,y)
|
172
|
+
print_hcon(x,y)
|
173
|
+
end
|
174
|
+
print "\n"
|
175
|
+
|
176
|
+
break if y == 5
|
177
|
+
(-1..5).each do |x|
|
178
|
+
print_vcon(x,y)
|
179
|
+
print_xcon(x,y)
|
180
|
+
end
|
181
|
+
print "\n"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
path = ARGV[0] || Pathname(__dir__) + "abc_path-1.txt"
|
187
|
+
ABCPath.new(path).call
|
data/examples/algebra_problems
CHANGED
@@ -21,7 +21,7 @@ class AlgebraProblem
|
|
21
21
|
end
|
22
22
|
|
23
23
|
class AlgebraProblem01 < AlgebraProblem
|
24
|
-
def
|
24
|
+
def call
|
25
25
|
x = Z3.Real("x")
|
26
26
|
solver.assert 5*(-3*x - 2) - (x - 3) == -4*(4*x + 5) + 13
|
27
27
|
print_solution! "01"
|
@@ -37,7 +37,7 @@ class AlgebraProblem03 < AlgebraProblem
|
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
40
|
+
def call
|
41
41
|
x = Z3.Real("x")
|
42
42
|
xm2abs = Z3.Real("|x-2|")
|
43
43
|
declare_abs(xm2abs, x-2)
|
@@ -55,7 +55,7 @@ class AlgebraProblem04 < AlgebraProblem
|
|
55
55
|
solver.assert a_b**2 == (ax-bx)**2 + (ay-by)**2
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def call
|
59
59
|
ax = Z3.Real("ax")
|
60
60
|
ay = Z3.Real("ay")
|
61
61
|
bx = Z3.Real("bx")
|
@@ -71,7 +71,7 @@ class AlgebraProblem04 < AlgebraProblem
|
|
71
71
|
end
|
72
72
|
|
73
73
|
class AlgebraProblem05 < AlgebraProblem
|
74
|
-
def
|
74
|
+
def call
|
75
75
|
x = Z3.Real("x")
|
76
76
|
y = Z3.Real("y")
|
77
77
|
solver.assert 2*x - 4*y == 9
|
@@ -85,7 +85,7 @@ class AlgebraProblem06 < AlgebraProblem
|
|
85
85
|
solver.assert y == 6*x + 1
|
86
86
|
end
|
87
87
|
|
88
|
-
def
|
88
|
+
def call
|
89
89
|
x1 = Z3.Real("x1")
|
90
90
|
y1 = Z3.Real("y1")
|
91
91
|
x2 = Z3.Real("x2")
|
@@ -108,7 +108,7 @@ class AlgebraProblem10 < AlgebraProblem
|
|
108
108
|
)
|
109
109
|
end
|
110
110
|
|
111
|
-
def
|
111
|
+
def call
|
112
112
|
x = Z3.Real("x")
|
113
113
|
y = Z3.Real("|-2x + 2|")
|
114
114
|
declare_abs(y, -2*x + 2)
|
@@ -117,12 +117,12 @@ class AlgebraProblem10 < AlgebraProblem
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
-
AlgebraProblem01.new.
|
121
|
-
AlgebraProblem03.new.
|
122
|
-
AlgebraProblem04.new.
|
123
|
-
AlgebraProblem05.new.
|
124
|
-
AlgebraProblem06.new.
|
125
|
-
AlgebraProblem10.new.
|
120
|
+
AlgebraProblem01.new.call
|
121
|
+
AlgebraProblem03.new.call
|
122
|
+
AlgebraProblem04.new.call
|
123
|
+
AlgebraProblem05.new.call
|
124
|
+
AlgebraProblem06.new.call
|
125
|
+
AlgebraProblem10.new.call
|
126
126
|
|
127
127
|
"""
|
128
128
|
http://www.analyzemath.com/Algebra1/Algebra1.html
|
data/examples/aquarium
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require_relative "../lib/z3"
|
5
|
+
|
6
|
+
class Aquarium
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).readlines.map(&:chomp).map(&:split)
|
9
|
+
@cols = data.shift.map(&:to_i)
|
10
|
+
@rows = data.map(&:shift).map(&:to_i)
|
11
|
+
@xsize = @cols.size
|
12
|
+
@ysize = @rows.size
|
13
|
+
raise unless data.all?{|row| row.size == @xsize}
|
14
|
+
@data = data
|
15
|
+
@solver = Z3::Solver.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
# How cells relate to containers
|
20
|
+
@ysize.times do |y|
|
21
|
+
@xsize.times do |x|
|
22
|
+
@solver.assert cell_var(x, y) == container_var(container_at(x, y), y)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Row sums
|
27
|
+
@ysize.times do |y|
|
28
|
+
sum = Z3.Add(*@xsize.times.map{|x| cell_var(x, y).ite(1, 0) })
|
29
|
+
@solver.assert sum == @rows[y]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Col sums
|
33
|
+
@xsize.times do |x|
|
34
|
+
sum = Z3.Add(*@ysize.times.map{|y| cell_var(x, y).ite(1, 0) })
|
35
|
+
@solver.assert sum == @cols[x]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Container water flow
|
39
|
+
containers.each do |name|
|
40
|
+
(0...@ysize).each do |y|
|
41
|
+
@solver.assert container_var(name, y).implies container_var(name, y+1)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if @solver.satisfiable?
|
46
|
+
@model = @solver.model
|
47
|
+
print_answer
|
48
|
+
else
|
49
|
+
puts "failed to solve"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def containers
|
56
|
+
@containers ||= @data.flatten.uniq.sort
|
57
|
+
end
|
58
|
+
|
59
|
+
def container_var(name, level)
|
60
|
+
Z3.Bool("b#{name},#{level}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def cell_var(x, y)
|
64
|
+
return nil unless (0...@xsize).include?(x)
|
65
|
+
return nil unless (0...@ysize).include?(y)
|
66
|
+
Z3.Bool("c#{x},#{y}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def container_at(x, y)
|
70
|
+
return nil unless (0...@xsize).include?(x)
|
71
|
+
return nil unless (0...@ysize).include?(y)
|
72
|
+
@data[y][x]
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_corner(x, y)
|
76
|
+
if [
|
77
|
+
container_at(x, y),
|
78
|
+
container_at(x, y-1),
|
79
|
+
container_at(x-1, y),
|
80
|
+
container_at(x-1, y-1),
|
81
|
+
].uniq.size == 1
|
82
|
+
print " "
|
83
|
+
else
|
84
|
+
print "+"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def print_vertical(x, y)
|
89
|
+
if x == 0 or container_at(x, y) != container_at(x-1, y)
|
90
|
+
print "|"
|
91
|
+
else
|
92
|
+
print " "
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_horizontal(x, y)
|
97
|
+
if y == 0 or container_at(x, y) != container_at(x, y-1)
|
98
|
+
print "-"
|
99
|
+
else
|
100
|
+
print " "
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_cell(x, y)
|
105
|
+
if @model[cell_var(x, y)].to_b
|
106
|
+
print "#"
|
107
|
+
else
|
108
|
+
print " "
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def print_answer
|
113
|
+
(0..@ysize).each do |y|
|
114
|
+
(0..@xsize).each do |x|
|
115
|
+
print_corner x, y
|
116
|
+
next if x == @xsize
|
117
|
+
print_horizontal x, y
|
118
|
+
end
|
119
|
+
print "\n"
|
120
|
+
|
121
|
+
next if y == @ysize
|
122
|
+
(0..@xsize).each do |x|
|
123
|
+
print_vertical x, y
|
124
|
+
next if x == @xsize
|
125
|
+
print_cell x, y
|
126
|
+
end
|
127
|
+
print "\n"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
path = ARGV[0] || Pathname(__dir__) + "aquarium-1.txt"
|
133
|
+
Aquarium.new(path).call
|
@@ -0,0 +1,11 @@
|
|
1
|
+
9 6 4 2 2 4 8 6 6 7
|
2
|
+
9 b b c c c c c d d e
|
3
|
+
6 b j n n n n c c d e
|
4
|
+
7 b j n i i h h g g f
|
5
|
+
4 b j n t t o o g g f
|
6
|
+
1 b t t t t o o p p p
|
7
|
+
5 b t s t t m o p q q
|
8
|
+
4 b t s s s m o r q q
|
9
|
+
7 b t t s s m r r r q
|
10
|
+
3 a s s s s m r l k k
|
11
|
+
8 a a a s s m m l k k
|
data/examples/bridges
CHANGED
@@ -15,7 +15,7 @@ class Bridges
|
|
15
15
|
@solver = Z3::Solver.new
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def call
|
19
19
|
@vars = {}
|
20
20
|
(0...@xsize).each do |x|
|
21
21
|
(0...@ysize).each do |y|
|
@@ -122,4 +122,4 @@ class Bridges
|
|
122
122
|
end
|
123
123
|
|
124
124
|
path = ARGV[0] || Pathname(__dir__) + "bridges-1.txt"
|
125
|
-
Bridges.new(path).
|
125
|
+
Bridges.new(path).call
|
@@ -0,0 +1,133 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require_relative "../lib/z3"
|
5
|
+
|
6
|
+
class CatsOrganizedNeatly
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).read
|
9
|
+
|
10
|
+
@board, *@cats = data.split("\n\n").map{|n| n.lines.map(&:chomp)}
|
11
|
+
|
12
|
+
raise "Board and cats mismatch" unless @board.join.scan(/\S/).count == @cats.join.scan(/\S/).count
|
13
|
+
|
14
|
+
@ysize = @board.size
|
15
|
+
@xsize = @board.map(&:size).max
|
16
|
+
|
17
|
+
# Up to user to make them unique
|
18
|
+
@cat_labels = @cats.map{|c| c.join[/\S/]}
|
19
|
+
|
20
|
+
@solver = Z3::Solver.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_board_vars
|
24
|
+
@bvars = {}
|
25
|
+
@ysize.times do |y|
|
26
|
+
@xsize.times do |x|
|
27
|
+
next unless @board[y][x] == "."
|
28
|
+
@bvars[[x,y]] = Z3.Int("b#{x},#{y}")
|
29
|
+
@solver.assert @bvars[[x,y]] >= 0
|
30
|
+
@solver.assert @bvars[[x,y]] < @cats.size
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup_cats_vars
|
36
|
+
@cx = []
|
37
|
+
@cy = []
|
38
|
+
@co = []
|
39
|
+
@cats.each_with_index do |cat, i|
|
40
|
+
@cx[i] = Z3.Int("cx#{i}")
|
41
|
+
@cy[i] = Z3.Int("cy#{i}")
|
42
|
+
@co[i] = Z3.Int("co#{i}")
|
43
|
+
@solver.assert @cx[i] >= 0
|
44
|
+
@solver.assert @cx[i] <= (@xsize-1)
|
45
|
+
@solver.assert @cy[i] >= 0
|
46
|
+
@solver.assert @cy[i] <= (@ysize-1)
|
47
|
+
@solver.assert @co[i] >= 0
|
48
|
+
@solver.assert @co[i] <= 3
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def cat_to_shifts(cat)
|
53
|
+
result = []
|
54
|
+
cat.each_with_index do |line, y|
|
55
|
+
line.chars.each_with_index do |c, x|
|
56
|
+
next if c == " "
|
57
|
+
result << [x,y]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
def cat_at(shifts, x, y, i)
|
64
|
+
Z3.And(*shifts.map{|sx, sy|
|
65
|
+
v = @bvars[[sx+x,sy+y]]
|
66
|
+
return false unless v
|
67
|
+
v == i
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
71
|
+
def setup_cat_board_var(shifts0, i)
|
72
|
+
cx = @cx[i]
|
73
|
+
cy = @cy[i]
|
74
|
+
co = @co[i]
|
75
|
+
|
76
|
+
shiftss = [
|
77
|
+
shifts0,
|
78
|
+
shifts0.map{|x,y| [y,-x]},
|
79
|
+
shifts0.map{|x,y| [-x,-y]},
|
80
|
+
shifts0.map{|x,y| [-y,x]},
|
81
|
+
]
|
82
|
+
|
83
|
+
shiftss.each_with_index do |shifts, o|
|
84
|
+
@ysize.times do |y|
|
85
|
+
@ysize.times do |x|
|
86
|
+
@solver.assert(
|
87
|
+
Z3.And(cx == x, cy == y, co == o).implies(cat_at(shifts, x, y, i))
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def setup_cats_board_vars
|
95
|
+
@cats.each_with_index do |cat, i|
|
96
|
+
shifts = cat_to_shifts(cat)
|
97
|
+
setup_cat_board_var(shifts, i)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def call
|
102
|
+
# Each cell is just one of the cats
|
103
|
+
setup_board_vars
|
104
|
+
setup_cats_vars
|
105
|
+
setup_cats_board_vars
|
106
|
+
|
107
|
+
if @solver.satisfiable?
|
108
|
+
@model = @solver.model
|
109
|
+
print_answer!
|
110
|
+
else
|
111
|
+
binding.pry
|
112
|
+
puts "failed to solve"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def print_answer!
|
117
|
+
@ysize.times do |y|
|
118
|
+
@xsize.times do |x|
|
119
|
+
if @bvars[[x,y]]
|
120
|
+
cat_number = @model[@bvars[[x,y]]].to_i
|
121
|
+
print @cat_labels[cat_number]
|
122
|
+
else
|
123
|
+
print "."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
print "\n"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
path = ARGV[0] || Pathname(__dir__) + "cats_organized_neatly-48.txt"
|
133
|
+
CatsOrganizedNeatly.new(path).call
|