z3 0.0.20181229 → 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 +0 -2
- data/Rakefile +8 -1
- 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/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/expr/expr.rb +3 -3
- data/lib/z3/low_level_auto.rb +138 -10
- data/lib/z3/optimize.rb +1 -0
- data/lib/z3/very_low_level.rb +5 -1
- data/lib/z3/very_low_level_auto.rb +35 -3
- 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/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/optimize_spec.rb +3 -1
- data/spec/set_expr_spec.rb +15 -9
- data/spec/solver_spec.rb +1 -2
- data/spec/spec_helper.rb +15 -0
- metadata +86 -7
data/examples/futoshiki
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require_relative "../lib/z3"
|
5
|
+
|
6
|
+
class Futoshiki
|
7
|
+
# This needs to be very exactly formatted
|
8
|
+
def initialize(path)
|
9
|
+
@data = Pathname(path).readlines.map(&:chomp)
|
10
|
+
@size = (@data.size+1)/2
|
11
|
+
@solver = Z3::Solver.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def cell_value(x, y)
|
15
|
+
v = @data[y*2][x*2]
|
16
|
+
if v == "#"
|
17
|
+
nil
|
18
|
+
elsif v =~ /\d/
|
19
|
+
v.to_i
|
20
|
+
else
|
21
|
+
raise "Bad cell value"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def cmp_right(x, y)
|
26
|
+
v = @data[y*2][x*2 + 1]
|
27
|
+
if v == ">"
|
28
|
+
">"
|
29
|
+
elsif v == "<"
|
30
|
+
"<"
|
31
|
+
elsif v == " "
|
32
|
+
nil
|
33
|
+
else
|
34
|
+
raise "Bad dot value"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def cmp_bottom(x, y)
|
39
|
+
v = @data[y*2 + 1][x*2]
|
40
|
+
if v == "_"
|
41
|
+
">"
|
42
|
+
elsif v == "^"
|
43
|
+
"<"
|
44
|
+
elsif v == " " or v == nil
|
45
|
+
false
|
46
|
+
else
|
47
|
+
raise "Bad dot value"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def call
|
52
|
+
@vars = {}
|
53
|
+
@size.times do |y|
|
54
|
+
@size.times do |x|
|
55
|
+
v = Z3.Int("v#{x},#{y}")
|
56
|
+
@vars[[x,y]] = v
|
57
|
+
cv = cell_value(x, y)
|
58
|
+
if cv
|
59
|
+
@solver.assert (v == cv)
|
60
|
+
else
|
61
|
+
@solver.assert (v >= 1) & (v <= @size)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@size.times do |x|
|
67
|
+
line = @size.times.map{|y| @vars[[x,y]] }
|
68
|
+
@solver.assert Z3.Distinct(*line)
|
69
|
+
end
|
70
|
+
|
71
|
+
@size.times do |y|
|
72
|
+
line = @size.times.map{|x| @vars[[x,y]] }
|
73
|
+
@solver.assert Z3.Distinct(*line)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Compare right
|
77
|
+
(0..@size-1).each do |y|
|
78
|
+
(0..@size-2).each do |x|
|
79
|
+
lv = @vars[[x,y]]
|
80
|
+
rv = @vars[[x+1,y]]
|
81
|
+
cmp = cmp_right(x,y)
|
82
|
+
if cmp == ">"
|
83
|
+
@solver.assert lv > rv
|
84
|
+
elsif cmp == "<"
|
85
|
+
@solver.assert lv < rv
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Compare bottom
|
91
|
+
(0..@size-2).each do |y|
|
92
|
+
(0..@size-1).each do |x|
|
93
|
+
tv = @vars[[x,y]]
|
94
|
+
bv = @vars[[x,y+1]]
|
95
|
+
cmp = cmp_bottom(x,y)
|
96
|
+
if cmp == ">"
|
97
|
+
@solver.assert tv > bv
|
98
|
+
elsif cmp == "<"
|
99
|
+
@solver.assert tv < bv
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if @solver.satisfiable?
|
105
|
+
@model = @solver.model
|
106
|
+
print_answer!
|
107
|
+
else
|
108
|
+
puts "failed to solve"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def print_answer!
|
115
|
+
output = @data.map(&:dup)
|
116
|
+
@size.times do |y|
|
117
|
+
@size.times do |x|
|
118
|
+
v = @model[@vars[[x,y]]]
|
119
|
+
output[2*y][2*x] = "#{v}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
puts output
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
path = ARGV[0] || Pathname(__dir__) + "futoshiki-1.txt"
|
128
|
+
Futoshiki.new(path).call
|
data/examples/kakurasu
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require_relative "../lib/z3"
|
5
|
+
|
6
|
+
class Kakurasu
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).readlines.grep(/\S/)
|
9
|
+
raise "Data needs to have two lines (cols, rows)" unless data.size == 2
|
10
|
+
@col_sums = data[0].split.map(&:to_i)
|
11
|
+
@row_sums = data[1].split.map(&:to_i)
|
12
|
+
|
13
|
+
@xsize = @col_sums.size
|
14
|
+
@ysize = @row_sums.size
|
15
|
+
@solver = Z3::Solver.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
# setup
|
20
|
+
@bvars = {}
|
21
|
+
@cvars = {}
|
22
|
+
@rvars = {}
|
23
|
+
@ysize.times do |y|
|
24
|
+
@ysize.times do |x|
|
25
|
+
b = Z3.Bool("b#{x},#{y}")
|
26
|
+
r = Z3.Int("r#{x},#{y}")
|
27
|
+
c = Z3.Int("c#{x},#{y}")
|
28
|
+
@solver.assert (!b).implies(r == 0)
|
29
|
+
@solver.assert (!b).implies(c == 0)
|
30
|
+
@solver.assert b.implies(r == (x+1))
|
31
|
+
@solver.assert b.implies(c == (y+1))
|
32
|
+
@bvars[[x,y]] = b
|
33
|
+
@cvars[x] ||= []
|
34
|
+
@cvars[x][y] = c
|
35
|
+
@rvars[y] ||= []
|
36
|
+
@rvars[y][x] = r
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@xsize.times do |x|
|
41
|
+
@solver.assert Z3.Add(*@rvars[x]) == @row_sums[x]
|
42
|
+
end
|
43
|
+
|
44
|
+
@ysize.times do |y|
|
45
|
+
@solver.assert Z3.Add(*@cvars[y]) == @col_sums[y]
|
46
|
+
end
|
47
|
+
|
48
|
+
if @solver.satisfiable?
|
49
|
+
@model = @solver.model
|
50
|
+
print_board!
|
51
|
+
else
|
52
|
+
puts "failed to solve"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def print_board!
|
59
|
+
@ysize.times do |y|
|
60
|
+
@ysize.times do |x|
|
61
|
+
if @model[@bvars[[x,y]]].to_b
|
62
|
+
print "[X]"
|
63
|
+
else
|
64
|
+
print "[ ]"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
print "\n"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
path = ARGV[0] || Pathname(__dir__) + "kakurasu-1.txt"
|
73
|
+
Kakurasu.new(path).call
|
data/examples/kakuro
CHANGED
@@ -25,7 +25,7 @@ class Kakuro
|
|
25
25
|
@solver = Z3::Solver.new
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def call
|
29
29
|
@cells = map_coordinates{|x,y| cell_var(x,y) if @data[[x,y]] == nil}.compact
|
30
30
|
|
31
31
|
(0...@ysize).each do |y|
|
@@ -111,4 +111,4 @@ class Kakuro
|
|
111
111
|
end
|
112
112
|
|
113
113
|
path = ARGV[0] || Pathname(__dir__) + "kakuro-1.txt"
|
114
|
-
Kakuro.new(path).
|
114
|
+
Kakuro.new(path).call
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require_relative "../lib/z3"
|
5
|
+
|
6
|
+
class KillerSudokuSolver
|
7
|
+
def initialize(path)
|
8
|
+
data = Pathname(path).read
|
9
|
+
knowns, cages, cage_counts = data.split(/\n{2,}/)
|
10
|
+
@knowns = knowns.strip.split("\n").map do |line|
|
11
|
+
line.split.map{|c| c == "." ? nil : c.to_i}
|
12
|
+
end
|
13
|
+
@cages = cages.strip.split("\n").map do |line|
|
14
|
+
line.split
|
15
|
+
end
|
16
|
+
@size = @knowns.size
|
17
|
+
raise "Bad size" unless @knowns.all?{|line| line.size == @size}
|
18
|
+
raise "Bad size" unless @cages.size == @size
|
19
|
+
raise "Bad size" unless @cages.all?{|line| line.size == @size}
|
20
|
+
@cage_counts = cage_counts.split("\n").to_h(&:split).transform_values(&:to_i)
|
21
|
+
@solver = Z3::Solver.new
|
22
|
+
if @size == 9
|
23
|
+
@boxsize = 3
|
24
|
+
elsif @size == 4
|
25
|
+
@boxsize = 2
|
26
|
+
else
|
27
|
+
raise "Bad size"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def call
|
32
|
+
@cells = (0...@size).map do |j|
|
33
|
+
(0...@size).map do |i|
|
34
|
+
cell_var(@knowns[j][i], i, j)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@cells.each do |row|
|
39
|
+
@solver.assert Z3.Distinct(*row)
|
40
|
+
end
|
41
|
+
@cells.transpose.each do |column|
|
42
|
+
@solver.assert Z3.Distinct(*column)
|
43
|
+
end
|
44
|
+
@cells.each_slice(@boxsize) do |rows|
|
45
|
+
rows.transpose.each_slice(@boxsize) do |square|
|
46
|
+
@solver.assert Z3.Distinct(*square.flatten)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
by_cage = {}
|
51
|
+
(0...@size).each do |j|
|
52
|
+
(0...@size).each do |i|
|
53
|
+
c = @cages[j][i]
|
54
|
+
by_cage[c] ||= []
|
55
|
+
by_cage[c] << @cells[j][i]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
by_cage.each do |cage_name, cells|
|
60
|
+
@solver.assert Z3.Add(*cells) == @cage_counts.fetch(cage_name)
|
61
|
+
@solver.assert Z3.Distinct(*cells)
|
62
|
+
end
|
63
|
+
|
64
|
+
if @solver.satisfiable?
|
65
|
+
@model = @solver.model
|
66
|
+
print_answer!
|
67
|
+
else
|
68
|
+
puts "failed to solve"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def cell_var(cell, i, j)
|
73
|
+
v = Z3.Int("cell[#{i+1},#{j+1}]")
|
74
|
+
@solver.assert v >= 1
|
75
|
+
@solver.assert v <= @size
|
76
|
+
@solver.assert v == cell if cell != nil
|
77
|
+
v
|
78
|
+
end
|
79
|
+
|
80
|
+
def print_answer!
|
81
|
+
@cells.each do |row|
|
82
|
+
puts row.map{|v| @model[v]}.join(" ")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
path = ARGV[0] || Pathname(__dir__) + "killer_sudoku-1.txt"
|
88
|
+
KillerSudokuSolver.new(path).call
|
@@ -0,0 +1,53 @@
|
|
1
|
+
. . . . . . . . .
|
2
|
+
. . . . . . . . .
|
3
|
+
. . . . . . . . .
|
4
|
+
. . . . . . . . .
|
5
|
+
. . . . . . . . .
|
6
|
+
. . . . . . . . .
|
7
|
+
. . . . . . . . .
|
8
|
+
. . . . . . . . .
|
9
|
+
. . . . . . . . .
|
10
|
+
|
11
|
+
a a b c d d d e e
|
12
|
+
f g b c v v x y z
|
13
|
+
f g g u u w x y z
|
14
|
+
f s s t t w A A B
|
15
|
+
h h C C C D D A B
|
16
|
+
i i G G F E E r q
|
17
|
+
j j m G F n o r q
|
18
|
+
k m m F F n o p q
|
19
|
+
k l l n n n p p p
|
20
|
+
|
21
|
+
a 9
|
22
|
+
b 8
|
23
|
+
c 14
|
24
|
+
d 13
|
25
|
+
e 15
|
26
|
+
f 15
|
27
|
+
g 18
|
28
|
+
h 8
|
29
|
+
i 9
|
30
|
+
j 12
|
31
|
+
k 10
|
32
|
+
l 8
|
33
|
+
m 15
|
34
|
+
n 26
|
35
|
+
o 13
|
36
|
+
p 19
|
37
|
+
q 12
|
38
|
+
r 12
|
39
|
+
s 10
|
40
|
+
t 10
|
41
|
+
u 11
|
42
|
+
v 13
|
43
|
+
w 11
|
44
|
+
x 10
|
45
|
+
y 5
|
46
|
+
z 7
|
47
|
+
A 12
|
48
|
+
B 11
|
49
|
+
C 13
|
50
|
+
D 12
|
51
|
+
E 9
|
52
|
+
F 17
|
53
|
+
G 18
|
@@ -31,7 +31,7 @@ class KinematicsProblem01 < KinematicsProblem
|
|
31
31
|
An airplane accelerates down a runway at 3.20 m/s2 for 32.8 s until is finally lifts off the ground.
|
32
32
|
Determine the distance traveled before takeoff.
|
33
33
|
"""
|
34
|
-
def
|
34
|
+
def call
|
35
35
|
a = Z3.Real("a")
|
36
36
|
t = Z3.Real("t")
|
37
37
|
d = Z3.Real("d")
|
@@ -46,7 +46,7 @@ class KinematicsProblem02 < KinematicsProblem
|
|
46
46
|
"""
|
47
47
|
A car starts from rest and accelerates uniformly over a time of 5.21 seconds for a distance of 110 m. Determine the acceleration of the car.
|
48
48
|
"""
|
49
|
-
def
|
49
|
+
def call
|
50
50
|
a = Z3.Real("a")
|
51
51
|
t = Z3.Real("t")
|
52
52
|
d = Z3.Real("d")
|
@@ -62,7 +62,7 @@ class KinematicsProblem03 < KinematicsProblem
|
|
62
62
|
Upton Chuck is riding the Giant Drop at Great America.
|
63
63
|
If Upton free falls for 2.60 seconds, what will be his final velocity and how far will he fall?
|
64
64
|
"""
|
65
|
-
def
|
65
|
+
def call
|
66
66
|
a = Z3.Real("a")
|
67
67
|
t = Z3.Real("t")
|
68
68
|
v = Z3.Real("v")
|
@@ -79,7 +79,7 @@ class KinematicsProblem04 < KinematicsProblem
|
|
79
79
|
"""
|
80
80
|
A race car accelerates uniformly from 18.5 m/s to 46.1 m/s in 2.47 seconds. Determine the acceleration of the car and the distance traveled.
|
81
81
|
"""
|
82
|
-
def
|
82
|
+
def call
|
83
83
|
a = Z3.Real("a")
|
84
84
|
t = Z3.Real("t")
|
85
85
|
vs= Z3.Real("vs")
|
@@ -100,7 +100,7 @@ class KinematicsProblem05 < KinematicsProblem
|
|
100
100
|
The acceleration of gravity on the moon is 1.67 m/s2.
|
101
101
|
Determine the time for the feather to fall to the surface of the moon.
|
102
102
|
"""
|
103
|
-
def
|
103
|
+
def call
|
104
104
|
a = Z3.Real("a")
|
105
105
|
t = Z3.Real("t")
|
106
106
|
d = Z3.Real("d")
|
@@ -118,7 +118,7 @@ class KinematicsProblem06 < KinematicsProblem
|
|
118
118
|
If a rocket-powered sled is accelerated to a speed of 444 m/s in 1.83 seconds,
|
119
119
|
then what is the acceleration and what is the distance that the sled travels?
|
120
120
|
"""
|
121
|
-
def
|
121
|
+
def call
|
122
122
|
a = Z3.Real("a")
|
123
123
|
t = Z3.Real("t")
|
124
124
|
d = Z3.Real("d")
|
@@ -136,7 +136,7 @@ class KinematicsProblem07 < KinematicsProblem
|
|
136
136
|
A bike accelerates uniformly from rest to a speed of 7.10 m/s over a distance of 35.4 m.
|
137
137
|
Determine the acceleration of the bike.
|
138
138
|
"""
|
139
|
-
def
|
139
|
+
def call
|
140
140
|
a = Z3.Real("a")
|
141
141
|
t = Z3.Real("t")
|
142
142
|
d = Z3.Real("d")
|
@@ -156,7 +156,7 @@ class KinematicsProblem08 < KinematicsProblem
|
|
156
156
|
The takeoff speed for this plane will be 65 m/s.
|
157
157
|
Assuming this minimum acceleration, what is the minimum allowed length for the runway?
|
158
158
|
"""
|
159
|
-
def
|
159
|
+
def call
|
160
160
|
a = Z3.Real("a")
|
161
161
|
t = Z3.Real("t")
|
162
162
|
d = Z3.Real("d")
|
@@ -174,7 +174,7 @@ class KinematicsProblem09 < KinematicsProblem
|
|
174
174
|
A car traveling at 22.4 m/s skids to a stop in 2.55 s.
|
175
175
|
Determine the skidding distance of the car (assume uniform acceleration).
|
176
176
|
"""
|
177
|
-
def
|
177
|
+
def call
|
178
178
|
t = Z3.Real("t")
|
179
179
|
d = Z3.Real("d")
|
180
180
|
v = Z3.Real("v")
|
@@ -190,7 +190,7 @@ class KinematicsProblem10 < KinematicsProblem
|
|
190
190
|
A kangaroo is capable of jumping to a height of 2.62 m.
|
191
191
|
Determine the takeoff speed of the kangaroo.
|
192
192
|
"""
|
193
|
-
def
|
193
|
+
def call
|
194
194
|
a = Z3.Real("a")
|
195
195
|
t = Z3.Real("t")
|
196
196
|
d = Z3.Real("d")
|
@@ -207,13 +207,13 @@ end
|
|
207
207
|
# This is actually param for Python Z3 printer, can't work until we get that
|
208
208
|
# Z3.set_param("rational_to_decimal", true)
|
209
209
|
|
210
|
-
KinematicsProblem01.new.
|
211
|
-
KinematicsProblem02.new.
|
212
|
-
KinematicsProblem03.new.
|
213
|
-
KinematicsProblem04.new.
|
214
|
-
KinematicsProblem05.new.
|
215
|
-
KinematicsProblem06.new.
|
216
|
-
KinematicsProblem07.new.
|
217
|
-
KinematicsProblem08.new.
|
218
|
-
KinematicsProblem09.new.
|
219
|
-
KinematicsProblem10.new.
|
210
|
+
KinematicsProblem01.new.call
|
211
|
+
KinematicsProblem02.new.call
|
212
|
+
KinematicsProblem03.new.call
|
213
|
+
KinematicsProblem04.new.call
|
214
|
+
KinematicsProblem05.new.call
|
215
|
+
KinematicsProblem06.new.call
|
216
|
+
KinematicsProblem07.new.call
|
217
|
+
KinematicsProblem08.new.call
|
218
|
+
KinematicsProblem09.new.call
|
219
|
+
KinematicsProblem10.new.call
|
data/examples/knights_puzzle
CHANGED
@@ -39,7 +39,7 @@ class KnightsPuzzle
|
|
39
39
|
puts ""
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def call
|
43
43
|
if @solver.satisfiable?
|
44
44
|
@model = @solver.model
|
45
45
|
puts "Solved"
|
@@ -119,4 +119,4 @@ class KnightsPuzzle
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
KnightsPuzzle.new.
|
122
|
+
KnightsPuzzle.new.call
|
data/examples/kropki
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require "pathname"
|
5
|
+
require_relative "../lib/z3"
|
6
|
+
|
7
|
+
# Based on https://play.google.com/store/apps/details?id=com.alexuvarov.android.kropki.puzzle&hl=en_GB
|
8
|
+
#
|
9
|
+
# NxN grid
|
10
|
+
# Each cell contains numbers 1 to N
|
11
|
+
# Each row and each column contains distinct numbers
|
12
|
+
# If there's black dot (o) between cells, one is twice the other
|
13
|
+
# If there's white dot (*) between cells, one is the other plus one
|
14
|
+
# If there's no dot, neither of these is true
|
15
|
+
# (for 1/2, dot can be of either color)
|
16
|
+
|
17
|
+
class Kropki
|
18
|
+
def initialize(path)
|
19
|
+
@data = Pathname(path).readlines.map(&:chomp)
|
20
|
+
@size = (@data.size - 1) / 2
|
21
|
+
raise unless @data.size == 2 * @size + 1
|
22
|
+
raise unless @data.all?{ |row| row.size == 2 * @size + 1 }
|
23
|
+
@solver = Z3::Solver.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
# Cells contain numbers 1 - N
|
28
|
+
@size.times do |y|
|
29
|
+
@size.times do |x|
|
30
|
+
@solver.assert cell(x, y) >= 1
|
31
|
+
@solver.assert cell(x, y) <= @size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Each row and column distinct
|
36
|
+
@size.times do |y|
|
37
|
+
@solver.assert Z3.Distinct(*@size.times.map{ |x| cell(x,y) })
|
38
|
+
end
|
39
|
+
@size.times do |x|
|
40
|
+
@solver.assert Z3.Distinct(*@size.times.map{ |y| cell(x,y) })
|
41
|
+
end
|
42
|
+
|
43
|
+
# Horizontal dots
|
44
|
+
@size.times do |y|
|
45
|
+
(0...@size).each do |x|
|
46
|
+
dot_constraints cell(x, y), cell(x+1, y), @data[2*y+1][2*x+2]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Vertical dots
|
51
|
+
@size.times do |x|
|
52
|
+
(0...@size).each do |y|
|
53
|
+
dot_constraints cell(x, y), cell(x, y+1), @data[2*y+2][2*x+1]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if @solver.satisfiable?
|
58
|
+
@model = @solver.model
|
59
|
+
print_answer
|
60
|
+
else
|
61
|
+
puts "failed to solve"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def dot_constraints(c1, c2, dot)
|
68
|
+
if dot == "o"
|
69
|
+
@solver.assert Z3.Or(c1 == c2 + 1, c2 == c1 + 1)
|
70
|
+
elsif dot == "*"
|
71
|
+
@solver.assert Z3.Or(c1 == c2 * 2, c2 == c1 * 2)
|
72
|
+
else
|
73
|
+
@solver.assert c1 != c2 + 1
|
74
|
+
@solver.assert c2 != c1 + 1
|
75
|
+
@solver.assert c1 != c2 * 2
|
76
|
+
@solver.assert c2 != c1 * 2
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cell(x, y)
|
81
|
+
Z3.Int("c#{x},#{y}")
|
82
|
+
end
|
83
|
+
|
84
|
+
def print_answer
|
85
|
+
output = @data.map(&:dup)
|
86
|
+
@size.times do |y|
|
87
|
+
@size.times do |x|
|
88
|
+
# This only works for 1-9, could use hex or something for more
|
89
|
+
value = @model[cell(x,y)].to_s
|
90
|
+
output[2*y+1][2*x+1, 1] = value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
puts output
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
path = ARGV[0] || Pathname(__dir__) + "kropki-1.txt"
|
100
|
+
Kropki.new(path).call
|
data/examples/letter_connections
CHANGED
@@ -61,7 +61,7 @@ class LetterConnections
|
|
61
61
|
@solver = Z3::Solver.new
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
64
|
+
def call
|
65
65
|
@line = map_coordinates{|x,y| line_var(x,y) }
|
66
66
|
@dir = map_coordinates{|x,y| dir_var(x,y) }
|
67
67
|
|
@@ -187,4 +187,4 @@ class LetterConnections
|
|
187
187
|
end
|
188
188
|
|
189
189
|
path = ARGV[0] || Pathname(__dir__) + "letter_connections-1.txt"
|
190
|
-
LetterConnections.new(path).
|
190
|
+
LetterConnections.new(path).call
|
data/examples/light_up
CHANGED
@@ -13,7 +13,7 @@ class LightUp
|
|
13
13
|
@solver = Z3::Solver.new
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def call
|
17
17
|
@lamps = map_coordinates{|x,y| int01(x,y)}
|
18
18
|
|
19
19
|
# No lamps on walls
|
@@ -95,4 +95,4 @@ class LightUp
|
|
95
95
|
end
|
96
96
|
|
97
97
|
path = ARGV[0] || Pathname(__dir__) + "light_up-1.txt"
|
98
|
-
LightUp.new(path).
|
98
|
+
LightUp.new(path).call
|
data/examples/minisudoku
CHANGED
@@ -13,7 +13,7 @@ class MiniSudokuSolver
|
|
13
13
|
@solver = Z3::Solver.new
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def call
|
17
17
|
@cells = (0..5).map do |j|
|
18
18
|
(0..5).map do |i|
|
19
19
|
cell_var(@data[j][i], i, j)
|
@@ -58,4 +58,4 @@ class MiniSudokuSolver
|
|
58
58
|
end
|
59
59
|
|
60
60
|
path = ARGV[0] || Pathname(__dir__) + "minisudoku-1.txt"
|
61
|
-
MiniSudokuSolver.new(path).
|
61
|
+
MiniSudokuSolver.new(path).call
|