z3 0.0.20160330 → 0.0.20160427
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.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/examples/{bridges_solver → bridges} +5 -12
- data/examples/bridges-1.txt +7 -0
- data/examples/{clogic_puzzle_solver → clogic_puzzle} +0 -0
- data/examples/four_hackers_puzzle +2 -1
- data/examples/{kakuro_solver → kakuro} +5 -15
- data/examples/kakuro-1.txt +10 -0
- data/examples/{knights_puzzle_solver → knights_puzzle} +4 -4
- data/examples/{letter_connections_solver → letter_connections} +6 -15
- data/examples/letter_connections-1.txt +10 -0
- data/examples/{light_up_solver → light_up} +5 -12
- data/examples/light_up-1.txt +7 -0
- data/examples/{minisudoku_solver → minisudoku} +8 -11
- data/examples/minisudoku-1.txt +6 -0
- data/examples/nonogram +152 -0
- data/examples/{selfref_solver → selfref} +0 -0
- data/examples/{sudoku_solver → sudoku} +9 -14
- data/examples/sudoku-1.txt +9 -0
- data/lib/z3.rb +10 -6
- data/lib/z3/ast.rb +33 -0
- data/lib/z3/{value/arith_value.rb → expr/arith_expr.rb} +3 -3
- data/lib/z3/{value/bitvec_value.rb → expr/bitvec_expr.rb} +1 -1
- data/lib/z3/{value/bool_value.rb → expr/bool_expr.rb} +5 -1
- data/lib/z3/{value/value.rb → expr/expr.rb} +7 -13
- data/lib/z3/expr/int_expr.rb +15 -0
- data/lib/z3/{value/int_value.rb → expr/real_expr.rb} +2 -2
- data/lib/z3/func_decl.rb +33 -23
- data/lib/z3/interface.rb +52 -30
- data/lib/z3/low_level.rb +10 -1
- data/lib/z3/low_level_auto.rb +83 -83
- data/lib/z3/model.rb +6 -5
- data/lib/z3/printer.rb +26 -0
- data/lib/z3/solver.rb +1 -5
- data/lib/z3/sort/bitvec_sort.rb +3 -3
- data/lib/z3/sort/bool_sort.rb +4 -4
- data/lib/z3/sort/int_sort.rb +2 -2
- data/lib/z3/sort/real_sort.rb +5 -5
- data/lib/z3/sort/sort.rb +22 -7
- data/lib/z3/very_low_level.rb +1 -1
- data/spec/bitvec_expr_spec.rb +55 -0
- data/spec/bitvec_sort_spec.rb +34 -0
- data/spec/bool_expr_spec.rb +65 -0
- data/spec/bool_sort_spec.rb +20 -0
- data/spec/{value_spec.rb → expr_spec.rb} +3 -3
- data/spec/int_expr_spec.rb +78 -0
- data/spec/int_sort_spec.rb +18 -0
- data/spec/integration/algebra_problems_spec.rb +19 -20
- data/spec/integration/basic_int_math_spec.rb +4 -5
- data/spec/integration/basic_logic_spec.rb +3 -4
- data/spec/integration/bit_tricks_spec.rb +2 -3
- data/spec/integration/bridges_spec.rb +2 -3
- data/spec/integration/four_hackers_puzzle_spec.rb +26 -0
- data/spec/integration/geometry_problem_spec.rb +10 -11
- data/spec/integration/kakuro_spec.rb +2 -3
- data/spec/integration/kinematics_problems_spec.rb +36 -37
- data/spec/integration/knights_puzzle_spec.rb +96 -0
- data/spec/integration/letter_connections_spec.rb +2 -3
- data/spec/integration/light_up_spec.rb +3 -4
- data/spec/integration/minisudoku_spec.rb +2 -3
- data/spec/integration/nonogram_spec.rb +26 -0
- data/spec/integration/selfref_spec.rb +2 -3
- data/spec/integration/sudoku_spec.rb +2 -3
- data/spec/integration/verbal_arithmetic_spec.rb +2 -3
- data/spec/model_spec.rb +13 -6
- data/spec/printer_spec.rb +22 -0
- data/spec/real_expr_spec.rb +64 -0
- data/spec/real_sort_spec.rb +24 -0
- data/spec/solver_spec.rb +11 -0
- data/spec/spec_helper.rb +39 -64
- metadata +81 -18
- data/lib/z3/value/real_value.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b373cf9a8caebd1c5a05420660ac2301528613eb
|
4
|
+
data.tar.gz: 4c45e3aa7326aeaefddee63a17c8a4eeb1fe57e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a2bd8245fcb32c67f0c609b477842a6f804fcc45d514f7d8d7a8ec047c8e6e70875167562aef1c51d04b637c900f001d4ad8e35368005f9aa3c7990239ab841
|
7
|
+
data.tar.gz: 73efd05c6292e8ef129c8a711a7de4b5a377c585991a11f4fe6930763c9e9e9ead23cc36fa98caba35966d9d7e5f897509ee7bb5bf003ff355d3ad2001b731f0
|
data/README.md
CHANGED
@@ -27,3 +27,7 @@ On other systems use appropriate package manager.
|
|
27
27
|
Ruby API tries to catch the most common mistakes, but if you use API in a weird way you can get C crash instead of nice Ruby exception.
|
28
28
|
|
29
29
|
Memory will leak a good deal. Generally avoid in long running processes.
|
30
|
+
|
31
|
+
### Python versions
|
32
|
+
|
33
|
+
Most of example solvers have Python versions available from https://github.com/taw/puzzle-solvers
|
@@ -1,9 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "pathname"
|
3
4
|
require_relative "../lib/z3"
|
4
5
|
|
5
6
|
class Bridges
|
6
|
-
def initialize(
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).read
|
7
9
|
data = data.strip.split("\n").map do |line|
|
8
10
|
line.split.map{|c| c == "_" ? nil : c.to_i}
|
9
11
|
end
|
@@ -119,14 +121,5 @@ class Bridges
|
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
122
|
-
|
123
|
-
|
124
|
-
3 _ _ 6 _ _ 4
|
125
|
-
_ 1 _ _ _ _ _
|
126
|
-
_ _ _ 4 _ 4 _
|
127
|
-
_ _ _ _ _ _ _
|
128
|
-
_ _ 1 _ 3 _ _
|
129
|
-
1 _ _ _ _ 2 _
|
130
|
-
_ 3 _ _ 5 _ 3
|
131
|
-
"""
|
132
|
-
).solve!
|
124
|
+
path = ARGV[0] || Pathname(__dir__) + "bridges-1.txt"
|
125
|
+
Bridges.new(path).solve!
|
File without changes
|
@@ -62,8 +62,9 @@ class LogicPuzzle
|
|
62
62
|
add_assertions!
|
63
63
|
if @solver.check == :sat
|
64
64
|
@solver.model.each do |k,v|
|
65
|
+
k = k.to_s.gsub("|", "")
|
65
66
|
i, name = k.split("-", 2)
|
66
|
-
puts "#{k} = #{@dict[name][v.to_i]}"
|
67
|
+
puts "#{k} = #{@dict[name][v.to_s.to_i]}"
|
67
68
|
end
|
68
69
|
else
|
69
70
|
puts "Puzzle has no solutions"
|
@@ -10,10 +10,12 @@ class Hash
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
require "pathname"
|
13
14
|
require_relative "../lib/z3"
|
14
15
|
|
15
16
|
class Kakuro
|
16
|
-
def initialize(
|
17
|
+
def initialize(path)
|
18
|
+
data = Pathname(path).read
|
17
19
|
data = data.strip.split("\n").map do |line|
|
18
20
|
line.split.map{|cell| parse_cell(cell)}
|
19
21
|
end
|
@@ -108,17 +110,5 @@ class Kakuro
|
|
108
110
|
end
|
109
111
|
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
x x x 10/ 24/ 29/ x 11/ 21/ 10/
|
114
|
-
x 11/ 19/24 _ _ _ /6 _ _ _
|
115
|
-
/31 _ _ _ _ _ 10/20 _ _ _
|
116
|
-
/4 _ _ 22/18 _ _ _ 23/13 _ _
|
117
|
-
/18 _ _ _ 24/26 _ _ _ _ _
|
118
|
-
x 19/ 30/16 _ _ 10/11 _ _ 11/ 20/
|
119
|
-
/34 _ _ _ _ _ 23/23 _ _ _
|
120
|
-
/7 _ _ 9/16 _ _ _ 3/4 _ _
|
121
|
-
/24 _ _ _ /23 _ _ _ _ _
|
122
|
-
/10 _ _ _ /11 _ _ _ x x
|
123
|
-
"""
|
124
|
-
).solve!
|
113
|
+
path = ARGV[0] || Pathname(__dir__) + "kakuro-1.txt"
|
114
|
+
Kakuro.new(path).solve!
|
@@ -0,0 +1,10 @@
|
|
1
|
+
x x x 10/ 24/ 29/ x 11/ 21/ 10/
|
2
|
+
x 11/ 19/24 _ _ _ /6 _ _ _
|
3
|
+
/31 _ _ _ _ _ 10/20 _ _ _
|
4
|
+
/4 _ _ 22/18 _ _ _ 23/13 _ _
|
5
|
+
/18 _ _ _ 24/26 _ _ _ _ _
|
6
|
+
x 19/ 30/16 _ _ 10/11 _ _ 11/ 20/
|
7
|
+
/34 _ _ _ _ _ 23/23 _ _ _
|
8
|
+
/7 _ _ 9/16 _ _ _ 3/4 _ _
|
9
|
+
/24 _ _ _ /23 _ _ _ _ _
|
10
|
+
/10 _ _ _ /11 _ _ _ x x
|
@@ -12,13 +12,13 @@ class KnightsPuzzle
|
|
12
12
|
@moves = @num_moves.times.map{|t| move(t)}
|
13
13
|
setup_board @boards[0], """
|
14
14
|
bbb.
|
15
|
-
|
15
|
+
xbxw
|
16
16
|
..ww
|
17
17
|
x.xw
|
18
18
|
"""
|
19
19
|
setup_board @boards[-1], """
|
20
20
|
www.
|
21
|
-
|
21
|
+
xwxb
|
22
22
|
..bb
|
23
23
|
x.xb
|
24
24
|
"""
|
@@ -29,11 +29,11 @@ class KnightsPuzzle
|
|
29
29
|
|
30
30
|
def print_board(t)
|
31
31
|
puts "State #{t}:"
|
32
|
-
puts @boards[t].transpose.map{|row| row.map{|c| ".wbx"[@model[c].to_i]}.join }.join("\n")
|
32
|
+
puts @boards[t].transpose.map{|row| row.map{|c| ".wbx"[@model[c].to_s.to_i]}.join }.join("\n")
|
33
33
|
end
|
34
34
|
|
35
35
|
def print_move(t)
|
36
|
-
move = Hash[@moves[t].map{|k,v| [k,@model[v].to_i]}]
|
36
|
+
move = Hash[@moves[t].map{|k,v| [k,@model[v].to_s.to_i]}]
|
37
37
|
figure = " wbx"[move[:figure]]
|
38
38
|
puts "#{figure}: #{move[:start_x]},#{move[:start_y]} -> #{move[:end_x]},#{move[:end_y]}"
|
39
39
|
puts ""
|
@@ -17,10 +17,13 @@
|
|
17
17
|
#
|
18
18
|
# This affects few enough real world puzzles and would complicate solution enough
|
19
19
|
# that I let it be.
|
20
|
+
|
21
|
+
require "pathname"
|
20
22
|
require_relative "../lib/z3"
|
21
23
|
|
22
24
|
class LetterConnections
|
23
|
-
def initialize(
|
25
|
+
def initialize(path)
|
26
|
+
data = Pathname(path).read
|
24
27
|
data = data.strip.split("\n")
|
25
28
|
@xsize = data[0].size
|
26
29
|
@ysize = data.size
|
@@ -183,17 +186,5 @@ class LetterConnections
|
|
183
186
|
end
|
184
187
|
end
|
185
188
|
|
186
|
-
|
187
|
-
|
188
|
-
..KJ....FE
|
189
|
-
.FJ.......
|
190
|
-
......G...
|
191
|
-
.K.I......
|
192
|
-
.......D..
|
193
|
-
IE......B.
|
194
|
-
...H...CD.
|
195
|
-
...A..C...
|
196
|
-
.A.H......
|
197
|
-
...B.G....
|
198
|
-
"""
|
199
|
-
).solve!
|
189
|
+
path = ARGV[0] || Pathname(__dir__) + "letter_connections-1.txt"
|
190
|
+
LetterConnections.new(path).solve!
|
@@ -1,9 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "pathname"
|
3
4
|
require_relative "../lib/z3"
|
4
5
|
|
5
6
|
class LightUp
|
6
|
-
def initialize(
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).read
|
7
9
|
data = data.split.map{|line| line.strip.chars}
|
8
10
|
@xsize = data[0].size
|
9
11
|
@ysize = data.size
|
@@ -92,14 +94,5 @@ class LightUp
|
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
-
....0..
|
98
|
-
.......
|
99
|
-
x.2.x..
|
100
|
-
...3...
|
101
|
-
..x.x.3
|
102
|
-
.......
|
103
|
-
..1....
|
104
|
-
"""
|
105
|
-
).solve!
|
97
|
+
path = ARGV[0] || Pathname(__dir__) + "light_up-1.txt"
|
98
|
+
LightUp.new(path).solve!
|
@@ -1,9 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "pathname"
|
3
4
|
require_relative "../lib/z3"
|
4
5
|
|
5
6
|
class MiniSudokuSolver
|
6
|
-
def initialize(
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).read
|
9
|
+
data = data.strip.split("\n").map do |line|
|
10
|
+
line.split.map{|c| c == "_" ? nil : c.to_i}
|
11
|
+
end
|
7
12
|
@data = data
|
8
13
|
@solver = Z3::Solver.new
|
9
14
|
end
|
@@ -52,13 +57,5 @@ class MiniSudokuSolver
|
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
[_, _, _, 4, _, _],
|
58
|
-
[_, _, 4, 1, 2, _],
|
59
|
-
[_, _, 6, 5, 4, _],
|
60
|
-
[_, 5, 2, 3, _, _],
|
61
|
-
[_, 2, 3, 6, _, _],
|
62
|
-
[_, _, 1, _, _, _],
|
63
|
-
])
|
64
|
-
minisudoku.solve!
|
60
|
+
path = ARGV[0] || Pathname(__dir__) + "minisudoku-1.txt"
|
61
|
+
MiniSudokuSolver.new(path).solve!
|
data/examples/nonogram
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/z3"
|
4
|
+
|
5
|
+
class Nonogram
|
6
|
+
def initialize(columns, rows)
|
7
|
+
@columns = columns
|
8
|
+
@rows = rows
|
9
|
+
@solver = Z3::Solver.new
|
10
|
+
@xsize = @columns.size
|
11
|
+
@ysize = @rows.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def print_answer!
|
15
|
+
@ysize.times do |y|
|
16
|
+
@xsize.times do |x|
|
17
|
+
v = @model[@cells[y][x]]
|
18
|
+
if v.to_s == "true"
|
19
|
+
print "\u2588"
|
20
|
+
else
|
21
|
+
print "\u00b7"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
print "\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def print_all_answers!
|
29
|
+
if @solver.check == :sat
|
30
|
+
@model = @solver.model
|
31
|
+
print_answer!
|
32
|
+
current_solution = []
|
33
|
+
# TODO: Model API doesn't currently have functionality necessary
|
34
|
+
# to make uniqueness checks work
|
35
|
+
# @model.each do |var, val|
|
36
|
+
# current_solution << (@model[var] != val)
|
37
|
+
# end
|
38
|
+
# @solver.assert Z3.Or(*current_solution)
|
39
|
+
# if @solver.check == :sat
|
40
|
+
# puts "solution is not unique"
|
41
|
+
# print_answer!
|
42
|
+
# else
|
43
|
+
# puts "solution is unique"
|
44
|
+
# end
|
45
|
+
else
|
46
|
+
puts "failed to solve"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_vars(nums, prefix, size)
|
51
|
+
results = []
|
52
|
+
(0...nums.size).each do |i|
|
53
|
+
vs = Z3.Int("#{prefix},#{i+1},s")
|
54
|
+
ve = Z3.Int("#{prefix},#{i+1},e")
|
55
|
+
@solver.assert ve-vs == nums[i]-1
|
56
|
+
@solver.assert vs >= 0
|
57
|
+
@solver.assert ve < size
|
58
|
+
results << [vs,ve]
|
59
|
+
end
|
60
|
+
|
61
|
+
(0...nums.size-1).each do |i|
|
62
|
+
ve0 = results[i][1]
|
63
|
+
vs1 = results[i+1][0]
|
64
|
+
@solver.assert vs1 >= ve0+2
|
65
|
+
end
|
66
|
+
|
67
|
+
results
|
68
|
+
end
|
69
|
+
|
70
|
+
def setup_cell_constraints(cells, cvars)
|
71
|
+
(0...cells.size).each do |i|
|
72
|
+
rule_i = Z3.Or(
|
73
|
+
*cvars.map{|vs,ve| Z3.And(vs <= i, i <= ve) }
|
74
|
+
)
|
75
|
+
@solver.assert cells[i] == rule_i
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def solve!
|
80
|
+
@cells = (0...@ysize).map{|y|
|
81
|
+
(0...@xsize).map{|x|
|
82
|
+
Z3.Bool("cell[#{x+1},#{y+1}]")
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
@column_vars = (0...@xsize).map{|i|
|
87
|
+
setup_vars(@columns[i], "c%d" % (i+1), @ysize)
|
88
|
+
}
|
89
|
+
@row_vars = (0...@ysize).map{|i|
|
90
|
+
setup_vars(@rows[i], "r%d" % (i+1), @xsize)
|
91
|
+
}
|
92
|
+
|
93
|
+
@xsize.times do |i|
|
94
|
+
setup_cell_constraints(@cells.map{|row| row[i]}, @column_vars[i])
|
95
|
+
end
|
96
|
+
|
97
|
+
@ysize.times do |i|
|
98
|
+
setup_cell_constraints(@cells[i], @row_vars[i])
|
99
|
+
end
|
100
|
+
|
101
|
+
print_all_answers!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
nonogram = Nonogram.new(
|
106
|
+
[
|
107
|
+
[2],
|
108
|
+
[1,2],
|
109
|
+
[2,3],
|
110
|
+
[2,3],
|
111
|
+
[3,1,1],
|
112
|
+
[2,1,1],
|
113
|
+
[1,1,1,2,2],
|
114
|
+
[1,1,3,1,3],
|
115
|
+
[2,6,4],
|
116
|
+
[3,3,9,1],
|
117
|
+
[5,3,2],
|
118
|
+
[3,1,2,2],
|
119
|
+
[2,1,7],
|
120
|
+
[3,3,2],
|
121
|
+
[2,4],
|
122
|
+
[2,1,2],
|
123
|
+
[2,2,1],
|
124
|
+
[2,2],
|
125
|
+
[1],
|
126
|
+
[1],
|
127
|
+
],
|
128
|
+
[
|
129
|
+
[3],
|
130
|
+
[5],
|
131
|
+
[3,1],
|
132
|
+
[2,1],
|
133
|
+
[3,3,4],
|
134
|
+
[2,2,7],
|
135
|
+
[6,1,1],
|
136
|
+
[4,2,2],
|
137
|
+
[1,1],
|
138
|
+
[3,1],
|
139
|
+
[6],
|
140
|
+
[2,7],
|
141
|
+
[6,3,1],
|
142
|
+
[1,2,2,1,1],
|
143
|
+
[4,1,1,3],
|
144
|
+
[4,2,2],
|
145
|
+
[3,3,1],
|
146
|
+
[3,3],
|
147
|
+
[3],
|
148
|
+
[2,1],
|
149
|
+
]
|
150
|
+
)
|
151
|
+
|
152
|
+
nonogram.solve!
|
File without changes
|
@@ -1,9 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
|
4
|
+
require "pathname"
|
3
5
|
require_relative "../lib/z3"
|
4
6
|
|
5
7
|
class SudokuSolver
|
6
|
-
def initialize(
|
8
|
+
def initialize(path)
|
9
|
+
data = Pathname(path).read
|
10
|
+
data = data.strip.split("\n").map do |line|
|
11
|
+
line.split.map{|c| c == "_" ? nil : c.to_i}
|
12
|
+
end
|
7
13
|
@data = data
|
8
14
|
@solver = Z3::Solver.new
|
9
15
|
end
|
@@ -52,16 +58,5 @@ class SudokuSolver
|
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
[_, 6, _, 5, _, 9, _, 4, _],
|
58
|
-
[9, 2, _, _, _, _, _, 7, 6],
|
59
|
-
[_, _, 1, _, _, _, 9, _, _],
|
60
|
-
[7, _, _, 6, _, 3, _, _, 9],
|
61
|
-
[_, _, _, _, _, _, _, _, _],
|
62
|
-
[3, _, _, 4, _, 1, _, _, 7],
|
63
|
-
[_, _, 6, _, _, _, 7, _, _],
|
64
|
-
[2, 4, _, _, _, _, _, 6, 5],
|
65
|
-
[_, 9, _, 1, _, 8, _, 3, _],
|
66
|
-
])
|
67
|
-
sudoku.solve!
|
61
|
+
path = ARGV[0] || Pathname(__dir__) + "sudoku-1.txt"
|
62
|
+
SudokuSolver.new(path).solve!
|