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.
- checksums.yaml +4 -4
- data/.rspec +0 -0
- 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/bridges-1.txt +0 -0
- 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/kakuro-1.txt +0 -0
- 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/letter_connections-1.txt +0 -0
- data/examples/light_up +2 -2
- data/examples/light_up-1.txt +0 -0
- data/examples/minisudoku +2 -2
- data/examples/minisudoku-1.txt +0 -0
- data/examples/miracle_sudoku +135 -0
- data/examples/miracle_sudoku-1.txt +9 -0
- data/examples/mortal_coil_puzzle +2 -2
- data/examples/mortal_coil_puzzle-9.txt +0 -0
- 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/beginner-1.txt +0 -0
- data/examples/regexp_crossword/beginner-2.txt +0 -0
- data/examples/regexp_crossword/beginner-3.txt +0 -0
- data/examples/regexp_crossword/beginner-4.txt +0 -0
- data/examples/regexp_crossword/beginner-5.txt +0 -0
- data/examples/regexp_crossword/experienced-1.txt +0 -0
- data/examples/regexp_crossword/experienced-2.txt +0 -0
- data/examples/regexp_crossword/experienced-3.txt +0 -0
- data/examples/regexp_crossword/experienced-4.txt +0 -0
- data/examples/regexp_crossword/experienced-5.txt +0 -0
- data/examples/regexp_crossword/tutorial-1.txt +0 -0
- data/examples/regexp_crossword/tutorial-2.txt +0 -0
- data/examples/regexp_crossword/tutorial-3.txt +0 -0
- data/examples/regexp_crossword/tutorial-4.txt +0 -0
- data/examples/regexp_crossword/tutorial-5.txt +0 -0
- data/examples/regexp_crossword/tutorial-6.txt +0 -0
- data/examples/regexp_crossword/tutorial-7.txt +0 -0
- data/examples/regexp_crossword/tutorial-8.txt +0 -0
- data/examples/regexp_crossword/tutorial-9.txt +0 -0
- data/examples/regexp_crossword_solver +2 -2
- data/examples/regexp_solver +2 -2
- data/examples/regexp_string_matcher.rb +0 -0
- 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/simple_regexp_parser.rb +0 -0
- 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/sudoku-1.txt +0 -0
- 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/ast.rb +0 -0
- data/lib/z3/context.rb +0 -0
- data/lib/z3/exception.rb +0 -0
- data/lib/z3/expr/arith_expr.rb +0 -0
- data/lib/z3/expr/array_expr.rb +0 -0
- data/lib/z3/expr/bitvec_expr.rb +47 -2
- data/lib/z3/expr/bool_expr.rb +0 -0
- data/lib/z3/expr/expr.rb +3 -3
- data/lib/z3/expr/float_expr.rb +0 -0
- data/lib/z3/expr/int_expr.rb +0 -0
- data/lib/z3/expr/real_expr.rb +0 -0
- data/lib/z3/expr/rounding_mode_expr.rb +0 -0
- data/lib/z3/expr/set_expr.rb +0 -0
- data/lib/z3/func_decl.rb +0 -0
- data/lib/z3/goal.rb +0 -0
- data/lib/z3/hacks.rb +0 -0
- data/lib/z3/interface.rb +0 -0
- data/lib/z3/low_level.rb +0 -0
- data/lib/z3/low_level_auto.rb +138 -10
- data/lib/z3/model.rb +0 -0
- data/lib/z3/optimize.rb +0 -0
- data/lib/z3/printer.rb +0 -0
- data/lib/z3/probe.rb +0 -0
- data/lib/z3/solver.rb +0 -0
- data/lib/z3/sort/array_sort.rb +0 -0
- data/lib/z3/sort/bitvec_sort.rb +0 -0
- data/lib/z3/sort/bool_sort.rb +0 -0
- data/lib/z3/sort/float_sort.rb +0 -0
- data/lib/z3/sort/int_sort.rb +0 -0
- data/lib/z3/sort/real_sort.rb +0 -0
- data/lib/z3/sort/rounding_mode_sort.rb +0 -0
- data/lib/z3/sort/set_sort.rb +0 -0
- data/lib/z3/sort/sort.rb +0 -0
- data/lib/z3/tactic.rb +0 -0
- data/lib/z3/very_low_level.rb +5 -1
- data/lib/z3/very_low_level_auto.rb +35 -3
- data/lib/z3.rb +0 -0
- data/spec/array_expr_spec.rb +0 -0
- data/spec/array_sort_spec.rb +0 -0
- data/spec/bitvec_expr_spec.rb +13 -0
- data/spec/bitvec_sort_spec.rb +0 -0
- data/spec/bool_expr_spec.rb +0 -0
- data/spec/bool_sort_spec.rb +0 -0
- data/spec/coverage_helper.rb +0 -0
- data/spec/expr_spec.rb +0 -0
- data/spec/float_expr_spec.rb +0 -0
- data/spec/float_sort_spec.rb +0 -0
- data/spec/goal_spec.rb +0 -0
- data/spec/int_expr_spec.rb +0 -0
- data/spec/int_sort_spec.rb +0 -0
- data/spec/integration/abc_path_spec.rb +21 -0
- data/spec/integration/algebra_problems_spec.rb +0 -0
- data/spec/integration/aquarium_spec.rb +27 -0
- data/spec/integration/basic_int_math_spec.rb +0 -0
- data/spec/integration/basic_logic_spec.rb +0 -0
- data/spec/integration/bit_tricks_spec.rb +0 -0
- data/spec/integration/bridges_spec.rb +0 -0
- data/spec/integration/cats_organized_neatly_spec.rb +14 -0
- data/spec/integration/cicruit_problem_spec.rb +0 -0
- data/spec/integration/color_nonogram_spec.rb +28 -0
- data/spec/integration/crossflip_spec.rb +0 -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/four_hackers_puzzle_spec.rb +0 -0
- data/spec/integration/futoshiki_spec.rb +23 -0
- data/spec/integration/geometry_problem_spec.rb +0 -0
- data/spec/integration/kakurasu_spec.rb +18 -0
- data/spec/integration/kakuro_spec.rb +0 -0
- data/spec/integration/killer_sudoku_spec.rb +10 -0
- data/spec/integration/kinematics_problems_spec.rb +0 -0
- data/spec/integration/knights_puzzle_spec.rb +11 -11
- data/spec/integration/kropki_spec.rb +19 -0
- data/spec/integration/letter_connections_spec.rb +0 -0
- data/spec/integration/light_up_spec.rb +0 -0
- data/spec/integration/minisudoku_spec.rb +0 -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/nonogram_spec.rb +0 -0
- data/spec/integration/oneofus_spec.rb +0 -0
- data/spec/integration/pyramid_nonogram_spec.rb +0 -0
- data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
- data/spec/integration/regexp_solver_spec.rb +0 -0
- data/spec/integration/renzoku_spec.rb +23 -0
- data/spec/integration/sandwich_sudoku_spec.rb +15 -0
- data/spec/integration/selfref_spec.rb +0 -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/sudoku_spec.rb +0 -0
- data/spec/integration/suguru_spec.rb +23 -0
- data/spec/integration/verbal_arithmetic_spec.rb +0 -0
- data/spec/integration/yajilin_spec.rb +25 -0
- data/spec/integration/zebra_puzzle_spec.rb +0 -0
- data/spec/interface_spec.rb +0 -0
- data/spec/model_spec.rb +0 -0
- data/spec/optimize_spec.rb +3 -1
- data/spec/printer_spec.rb +0 -0
- data/spec/probe_spec.rb +0 -0
- data/spec/real_expr_spec.rb +0 -0
- data/spec/real_sort_spec.rb +0 -0
- data/spec/rounding_mode_expr_spec.rb +0 -0
- data/spec/rounding_mode_sort_spec.rb +0 -0
- data/spec/set_expr_spec.rb +15 -9
- data/spec/set_sort_spec.rb +0 -0
- data/spec/solver_spec.rb +1 -2
- data/spec/sort_spec.rb +0 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/tactic_spec.rb +0 -0
- data/spec/z3_spec.rb +0 -0
- 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,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
|
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
|
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.
|
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,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
|