z3 0.0.20181229 → 0.0.20211213

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -2
  3. data/Rakefile +8 -1
  4. data/examples/abc_path +187 -0
  5. data/examples/abc_path-1.txt +7 -0
  6. data/examples/algebra_problems +12 -12
  7. data/examples/aquarium +133 -0
  8. data/examples/aquarium-1.txt +11 -0
  9. data/examples/bridges +2 -2
  10. data/examples/cats_organized_neatly +133 -0
  11. data/examples/cats_organized_neatly-10.txt +15 -0
  12. data/examples/cats_organized_neatly-3.txt +8 -0
  13. data/examples/cats_organized_neatly-48.txt +32 -0
  14. data/examples/circuit_problems +4 -4
  15. data/examples/clogic_puzzle +2 -2
  16. data/examples/color_nonogram +150 -0
  17. data/examples/color_nonogram-1.txt +23 -0
  18. data/examples/crossflip +2 -4
  19. data/examples/dominion +153 -0
  20. data/examples/dominion-1.txt +8 -0
  21. data/examples/dominosa +133 -0
  22. data/examples/dominosa-1.txt +8 -0
  23. data/examples/eulero +99 -0
  24. data/examples/eulero-1.txt +5 -0
  25. data/examples/four_hackers_puzzle +2 -2
  26. data/examples/futoshiki +128 -0
  27. data/examples/futoshiki-1.txt +17 -0
  28. data/examples/kakurasu +73 -0
  29. data/examples/kakurasu-1.txt +2 -0
  30. data/examples/kakuro +2 -2
  31. data/examples/killer_sudoku +88 -0
  32. data/examples/killer_sudoku-1.txt +17 -0
  33. data/examples/killer_sudoku-2.txt +53 -0
  34. data/examples/kinematics_problems +20 -20
  35. data/examples/knights_puzzle +2 -2
  36. data/examples/kropki +100 -0
  37. data/examples/kropki-1.txt +13 -0
  38. data/examples/letter_connections +2 -2
  39. data/examples/light_up +2 -2
  40. data/examples/minisudoku +2 -2
  41. data/examples/miracle_sudoku +135 -0
  42. data/examples/miracle_sudoku-1.txt +9 -0
  43. data/examples/mortal_coil_puzzle +2 -2
  44. data/examples/nanro +245 -0
  45. data/examples/nanro-1.txt +8 -0
  46. data/examples/nine_clocks +106 -0
  47. data/examples/nonogram +2 -2
  48. data/examples/pyramid_nonogram +2 -2
  49. data/examples/regexp_crossword_solver +2 -2
  50. data/examples/regexp_solver +2 -2
  51. data/examples/renzoku +124 -0
  52. data/examples/renzoku-1.txt +17 -0
  53. data/examples/sandwich_sudoku +101 -0
  54. data/examples/sandwich_sudoku-1.txt +10 -0
  55. data/examples/selfref +2 -2
  56. data/examples/skyscrapers +118 -0
  57. data/examples/skyscrapers-1.txt +6 -0
  58. data/examples/skyscrapers-2.txt +11 -0
  59. data/examples/star_battle +134 -0
  60. data/examples/star_battle-1.txt +10 -0
  61. data/examples/stitches +180 -0
  62. data/examples/stitches-1.txt +11 -0
  63. data/examples/sudoku +2 -2
  64. data/examples/suguru +199 -0
  65. data/examples/suguru-1.txt +17 -0
  66. data/examples/verbal_arithmetic +2 -2
  67. data/examples/yajilin +268 -0
  68. data/examples/yajilin-1.txt +10 -0
  69. data/lib/z3/expr/expr.rb +3 -3
  70. data/lib/z3/low_level_auto.rb +138 -10
  71. data/lib/z3/optimize.rb +1 -0
  72. data/lib/z3/very_low_level.rb +5 -1
  73. data/lib/z3/very_low_level_auto.rb +35 -3
  74. data/spec/integration/abc_path_spec.rb +21 -0
  75. data/spec/integration/aquarium_spec.rb +27 -0
  76. data/spec/integration/cats_organized_neatly_spec.rb +14 -0
  77. data/spec/integration/color_nonogram_spec.rb +28 -0
  78. data/spec/integration/dominion_spec.rb +14 -0
  79. data/spec/integration/dominosa_spec.rb +21 -0
  80. data/spec/integration/eulero_spec.rb +11 -0
  81. data/spec/integration/futoshiki_spec.rb +23 -0
  82. data/spec/integration/kakurasu_spec.rb +18 -0
  83. data/spec/integration/killer_sudoku_spec.rb +10 -0
  84. data/spec/integration/knights_puzzle_spec.rb +11 -11
  85. data/spec/integration/kropki_spec.rb +19 -0
  86. data/spec/integration/miracle_sudoku_spec.rb +15 -0
  87. data/spec/integration/mortal_coil_puzzle_spec.rb +8 -6
  88. data/spec/integration/nanro_spec.rb +39 -0
  89. data/spec/integration/nine_clocks_spec.rb +30 -0
  90. data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
  91. data/spec/integration/renzoku_spec.rb +23 -0
  92. data/spec/integration/sandwich_sudoku_spec.rb +15 -0
  93. data/spec/integration/skyscraper_spec.rb +10 -0
  94. data/spec/integration/star_battle_spec.rb +27 -0
  95. data/spec/integration/stitches_spec.rb +25 -0
  96. data/spec/integration/suguru_spec.rb +23 -0
  97. data/spec/integration/yajilin_spec.rb +25 -0
  98. data/spec/optimize_spec.rb +3 -1
  99. data/spec/set_expr_spec.rb +15 -9
  100. data/spec/solver_spec.rb +1 -2
  101. data/spec/spec_helper.rb +15 -0
  102. metadata +86 -7
data/examples/suguru ADDED
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class Suguru
7
+ def initialize(path)
8
+ @data = Pathname(path).readlines.map(&:chomp)
9
+ @ysize = (@data.size - 1) / 2
10
+ @xsize = (@data[0].size - 1) / 2
11
+ raise unless @data.size == 2 * @ysize + 1
12
+ raise unless @data.all?{ |row| row.size == 2 * @xsize + 1 }
13
+ @solver = Z3::Solver.new
14
+ end
15
+
16
+ def call
17
+ @connections = calculate_connections
18
+ @containers = calculate_containers
19
+
20
+ @ysize.times do |y|
21
+ @ysize.times do |x|
22
+ v = cell_data(x, y)
23
+ if v
24
+ @solver.assert cell(x, y) == v
25
+ end
26
+ end
27
+ end
28
+
29
+ @containers.each do |name, cells|
30
+ @solver.assert Z3.Distinct(*cells)
31
+ cells.each do |c|
32
+ @solver.assert c >= 1
33
+ @solver.assert c <= cells.size
34
+ end
35
+ end
36
+
37
+ @ysize.times do |y|
38
+ @xsize.times do |x|
39
+ @solver.assert cell(x, y) != cell(x+1, y)
40
+ @solver.assert cell(x, y) != cell(x-1, y+1)
41
+ @solver.assert cell(x, y) != cell(x, y+1)
42
+ @solver.assert cell(x, y) != cell(x+1, y+1)
43
+ end
44
+ end
45
+
46
+ if @solver.satisfiable?
47
+ @model = @solver.model
48
+ print_answer
49
+ else
50
+ puts "failed to solve"
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def calculate_containers
57
+ cells = @xsize.times.flat_map{|x| @ysize.times.map{|y| [x,y]}}
58
+
59
+ containers = {}
60
+ cell_assignments = {}
61
+ cells.each do |c|
62
+ ct = c.join(",")
63
+ containers[ct] = [c]
64
+ cell_assignments[c] = ct
65
+ end
66
+
67
+ @connections.each do |c1, c2|
68
+ # Already in same container
69
+ ct1 = cell_assignments[c1]
70
+ ct2 = cell_assignments[c2]
71
+ next if ct1 == ct2
72
+ containers.delete(ct2).each do |c3|
73
+ cell_assignments[c3] = ct1
74
+ containers[ct1] << c3
75
+ end
76
+ end
77
+
78
+ containers.transform_values{|cells| cells.map{|x,y| cell(x,y)}}
79
+ end
80
+
81
+ def calculate_connections
82
+ result = []
83
+
84
+ @ysize.times do |y|
85
+ (0..@xsize-2).each do |x|
86
+ if cell_open_right?(x, y)
87
+ result << [[x,y], [x+1,y]]
88
+ end
89
+ end
90
+ end
91
+
92
+ @xsize.times do |x|
93
+ (0..@ysize-2).each do |y|
94
+ if cell_open_down?(x, y)
95
+ result << [[x,y], [x,y+1]]
96
+ end
97
+ end
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ def cell_open_right?(x, y)
104
+ @data[2*y+1][2*x+2] == " "
105
+ end
106
+
107
+ def cell_open_down?(x, y)
108
+ @data[2*y+2][2*x+1] == " "
109
+ end
110
+
111
+ def cell_data(x, y)
112
+ v = @data[2*y+1][2*x+1]
113
+ return nil if v == " "
114
+ v.to_i
115
+ end
116
+
117
+ def cell(x, y)
118
+ Z3.Int("c#{x},#{y}")
119
+ end
120
+
121
+ def print_answer
122
+ output = @data.map(&:dup)
123
+ @ysize.times do |y|
124
+ @xsize.times do |x|
125
+ # This only works for 1-9, could use hex or something for more
126
+ value = @model[cell(x,y)].to_s
127
+ output[2*y+1][2*x+1, 1] = value
128
+ end
129
+ end
130
+
131
+ puts output
132
+ end
133
+ end
134
+
135
+ path = ARGV[0] || Pathname(__dir__) + "suguru-1.txt"
136
+ Suguru.new(path).call
137
+
138
+
139
+ __END__
140
+
141
+ # Based on https://play.google.com/store/apps/details?id=com.alexuvarov.android.kropki.puzzle&hl=en_GB
142
+ #
143
+ # NxN grid
144
+ # Each cell contains numbers 1 to N
145
+ # Each row and each column contains distinct numbers
146
+ # If there's black dot (o) between cells, one is twice the other
147
+ # If there's white dot (*) between cells, one is the other plus one
148
+ # If there's no dot, neither of these is true
149
+ # (for 1/2, dot can be of either color)
150
+
151
+ class Kropki
152
+ def call
153
+ # Cells contain numbers 1 - N
154
+ @size.times do |y|
155
+ @size.times do |x|
156
+ @solver.assert cell(x, y) >= 1
157
+ @solver.assert cell(x, y) <= @size
158
+ end
159
+ end
160
+
161
+ # Each row and column distinct
162
+ @size.times do |y|
163
+ @solver.assert Z3.Distinct(*@size.times.map{ |x| cell(x,y) })
164
+ end
165
+ @size.times do |x|
166
+ @solver.assert Z3.Distinct(*@size.times.map{ |y| cell(x,y) })
167
+ end
168
+
169
+ # Horizontal dots
170
+ @size.times do |y|
171
+ (0...@size).each do |x|
172
+ dot_constraints cell(x, y), cell(x+1, y), @data[2*y+1][2*x+2]
173
+ end
174
+ end
175
+
176
+ # Vertical dots
177
+ @size.times do |x|
178
+ (0...@size).each do |y|
179
+ dot_constraints cell(x, y), cell(x, y+1), @data[2*y+2][2*x+1]
180
+ end
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def dot_constraints(c1, c2, dot)
187
+ if dot == "o"
188
+ @solver.assert Z3.Or(c1 == c2 + 1, c2 == c1 + 1)
189
+ elsif dot == "*"
190
+ @solver.assert Z3.Or(c1 == c2 * 2, c2 == c1 * 2)
191
+ else
192
+ @solver.assert c1 != c2 + 1
193
+ @solver.assert c2 != c1 + 1
194
+ @solver.assert c1 != c2 * 2
195
+ @solver.assert c2 != c1 * 2
196
+ end
197
+ end
198
+
199
+ end
@@ -0,0 +1,17 @@
1
+ +---+-----+
2
+ | |1 4 |
3
+ | | +---+
4
+ |2 6| | 2|
5
+ | | | |
6
+ | | |4 3|
7
+ +---+-+ |
8
+ | 3 | |
9
+ | +-+---+
10
+ |2 | |
11
+ +-+ +-+ |
12
+ | | |6 4|
13
+ +-+---+ |
14
+ | |3 | 3|
15
+ | | +---+
16
+ | | 1 2 |
17
+ +-+-------+
@@ -16,7 +16,7 @@ class VerbalArithmetic
16
16
  @c = c.chars.map{|v| @vars[v]}
17
17
  end
18
18
 
19
- def solve!
19
+ def call
20
20
  @solver.assert @a[0] != 0
21
21
  @solver.assert @b[0] != 0
22
22
  @solver.assert @c[0] != 0
@@ -44,4 +44,4 @@ class VerbalArithmetic
44
44
  end
45
45
  end
46
46
 
47
- VerbalArithmetic.new("SEND", "MORE", "MONEY").solve!
47
+ VerbalArithmetic.new("SEND", "MORE", "MONEY").call
data/examples/yajilin ADDED
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class Yajilin
7
+ def initialize(path)
8
+ @data = Pathname(path)
9
+ .readlines
10
+ .map{|line|
11
+ line
12
+ .chomp
13
+ .split
14
+ .map{|x| x == "." ? nil : x}
15
+ }
16
+ @xsize = @data.size
17
+ @ysize = @data[0].size
18
+ raise unless @data.all?{|row| row.size == @xsize}
19
+ @solver = Z3::Solver.new
20
+ end
21
+
22
+ def on_board?(x,y)
23
+ x >= 0 and y >= 0 and x < @xsize and y < @ysize
24
+ end
25
+
26
+ # true = white
27
+ # false = black
28
+ def cvar(x,y)
29
+ return unless on_board?(x,y)
30
+ Z3.Bool("c[#{x},#{y}]")
31
+ end
32
+
33
+ # true - connected
34
+ # right from (x,y)
35
+ def hvar(x,y)
36
+ return unless on_board?(x,y) and x != @xsize - 1
37
+ Z3.Bool("h[#{x},#{y}]")
38
+ end
39
+
40
+ # true - connected
41
+ # down from (x,y)
42
+ def vvar(x,y)
43
+ return unless on_board?(x,y) and y != @ysize - 1
44
+ Z3.Bool("v[#{x},#{y}]")
45
+ end
46
+
47
+ # Every cell has a number that shows its position within the loop
48
+ def nvar(x,y)
49
+ Z3.Int("n[#{x},#{y}]")
50
+ end
51
+
52
+ def hint_cell?(x,y)
53
+ !!@data[y][x]
54
+ end
55
+
56
+ def neighbours(x,y)
57
+ [
58
+ [x+1, y ],
59
+ [x-1, y ],
60
+ [x, y+1],
61
+ [x, y-1],
62
+ ].select{|nx,ny|
63
+ on_board?(nx, ny) and !hint_cell?(nx,ny)
64
+ }
65
+ end
66
+
67
+ def neighbour_cvars(x,y)
68
+ neighbours(x,y)
69
+ .map{|nx,ny| cvar(nx, ny)}
70
+ end
71
+
72
+ def connections_at(x,y)
73
+ [
74
+ hvar(x,y),
75
+ hvar(x-1,y),
76
+ vvar(x,y),
77
+ vvar(x,y-1),
78
+ ].compact
79
+ end
80
+
81
+ def count_loops_at(x,y)
82
+ Z3.Add(*connections_at(x,y).map{|n| n.ite(1,0)})
83
+ end
84
+
85
+ def assert_connections
86
+ each_xy do |x,y|
87
+ if hint_cell?(x, y)
88
+ @solver.assert count_loops_at(x,y) == 0
89
+ else
90
+ @solver.assert cvar(x,y).implies(count_loops_at(x,y) == 2)
91
+ @solver.assert (~cvar(x,y)).implies(count_loops_at(x,y) == 0)
92
+ end
93
+ end
94
+ end
95
+
96
+ def assert_no_black_multiples
97
+ each_xy do |x,y|
98
+ next if hint_cell?(x, y)
99
+ @solver.assert (~cvar(x,y)).implies(Z3.And(*neighbour_cvars(x,y)))
100
+ end
101
+ end
102
+
103
+ def stripe_left(x0,y)
104
+ (0...x0).map{|x| cvar(x,y) unless hint_cell?(x,y) }.compact
105
+ end
106
+
107
+ def stripe_right(x0,y)
108
+ (x0+1...@xsize).map{|x| cvar(x,y) unless hint_cell?(x,y) }.compact
109
+ end
110
+
111
+ def stripe_up(x,y0)
112
+ (0...y0).map{|y| cvar(x,y) unless hint_cell?(x,y) }.compact
113
+ end
114
+
115
+ def stripe_down(x,y0)
116
+ (y0+1...@ysize).map{|y| cvar(x,y) unless hint_cell?(x,y) }.compact
117
+ end
118
+
119
+ def assert_arrows
120
+ each_xy do |x,y|
121
+ if hint_cell?(x, y)
122
+ count = @data[y][x][0].to_i
123
+ dir = @data[y][x][1]
124
+ case dir
125
+ when "<", "←"
126
+ stripe = stripe_left(x,y)
127
+ when ">", "→"
128
+ stripe = stripe_right(x,y)
129
+ when "^", "↑"
130
+ stripe = stripe_up(x,y)
131
+ when "_", "↓"
132
+ stripe = stripe_down(x,y)
133
+ else
134
+ raise
135
+ end
136
+
137
+ @solver.assert Z3.Add(*stripe.map{|c| c.ite(0,1)}) == count
138
+ end
139
+ end
140
+ end
141
+
142
+ def each_xy
143
+ @ysize.times do |y|
144
+ @xsize.times do |x|
145
+ yield(x,y)
146
+ end
147
+ end
148
+ end
149
+
150
+ def connections_and_nvars(x,y)
151
+ [
152
+ [x+1, y, hvar(x, y)],
153
+ [x-1, y, hvar(x-1, y)],
154
+ [x, y+1, vvar(x, y)],
155
+ [x, y-1, vvar(x, y-1)],
156
+ ].select{|nx,ny,convar|
157
+ on_board?(nx, ny) and !hint_cell?(nx,ny)
158
+ }.map{|nx,ny,convar| [nvar(nx,ny), convar] }
159
+ end
160
+
161
+ # Loop will have Ns of 0+, all distinct and consecutive
162
+ # Non-loop will have Ns of MAX + cell_number
163
+ def assert_single_loop
164
+ # max_value = @xsize * @ysize + 10
165
+ max_value = 1000
166
+
167
+ each_xy do |x,y|
168
+ cell_idx = x + y * @xsize
169
+ @solver.assert nvar(x,y) >= 0
170
+ if hint_cell?(x,y)
171
+ @solver.assert nvar(x,y) == max_value + cell_idx
172
+ else
173
+ @solver.assert (~cvar(x,y)).implies( nvar(x,y) == max_value + cell_idx )
174
+
175
+ nval = connections_and_nvars(x,y)
176
+ .map{|n,c| c.ite(n+1, max_value) }
177
+ .reduce{|a,b| (a <= b).ite(a, b) }
178
+
179
+ @solver.assert cvar(x,y).implies((nvar(x,y) == 0) | (nvar(x,y) == nval) )
180
+ @solver.assert cvar(x,y).implies(nvar(x,y) < max_value)
181
+ end
182
+ end
183
+
184
+ # only one loop start
185
+ @solver.assert Z3.Add(
186
+ *enum_for(:each_xy).map{|x,y|
187
+ (nvar(x,y) == 0).ite(1,0)
188
+ }) == 1
189
+ end
190
+
191
+ def call
192
+ assert_connections
193
+ assert_no_black_multiples
194
+ assert_arrows
195
+ assert_single_loop
196
+
197
+ if @solver.satisfiable?
198
+ @model = @solver.model
199
+ print_answer
200
+ else
201
+ puts "failed to solve"
202
+ end
203
+ end
204
+
205
+ def hint_data(x,y)
206
+ u = @data[y][x]
207
+ n = u[0]
208
+ a = {
209
+ "<" => "←",
210
+ ">" => "→",
211
+ "_" => "↓",
212
+ "^" => "↑",
213
+ }[u[1]] || u[1]
214
+ n + a
215
+ end
216
+
217
+ # TODO: make it nicer
218
+ def print_answer
219
+ @ysize.times do |y|
220
+ @xsize.times do |x|
221
+ if hint_cell?(x,y)
222
+ print hint_data(x,y)
223
+ # print "O "
224
+ else
225
+ c = @model[cvar(x,y)].to_b
226
+ if c
227
+ print "* "
228
+ else
229
+ print "# "
230
+ end
231
+ end
232
+
233
+ next if x == @xsize - 1
234
+ if @model[hvar(x,y)].to_b
235
+ print "-"
236
+ else
237
+ print " "
238
+ end
239
+ end
240
+ print "\n"
241
+
242
+ next if y == @ysize-1
243
+ @xsize.times do |x|
244
+ if @model[vvar(x,y)].to_b
245
+ print "| "
246
+ else
247
+ print " "
248
+ end
249
+ end
250
+
251
+ print "\n"
252
+ end
253
+ end
254
+
255
+ def print_debug_loop_info
256
+ print "\n"
257
+ @ysize.times do |y|
258
+ @xsize.times do |x|
259
+ n = @model[nvar(x,y)].to_i
260
+ print "%8d" % n
261
+ end
262
+ print "\n"
263
+ end
264
+ end
265
+ end
266
+
267
+ path = ARGV[0] || Pathname(__dir__) + "yajilin-1.txt"
268
+ Yajilin.new(path).call
@@ -0,0 +1,10 @@
1
+ . . . . . . . . . .
2
+ . . . . 2↓ . . . . .
3
+ . . . . . . . . . .
4
+ . . 2↑ . . . . 2↑ . .
5
+ . . . 1↓ . . . . . .
6
+ . . . . . . 1→ . . .
7
+ . . . . . . . . 0→ .
8
+ 0↑ . . 1← . 1↓ . . . .
9
+ . . . . . . . . 2← .
10
+ . . . . . . . . . .
data/lib/z3/expr/expr.rb CHANGED
@@ -115,7 +115,7 @@ module Z3
115
115
  a.sort.new(Z3::LowLevel.mk_bvand(a, b))
116
116
  end
117
117
  else
118
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
118
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
119
119
  end
120
120
  end
121
121
 
@@ -129,7 +129,7 @@ module Z3
129
129
  a.sort.new(Z3::LowLevel.mk_bvor(a, b))
130
130
  end
131
131
  else
132
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
132
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
133
133
  end
134
134
  end
135
135
 
@@ -145,7 +145,7 @@ module Z3
145
145
  a.sort.new(Z3::LowLevel.mk_bvxor(a, b))
146
146
  end
147
147
  else
148
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
148
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
149
149
  end
150
150
  end
151
151