z3 0.0.20181229 → 0.0.20220320

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