z3 0.0.20160330 → 0.0.20160427
Sign up to get free protection for your applications and to get access to all the features.
- 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!
|