z3 0.0.20180624 → 0.0.20211213

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -4
  3. data/Rakefile +15 -8
  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/simple_regexp_parser.rb +58 -56
  57. data/examples/skyscrapers +118 -0
  58. data/examples/skyscrapers-1.txt +6 -0
  59. data/examples/skyscrapers-2.txt +11 -0
  60. data/examples/star_battle +134 -0
  61. data/examples/star_battle-1.txt +10 -0
  62. data/examples/stitches +180 -0
  63. data/examples/stitches-1.txt +11 -0
  64. data/examples/sudoku +2 -2
  65. data/examples/suguru +199 -0
  66. data/examples/suguru-1.txt +17 -0
  67. data/examples/verbal_arithmetic +2 -2
  68. data/examples/yajilin +268 -0
  69. data/examples/yajilin-1.txt +10 -0
  70. data/lib/z3/ast.rb +8 -0
  71. data/lib/z3/expr/bitvec_expr.rb +10 -0
  72. data/lib/z3/expr/expr.rb +16 -15
  73. data/lib/z3/low_level.rb +6 -2
  74. data/lib/z3/low_level_auto.rb +180 -36
  75. data/lib/z3/optimize.rb +5 -4
  76. data/lib/z3/printer.rb +29 -1
  77. data/lib/z3/sort/bitvec_sort.rb +1 -0
  78. data/lib/z3/sort/sort.rb +9 -5
  79. data/lib/z3/very_low_level.rb +8 -5
  80. data/lib/z3/very_low_level_auto.rb +45 -9
  81. data/spec/bitvec_expr_spec.rb +35 -21
  82. data/spec/bitvec_sort_spec.rb +4 -0
  83. data/spec/expr_spec.rb +62 -0
  84. data/spec/integration/abc_path_spec.rb +21 -0
  85. data/spec/integration/aquarium_spec.rb +27 -0
  86. data/spec/integration/cats_organized_neatly_spec.rb +14 -0
  87. data/spec/integration/color_nonogram_spec.rb +28 -0
  88. data/spec/integration/dominion_spec.rb +14 -0
  89. data/spec/integration/dominosa_spec.rb +21 -0
  90. data/spec/integration/eulero_spec.rb +11 -0
  91. data/spec/integration/futoshiki_spec.rb +23 -0
  92. data/spec/integration/kakurasu_spec.rb +18 -0
  93. data/spec/integration/killer_sudoku_spec.rb +10 -0
  94. data/spec/integration/knights_puzzle_spec.rb +11 -11
  95. data/spec/integration/kropki_spec.rb +19 -0
  96. data/spec/integration/miracle_sudoku_spec.rb +15 -0
  97. data/spec/integration/mortal_coil_puzzle_spec.rb +8 -6
  98. data/spec/integration/nanro_spec.rb +39 -0
  99. data/spec/integration/nine_clocks_spec.rb +30 -0
  100. data/spec/integration/oneofus_spec.rb +7 -15
  101. data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
  102. data/spec/integration/renzoku_spec.rb +23 -0
  103. data/spec/integration/sandwich_sudoku_spec.rb +15 -0
  104. data/spec/integration/skyscraper_spec.rb +10 -0
  105. data/spec/integration/star_battle_spec.rb +27 -0
  106. data/spec/integration/stitches_spec.rb +25 -0
  107. data/spec/integration/suguru_spec.rb +23 -0
  108. data/spec/integration/yajilin_spec.rb +25 -0
  109. data/spec/interface_spec.rb +18 -0
  110. data/spec/optimize_spec.rb +6 -4
  111. data/spec/printer_spec.rb +30 -0
  112. data/spec/set_expr_spec.rb +14 -8
  113. data/spec/solver_spec.rb +4 -5
  114. data/spec/spec_helper.rb +15 -0
  115. metadata +104 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c5b58b092ac3d83d29341b7286e74068497c6f3e59232b8b0e23373a5e2e4eb
4
- data.tar.gz: 5dfef87be452768052ece356eb0cd52f0725556769937d0030b1403a9422946e
3
+ metadata.gz: c73d508e9644e0c32681754a8f33959bc54d838c21d13f896223225e8b275a76
4
+ data.tar.gz: 12e017b721b97aaec2d8e1a75145f0c5d3f0769eee2badc89e08e2b0ff11fef1
5
5
  SHA512:
6
- metadata.gz: 209cd2c1ced0b55bfad330f725b296075b14651af5d7faee5e236e920a9c48d8e65db30a7b31aabc0b4744aca2389aae8ea5db78815e9fbde5c8f9e379fba091
7
- data.tar.gz: 43d4e8ec32e9a3eb433ea6a73b9e713ccd75182be3702be42ee2f60592a1c61a5a56ba0c0aca3711477fa7d6d5dfe85e21b5922701b38bdbef2882ce57569d71
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.6.
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::VeryLowLever` and `Z3::LowLevel` are FFI interface 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.
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
- system "./api/gen_api api/definitions.h"
17
+ sh "./api/gen_api api/definitions.h"
11
18
  end
12
19
 
13
20
  desc "Clean up"
14
21
  task "clean" do
15
- system "trash z3-*.gem coverage"
22
+ sh "trash z3-*.gem coverage"
16
23
  end
17
24
 
18
25
  desc "Run tests"
19
26
  task "spec" do
20
- system "rspec"
27
+ sh "rspec"
21
28
  end
22
29
 
23
30
  desc "Run unit tests"
24
31
  task "spec:unit" do
25
- system "rspec spec/*_spec.rb"
32
+ sh "rspec spec/*_spec.rb"
26
33
  end
27
34
 
28
35
  desc "Run integration tests"
29
36
  task "spec:integration" do
30
- system "rspec spec/integration/*_spec.rb"
37
+ sh "rspec spec/integration/*_spec.rb"
31
38
  end
32
39
 
33
40
  desc "Build gem"
34
41
  task "gem:build" do
35
- system "gem build z3.gemspec"
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
- system "gem", "push", gem_file
48
+ sh "gem", "push", gem_file
42
49
  end
43
50
 
44
51
  desc "Report missing APIs"
45
52
  task "coverage:missing" do
46
- system "COVERAGE=1 rake test"
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
@@ -0,0 +1,7 @@
1
+ C O R D F B U
2
+ N . . . . . L
3
+ G . . . . . I
4
+ Q . . . . . H
5
+ V . . . . . S
6
+ T . . . . . X
7
+ J P M W K Y E
@@ -21,7 +21,7 @@ class AlgebraProblem
21
21
  end
22
22
 
23
23
  class AlgebraProblem01 < AlgebraProblem
24
- def solve!
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 solve!
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 solve!
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 solve!
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 solve!
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 solve!
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.solve!
121
- AlgebraProblem03.new.solve!
122
- AlgebraProblem04.new.solve!
123
- AlgebraProblem05.new.solve!
124
- AlgebraProblem06.new.solve!
125
- AlgebraProblem10.new.solve!
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 solve!
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).solve!
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
@@ -0,0 +1,15 @@
1
+ ...
2
+ . .
3
+ ....
4
+ ....
5
+
6
+ a
7
+ aa
8
+
9
+ bbb
10
+
11
+ cc
12
+ c
13
+
14
+ dd
15
+ dd
@@ -0,0 +1,8 @@
1
+ ...
2
+ ...
3
+ ...
4
+
5
+ aaa
6
+
7
+ bbb
8
+ bbb