z3 0.0.20181229 → 0.0.20220320

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.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -0
  3. data/README.md +0 -2
  4. data/Rakefile +8 -1
  5. data/examples/abc_path +187 -0
  6. data/examples/abc_path-1.txt +7 -0
  7. data/examples/algebra_problems +12 -12
  8. data/examples/aquarium +133 -0
  9. data/examples/aquarium-1.txt +11 -0
  10. data/examples/bridges +2 -2
  11. data/examples/bridges-1.txt +0 -0
  12. data/examples/cats_organized_neatly +133 -0
  13. data/examples/cats_organized_neatly-10.txt +15 -0
  14. data/examples/cats_organized_neatly-3.txt +8 -0
  15. data/examples/cats_organized_neatly-48.txt +32 -0
  16. data/examples/circuit_problems +4 -4
  17. data/examples/clogic_puzzle +2 -2
  18. data/examples/color_nonogram +150 -0
  19. data/examples/color_nonogram-1.txt +23 -0
  20. data/examples/crossflip +2 -4
  21. data/examples/dominion +153 -0
  22. data/examples/dominion-1.txt +8 -0
  23. data/examples/dominosa +133 -0
  24. data/examples/dominosa-1.txt +8 -0
  25. data/examples/eulero +99 -0
  26. data/examples/eulero-1.txt +5 -0
  27. data/examples/four_hackers_puzzle +2 -2
  28. data/examples/futoshiki +128 -0
  29. data/examples/futoshiki-1.txt +17 -0
  30. data/examples/kakurasu +73 -0
  31. data/examples/kakurasu-1.txt +2 -0
  32. data/examples/kakuro +2 -2
  33. data/examples/kakuro-1.txt +0 -0
  34. data/examples/killer_sudoku +88 -0
  35. data/examples/killer_sudoku-1.txt +17 -0
  36. data/examples/killer_sudoku-2.txt +53 -0
  37. data/examples/kinematics_problems +20 -20
  38. data/examples/knights_puzzle +2 -2
  39. data/examples/kropki +100 -0
  40. data/examples/kropki-1.txt +13 -0
  41. data/examples/letter_connections +2 -2
  42. data/examples/letter_connections-1.txt +0 -0
  43. data/examples/light_up +2 -2
  44. data/examples/light_up-1.txt +0 -0
  45. data/examples/minisudoku +2 -2
  46. data/examples/minisudoku-1.txt +0 -0
  47. data/examples/miracle_sudoku +135 -0
  48. data/examples/miracle_sudoku-1.txt +9 -0
  49. data/examples/mortal_coil_puzzle +2 -2
  50. data/examples/mortal_coil_puzzle-9.txt +0 -0
  51. data/examples/nanro +245 -0
  52. data/examples/nanro-1.txt +8 -0
  53. data/examples/nine_clocks +106 -0
  54. data/examples/nonogram +2 -2
  55. data/examples/pyramid_nonogram +2 -2
  56. data/examples/regexp_crossword/beginner-1.txt +0 -0
  57. data/examples/regexp_crossword/beginner-2.txt +0 -0
  58. data/examples/regexp_crossword/beginner-3.txt +0 -0
  59. data/examples/regexp_crossword/beginner-4.txt +0 -0
  60. data/examples/regexp_crossword/beginner-5.txt +0 -0
  61. data/examples/regexp_crossword/experienced-1.txt +0 -0
  62. data/examples/regexp_crossword/experienced-2.txt +0 -0
  63. data/examples/regexp_crossword/experienced-3.txt +0 -0
  64. data/examples/regexp_crossword/experienced-4.txt +0 -0
  65. data/examples/regexp_crossword/experienced-5.txt +0 -0
  66. data/examples/regexp_crossword/tutorial-1.txt +0 -0
  67. data/examples/regexp_crossword/tutorial-2.txt +0 -0
  68. data/examples/regexp_crossword/tutorial-3.txt +0 -0
  69. data/examples/regexp_crossword/tutorial-4.txt +0 -0
  70. data/examples/regexp_crossword/tutorial-5.txt +0 -0
  71. data/examples/regexp_crossword/tutorial-6.txt +0 -0
  72. data/examples/regexp_crossword/tutorial-7.txt +0 -0
  73. data/examples/regexp_crossword/tutorial-8.txt +0 -0
  74. data/examples/regexp_crossword/tutorial-9.txt +0 -0
  75. data/examples/regexp_crossword_solver +2 -2
  76. data/examples/regexp_solver +2 -2
  77. data/examples/regexp_string_matcher.rb +0 -0
  78. data/examples/renzoku +124 -0
  79. data/examples/renzoku-1.txt +17 -0
  80. data/examples/sandwich_sudoku +101 -0
  81. data/examples/sandwich_sudoku-1.txt +10 -0
  82. data/examples/selfref +2 -2
  83. data/examples/simple_regexp_parser.rb +0 -0
  84. data/examples/skyscrapers +118 -0
  85. data/examples/skyscrapers-1.txt +6 -0
  86. data/examples/skyscrapers-2.txt +11 -0
  87. data/examples/star_battle +134 -0
  88. data/examples/star_battle-1.txt +10 -0
  89. data/examples/stitches +180 -0
  90. data/examples/stitches-1.txt +11 -0
  91. data/examples/sudoku +2 -2
  92. data/examples/sudoku-1.txt +0 -0
  93. data/examples/suguru +199 -0
  94. data/examples/suguru-1.txt +17 -0
  95. data/examples/verbal_arithmetic +2 -2
  96. data/examples/yajilin +268 -0
  97. data/examples/yajilin-1.txt +10 -0
  98. data/lib/z3/ast.rb +0 -0
  99. data/lib/z3/context.rb +0 -0
  100. data/lib/z3/exception.rb +0 -0
  101. data/lib/z3/expr/arith_expr.rb +0 -0
  102. data/lib/z3/expr/array_expr.rb +0 -0
  103. data/lib/z3/expr/bitvec_expr.rb +47 -2
  104. data/lib/z3/expr/bool_expr.rb +0 -0
  105. data/lib/z3/expr/expr.rb +3 -3
  106. data/lib/z3/expr/float_expr.rb +0 -0
  107. data/lib/z3/expr/int_expr.rb +0 -0
  108. data/lib/z3/expr/real_expr.rb +0 -0
  109. data/lib/z3/expr/rounding_mode_expr.rb +0 -0
  110. data/lib/z3/expr/set_expr.rb +0 -0
  111. data/lib/z3/func_decl.rb +0 -0
  112. data/lib/z3/goal.rb +0 -0
  113. data/lib/z3/hacks.rb +0 -0
  114. data/lib/z3/interface.rb +0 -0
  115. data/lib/z3/low_level.rb +0 -0
  116. data/lib/z3/low_level_auto.rb +138 -10
  117. data/lib/z3/model.rb +0 -0
  118. data/lib/z3/optimize.rb +0 -0
  119. data/lib/z3/printer.rb +0 -0
  120. data/lib/z3/probe.rb +0 -0
  121. data/lib/z3/solver.rb +0 -0
  122. data/lib/z3/sort/array_sort.rb +0 -0
  123. data/lib/z3/sort/bitvec_sort.rb +0 -0
  124. data/lib/z3/sort/bool_sort.rb +0 -0
  125. data/lib/z3/sort/float_sort.rb +0 -0
  126. data/lib/z3/sort/int_sort.rb +0 -0
  127. data/lib/z3/sort/real_sort.rb +0 -0
  128. data/lib/z3/sort/rounding_mode_sort.rb +0 -0
  129. data/lib/z3/sort/set_sort.rb +0 -0
  130. data/lib/z3/sort/sort.rb +0 -0
  131. data/lib/z3/tactic.rb +0 -0
  132. data/lib/z3/very_low_level.rb +5 -1
  133. data/lib/z3/very_low_level_auto.rb +35 -3
  134. data/lib/z3.rb +0 -0
  135. data/spec/array_expr_spec.rb +0 -0
  136. data/spec/array_sort_spec.rb +0 -0
  137. data/spec/bitvec_expr_spec.rb +13 -0
  138. data/spec/bitvec_sort_spec.rb +0 -0
  139. data/spec/bool_expr_spec.rb +0 -0
  140. data/spec/bool_sort_spec.rb +0 -0
  141. data/spec/coverage_helper.rb +0 -0
  142. data/spec/expr_spec.rb +0 -0
  143. data/spec/float_expr_spec.rb +0 -0
  144. data/spec/float_sort_spec.rb +0 -0
  145. data/spec/goal_spec.rb +0 -0
  146. data/spec/int_expr_spec.rb +0 -0
  147. data/spec/int_sort_spec.rb +0 -0
  148. data/spec/integration/abc_path_spec.rb +21 -0
  149. data/spec/integration/algebra_problems_spec.rb +0 -0
  150. data/spec/integration/aquarium_spec.rb +27 -0
  151. data/spec/integration/basic_int_math_spec.rb +0 -0
  152. data/spec/integration/basic_logic_spec.rb +0 -0
  153. data/spec/integration/bit_tricks_spec.rb +0 -0
  154. data/spec/integration/bridges_spec.rb +0 -0
  155. data/spec/integration/cats_organized_neatly_spec.rb +14 -0
  156. data/spec/integration/cicruit_problem_spec.rb +0 -0
  157. data/spec/integration/color_nonogram_spec.rb +28 -0
  158. data/spec/integration/crossflip_spec.rb +0 -0
  159. data/spec/integration/dominion_spec.rb +14 -0
  160. data/spec/integration/dominosa_spec.rb +21 -0
  161. data/spec/integration/eulero_spec.rb +11 -0
  162. data/spec/integration/four_hackers_puzzle_spec.rb +0 -0
  163. data/spec/integration/futoshiki_spec.rb +23 -0
  164. data/spec/integration/geometry_problem_spec.rb +0 -0
  165. data/spec/integration/kakurasu_spec.rb +18 -0
  166. data/spec/integration/kakuro_spec.rb +0 -0
  167. data/spec/integration/killer_sudoku_spec.rb +10 -0
  168. data/spec/integration/kinematics_problems_spec.rb +0 -0
  169. data/spec/integration/knights_puzzle_spec.rb +11 -11
  170. data/spec/integration/kropki_spec.rb +19 -0
  171. data/spec/integration/letter_connections_spec.rb +0 -0
  172. data/spec/integration/light_up_spec.rb +0 -0
  173. data/spec/integration/minisudoku_spec.rb +0 -0
  174. data/spec/integration/miracle_sudoku_spec.rb +15 -0
  175. data/spec/integration/mortal_coil_puzzle_spec.rb +8 -6
  176. data/spec/integration/nanro_spec.rb +39 -0
  177. data/spec/integration/nine_clocks_spec.rb +30 -0
  178. data/spec/integration/nonogram_spec.rb +0 -0
  179. data/spec/integration/oneofus_spec.rb +0 -0
  180. data/spec/integration/pyramid_nonogram_spec.rb +0 -0
  181. data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
  182. data/spec/integration/regexp_solver_spec.rb +0 -0
  183. data/spec/integration/renzoku_spec.rb +23 -0
  184. data/spec/integration/sandwich_sudoku_spec.rb +15 -0
  185. data/spec/integration/selfref_spec.rb +0 -0
  186. data/spec/integration/skyscraper_spec.rb +10 -0
  187. data/spec/integration/star_battle_spec.rb +27 -0
  188. data/spec/integration/stitches_spec.rb +25 -0
  189. data/spec/integration/sudoku_spec.rb +0 -0
  190. data/spec/integration/suguru_spec.rb +23 -0
  191. data/spec/integration/verbal_arithmetic_spec.rb +0 -0
  192. data/spec/integration/yajilin_spec.rb +25 -0
  193. data/spec/integration/zebra_puzzle_spec.rb +0 -0
  194. data/spec/interface_spec.rb +0 -0
  195. data/spec/model_spec.rb +0 -0
  196. data/spec/optimize_spec.rb +3 -1
  197. data/spec/printer_spec.rb +0 -0
  198. data/spec/probe_spec.rb +0 -0
  199. data/spec/real_expr_spec.rb +0 -0
  200. data/spec/real_sort_spec.rb +0 -0
  201. data/spec/rounding_mode_expr_spec.rb +0 -0
  202. data/spec/rounding_mode_sort_spec.rb +0 -0
  203. data/spec/set_expr_spec.rb +15 -9
  204. data/spec/set_sort_spec.rb +0 -0
  205. data/spec/solver_spec.rb +1 -2
  206. data/spec/sort_spec.rb +0 -0
  207. data/spec/spec_helper.rb +15 -0
  208. data/spec/tactic_spec.rb +0 -0
  209. data/spec/z3_spec.rb +0 -0
  210. metadata +85 -5
data/examples/renzoku ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class Renzoku
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 dot_right?(x, y)
26
+ v = @data[y*2][x*2 + 1]
27
+ if v == "."
28
+ true
29
+ elsif v == " "
30
+ false
31
+ else
32
+ raise "Bad dot value"
33
+ end
34
+ end
35
+
36
+ def dot_bottom?(x, y)
37
+ v = @data[y*2 + 1][x*2]
38
+ if v == "."
39
+ true
40
+ elsif v == " " or v == nil
41
+ false
42
+ else
43
+ raise "Bad dot value"
44
+ end
45
+ end
46
+
47
+ def call
48
+ @vars = {}
49
+ @size.times do |y|
50
+ @size.times do |x|
51
+ v = Z3.Int("v#{x},#{y}")
52
+ @vars[[x,y]] = v
53
+ cv = cell_value(x, y)
54
+ if cv
55
+ @solver.assert (v == cv)
56
+ else
57
+ @solver.assert (v >= 1) & (v <= @size)
58
+ end
59
+ end
60
+ end
61
+
62
+ @size.times do |x|
63
+ line = @size.times.map{|y| @vars[[x,y]] }
64
+ @solver.assert Z3.Distinct(*line)
65
+ end
66
+
67
+ @size.times do |y|
68
+ line = @size.times.map{|x| @vars[[x,y]] }
69
+ @solver.assert Z3.Distinct(*line)
70
+ end
71
+
72
+ # Dots right
73
+ (0..@size-1).each do |y|
74
+ (0..@size-2).each do |x|
75
+ lv = @vars[[x,y]]
76
+ rv = @vars[[x+1,y]]
77
+ if dot_right?(x,y)
78
+ @solver.assert (lv == (rv + 1)) | (lv == (rv - 1))
79
+ else
80
+ @solver.assert lv != (rv + 1)
81
+ @solver.assert lv != (rv - 1)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Dots bottom
87
+ (0..@size-2).each do |y|
88
+ (0..@size-1).each do |x|
89
+ tv = @vars[[x,y]]
90
+ bv = @vars[[x,y+1]]
91
+ if dot_bottom?(x,y)
92
+ @solver.assert (tv == (bv + 1)) | (tv == (bv - 1))
93
+ else
94
+ @solver.assert tv != (bv + 1)
95
+ @solver.assert tv != (bv - 1)
96
+ end
97
+ end
98
+ end
99
+
100
+ if @solver.satisfiable?
101
+ @model = @solver.model
102
+ print_answer!
103
+ else
104
+ puts "failed to solve"
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def print_answer!
111
+ output = @data.map(&:dup)
112
+ @size.times do |y|
113
+ @size.times do |x|
114
+ v = @model[@vars[[x,y]]]
115
+ output[2*y][2*x] = "#{v}"
116
+ end
117
+ end
118
+
119
+ puts output
120
+ end
121
+ end
122
+
123
+ path = ARGV[0] || Pathname(__dir__) + "renzoku-1.txt"
124
+ Renzoku.new(path).call
@@ -0,0 +1,17 @@
1
+ # # # # # #.# #.#
2
+ . . . .
3
+ #.# #.# # #.#.# #
4
+ . .
5
+ # # 2 #.# #.# # #
6
+
7
+ 4 #.# #.# # # # #
8
+ . . .
9
+ # # # # # # #.# #
10
+ .
11
+ # #.# # # #.# 2 #
12
+ . .
13
+ 1.# # #.# # # # #
14
+ . .
15
+ # # # #.#.#.#.# 2
16
+ . .
17
+ # #.# # # # # # #
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+ require "paint"
6
+
7
+ class SandwichSudokuSolver
8
+ def initialize(path)
9
+ data = Pathname(path).read
10
+ data = data.strip.split("\n").map do |line|
11
+ line.split.map{|c| c =~ /\A\d+\z/ ? c.to_i : nil}
12
+ end
13
+ @col_counts = data.shift
14
+ @row_counts = data.map(&:shift)
15
+ @xsize = @col_counts.size
16
+ @ysize = @row_counts.size
17
+ @data = data
18
+ raise unless data.size == @ysize
19
+ raise unless data.all?{|r| r.size == @ysize}
20
+ @solver = Z3::Solver.new
21
+ end
22
+
23
+ def call
24
+ @cells = (0..8).map do |y|
25
+ (0..8).map do |x|
26
+ cell_var(@data[y][x], x, y)
27
+ end
28
+ end
29
+
30
+ @cells.each do |row|
31
+ @solver.assert Z3.Distinct(*row)
32
+ end
33
+ @cells.transpose.each do |column|
34
+ @solver.assert Z3.Distinct(*column)
35
+ end
36
+ @cells.each_slice(3) do |rows|
37
+ rows.transpose.each_slice(3) do |square|
38
+ @solver.assert Z3.Distinct(*square.flatten)
39
+ end
40
+ end
41
+
42
+ 9.times do |x|
43
+ assert_sandwich "c#{x+1}", @col_counts[x], col_vars(x)
44
+ end
45
+
46
+ 9.times do |y|
47
+ assert_sandwich "s#{y+1}", @row_counts[y], row_vars(y)
48
+ end
49
+
50
+ # TODO: SANDWICHES
51
+
52
+ if @solver.satisfiable?
53
+ @model = @solver.model
54
+ print_answer!
55
+ else
56
+ puts "failed to solve"
57
+ end
58
+ end
59
+
60
+ def assert_sandwich(name, count, vars)
61
+ ss = Z3.Int("#{name}-ss")
62
+ se = Z3.Int("#{name}-se")
63
+ @solver.assert ss >= 0
64
+ @solver.assert ss <= 8
65
+ @solver.assert se >= 0
66
+ @solver.assert se <= 8
67
+ @solver.assert ss < se
68
+ 9.times do |i|
69
+ @solver.assert ((vars[i] == 1) | (vars[i] == 9)) == ((ss == i) | (se == i))
70
+ end
71
+ e = 9.times.map{|i|
72
+ Z3.And(i > ss, i < se).ite(vars[i], 0)
73
+ }
74
+ @solver.assert Z3.Add(*e) == count
75
+ end
76
+
77
+ def row_vars(y)
78
+ @cells[y]
79
+ end
80
+
81
+ def col_vars(x)
82
+ @cells.map{|line| line[x]}
83
+ end
84
+
85
+ def cell_var(cell, x, y)
86
+ v = Z3.Int("cell[#{x+1},#{y+1}]")
87
+ @solver.assert v >= 1
88
+ @solver.assert v <= 9
89
+ @solver.assert v == cell if cell != nil
90
+ v
91
+ end
92
+
93
+ def print_answer!
94
+ @cells.each do |row|
95
+ puts row.map{|v| @model[v]}.join(" ")
96
+ end
97
+ end
98
+ end
99
+
100
+ path = ARGV[0] || Pathname(__dir__) + "sandwich_sudoku-1.txt"
101
+ SandwichSudokuSolver.new(path).call
@@ -0,0 +1,10 @@
1
+ 19 7 15 19 4 0 6 9 35
2
+ 5 . . . . . . . . 1
3
+ 13 . . . . . . . . .
4
+ 20 . . . . . . . . .
5
+ 9 . . . . . . . . .
6
+ 12 . . . . 1 . . . .
7
+ 0 . . . . . . . . .
8
+ 4 . . . . . . . . .
9
+ 14 . . . . . . . . .
10
+ 5 . . . . . . . . .
data/examples/selfref CHANGED
@@ -30,7 +30,7 @@ class SelfRefPuzzleSolver
30
30
  Z3.Or(*ary.map{|i| cons_answers == i})
31
31
  end
32
32
 
33
- def solve!
33
+ def call
34
34
  @a_answers = Z3.Add(*(1..20).map{|i| @a[i][1]})
35
35
  @b_answers = Z3.Add(*(1..20).map{|i| @a[i][2]})
36
36
  @c_answers = Z3.Add(*(1..20).map{|i| @a[i][3]})
@@ -205,7 +205,7 @@ class SelfRefPuzzleSolver
205
205
  end
206
206
 
207
207
 
208
- SelfRefPuzzleSolver.new.solve!
208
+ SelfRefPuzzleSolver.new.call
209
209
 
210
210
  __END__
211
211
  http://faculty.uml.edu/jpropp/srat-Q.txt
File without changes
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class SkyscrapersSolver
7
+ def initialize(path)
8
+ parse_data(Pathname(path).read)
9
+ @solver = Z3::Solver.new
10
+ end
11
+
12
+ def call
13
+ setup_grid_vars
14
+ setup_constraints
15
+
16
+ if @solver.satisfiable?
17
+ @model = @solver.model
18
+ print_answer
19
+ else
20
+ puts "No solution"
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def setup_grid_vars
27
+ @gridvars = @size.times.map do |y|
28
+ @size.times.map do |x|
29
+ v = Z3.Int("g#{x},#{y}")
30
+ if @grid[y][x]
31
+ @solver.assert v == @grid[y][x]
32
+ else
33
+ @solver.assert v >= 1
34
+ @solver.assert v <= @size
35
+ end
36
+ v
37
+ end
38
+ end
39
+ [*@gridvars, *@gridvars.transpose].each do |row|
40
+ @solver.assert Z3.Distinct(*row)
41
+ end
42
+ end
43
+
44
+ def setup_constraints
45
+ @size.times do |i|
46
+ setup_visibility "L#{i}", @left[i], @gridvars[i]
47
+ setup_visibility "R#{i}", @right[i], @gridvars[i].reverse
48
+ setup_visibility "T#{i}", @top[i], @gridvars.map{|row| row[i] }
49
+ setup_visibility "B#{i}", @bottom[i], @gridvars.map{|row| row[i] }.reverse
50
+ end
51
+ end
52
+
53
+ def setup_visibility(label, expected, vars)
54
+ return unless expected
55
+ # Variables between cells:
56
+ # - count seen
57
+ # - max seen
58
+
59
+ count_vars = (0..@size).map{|i| Z3.Int("#{label}-c#{i}") }
60
+ max_vars = (0..@size).map{|i| Z3.Int("#{label}-m#{i}") }
61
+
62
+ @solver.assert count_vars.first == expected
63
+ @solver.assert max_vars.first == 0
64
+
65
+ @size.times do |i|
66
+ count_near = count_vars[i]
67
+ count_far = count_vars[i+1]
68
+ max_near = max_vars[i]
69
+ max_far = max_vars[i+1]
70
+ current = vars[i]
71
+ visible = Z3.Bool("#{label}-v#{i}")
72
+ @solver.assert visible == (current > max_near)
73
+ @solver.assert count_near == Z3.IfThenElse(visible, count_far+1, count_far)
74
+ @solver.assert max_far == Z3.IfThenElse(visible, current, max_near)
75
+ end
76
+
77
+ @solver.assert count_vars.last == 0
78
+ # This is redundant:
79
+ @solver.assert max_vars.last == @size
80
+ end
81
+
82
+ def print_answer
83
+ @size.times do |y|
84
+ @size.times do |x|
85
+ v = @model[@gridvars[y][x]]
86
+ print "#{v} "
87
+ end
88
+ print "\n"
89
+ end
90
+ end
91
+
92
+ def parse_data(data)
93
+ data = data.lines.map do |line|
94
+ line.split.map do |x|
95
+ if x =~ /\d+/
96
+ x.to_i
97
+ elsif x == "." or x == "-"
98
+ nil
99
+ else
100
+ raise "Unrecognized symbol #{x.inspect} in input"
101
+ end
102
+ end
103
+ end
104
+
105
+ @top = data.shift
106
+ @bottom = data.pop
107
+ @left = data.map(&:shift)
108
+ @right = data.map(&:pop)
109
+ @size = @top.size
110
+ raise "Grid must be square" unless [@top.size, @bottom.size, @left.size, @right.size, *data.map(&:size)].uniq.size == 1
111
+ @grid = data
112
+ end
113
+
114
+ end
115
+
116
+ path = ARGV[0] || Pathname(__dir__) + "skyscrapers-1.txt"
117
+ SkyscrapersSolver.new(path).call
118
+
@@ -0,0 +1,6 @@
1
+ 2 - - -
2
+ - . . 1 . -
3
+ - . . . . 3
4
+ - . . . 2 -
5
+ - . . . . -
6
+ 3 - - 1
@@ -0,0 +1,11 @@
1
+ 3 - 4 - - 3 3 3 3
2
+ - . . . . . . . . 6 -
3
+ - . 1 . . . . . . . -
4
+ - 1 6 . . . . . . . -
5
+ - . . . 3 . . . . . 3
6
+ 4 . . . 7 . . . . . 3
7
+ 3 . . . . . 5 . . . 1
8
+ 4 . . . 4 . . . 6 5 3
9
+ - . . 4 . . . . . . 3
10
+ - . . . . . 2 6 . . -
11
+ - - - 3 4 2 4 4 -
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class StarBattle
7
+ def initialize(path)
8
+ @data = Pathname(path).readlines.map(&:chomp).map(&:split)
9
+ @size = @data.size
10
+ raise unless @data.all?{|row| row.size == @size}
11
+ raise unless containers.size == @size
12
+ @solver = Z3::Solver.new
13
+ end
14
+
15
+ def call
16
+ # Each row contains 2 stars
17
+ @size.times do |y|
18
+ sum = Z3.Add(*@size.times.map{|x| cell_var(x, y).ite(1, 0) })
19
+ @solver.assert sum == 2
20
+ end
21
+
22
+ # Each column contains 2 stars
23
+ @size.times do |x|
24
+ sum = Z3.Add(*@size.times.map{|y| cell_var(x, y).ite(1, 0) })
25
+ @solver.assert sum == 2
26
+ end
27
+
28
+ # Each container contains 2 stars
29
+ coords.group_by{|x,y| container_at(x,y) }.each do |name, cells|
30
+ sum = Z3.Add(*cells.map{|x,y| cell_var(x, y).ite(1, 0) })
31
+ @solver.assert sum == 2
32
+ end
33
+
34
+ # Can't be adjacent
35
+ coords.each do |x, y|
36
+ @solver.assert cell_var(x, y).implies !cell_var(x+1, y)
37
+ @solver.assert cell_var(x, y).implies !cell_var(x-1, y+1)
38
+ @solver.assert cell_var(x, y).implies !cell_var(x, y+1)
39
+ @solver.assert cell_var(x, y).implies !cell_var(x+1, y+1)
40
+ end
41
+
42
+ if @solver.satisfiable?
43
+ @model = @solver.model
44
+ print_answer
45
+ else
46
+ puts "failed to solve"
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def coords
53
+ @size.times.flat_map do |y|
54
+ @size.times.map do |x|
55
+ [x,y]
56
+ end
57
+ end
58
+ end
59
+
60
+ def containers
61
+ @containers ||= @data.flatten.uniq.sort
62
+ end
63
+
64
+ def cell_var(x, y)
65
+ return nil unless (0...@size).include?(x)
66
+ return nil unless (0...@size).include?(y)
67
+ Z3.Bool("c#{x},#{y}")
68
+ end
69
+
70
+ def container_at(x, y)
71
+ return nil unless (0...@size).include?(x)
72
+ return nil unless (0...@size).include?(y)
73
+ @data[y][x]
74
+ end
75
+
76
+ def print_corner(x, y)
77
+ if [
78
+ container_at(x, y),
79
+ container_at(x, y-1),
80
+ container_at(x-1, y),
81
+ container_at(x-1, y-1),
82
+ ].uniq.size == 1
83
+ print " "
84
+ else
85
+ print "+"
86
+ end
87
+ end
88
+
89
+ def print_vertical(x, y)
90
+ if x == 0 or container_at(x, y) != container_at(x-1, y)
91
+ print "|"
92
+ else
93
+ print " "
94
+ end
95
+ end
96
+
97
+ def print_horizontal(x, y)
98
+ if y == 0 or container_at(x, y) != container_at(x, y-1)
99
+ print "-"
100
+ else
101
+ print " "
102
+ end
103
+ end
104
+
105
+ def print_cell(x, y)
106
+ if @model[cell_var(x, y)].to_b
107
+ print "*"
108
+ else
109
+ print " "
110
+ end
111
+ end
112
+
113
+ def print_answer
114
+ (0..@size).each do |y|
115
+ (0..@size).each do |x|
116
+ print_corner x, y
117
+ next if x == @size
118
+ print_horizontal x, y
119
+ end
120
+ print "\n"
121
+
122
+ next if y == @size
123
+ (0..@size).each do |x|
124
+ print_vertical x, y
125
+ next if x == @size
126
+ print_cell x, y
127
+ end
128
+ print "\n"
129
+ end
130
+ end
131
+ end
132
+
133
+ path = ARGV[0] || Pathname(__dir__) + "star_battle-1.txt"
134
+ StarBattle.new(path).call
@@ -0,0 +1,10 @@
1
+ a a a a a a a a a b
2
+ a c a d a a a a a b
3
+ a c a d d d d d b b
4
+ c c c d j j d b b b
5
+ c j j d d j d h e e
6
+ j j j j j j d h e e
7
+ j j j i i i i h h e
8
+ j g g g i i i h e e
9
+ f g g f f h i h e e
10
+ f f f f f h h h h h