z3 0.0.20160330 → 0.0.20160427

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/examples/{bridges_solver → bridges} +5 -12
  4. data/examples/bridges-1.txt +7 -0
  5. data/examples/{clogic_puzzle_solver → clogic_puzzle} +0 -0
  6. data/examples/four_hackers_puzzle +2 -1
  7. data/examples/{kakuro_solver → kakuro} +5 -15
  8. data/examples/kakuro-1.txt +10 -0
  9. data/examples/{knights_puzzle_solver → knights_puzzle} +4 -4
  10. data/examples/{letter_connections_solver → letter_connections} +6 -15
  11. data/examples/letter_connections-1.txt +10 -0
  12. data/examples/{light_up_solver → light_up} +5 -12
  13. data/examples/light_up-1.txt +7 -0
  14. data/examples/{minisudoku_solver → minisudoku} +8 -11
  15. data/examples/minisudoku-1.txt +6 -0
  16. data/examples/nonogram +152 -0
  17. data/examples/{selfref_solver → selfref} +0 -0
  18. data/examples/{sudoku_solver → sudoku} +9 -14
  19. data/examples/sudoku-1.txt +9 -0
  20. data/lib/z3.rb +10 -6
  21. data/lib/z3/ast.rb +33 -0
  22. data/lib/z3/{value/arith_value.rb → expr/arith_expr.rb} +3 -3
  23. data/lib/z3/{value/bitvec_value.rb → expr/bitvec_expr.rb} +1 -1
  24. data/lib/z3/{value/bool_value.rb → expr/bool_expr.rb} +5 -1
  25. data/lib/z3/{value/value.rb → expr/expr.rb} +7 -13
  26. data/lib/z3/expr/int_expr.rb +15 -0
  27. data/lib/z3/{value/int_value.rb → expr/real_expr.rb} +2 -2
  28. data/lib/z3/func_decl.rb +33 -23
  29. data/lib/z3/interface.rb +52 -30
  30. data/lib/z3/low_level.rb +10 -1
  31. data/lib/z3/low_level_auto.rb +83 -83
  32. data/lib/z3/model.rb +6 -5
  33. data/lib/z3/printer.rb +26 -0
  34. data/lib/z3/solver.rb +1 -5
  35. data/lib/z3/sort/bitvec_sort.rb +3 -3
  36. data/lib/z3/sort/bool_sort.rb +4 -4
  37. data/lib/z3/sort/int_sort.rb +2 -2
  38. data/lib/z3/sort/real_sort.rb +5 -5
  39. data/lib/z3/sort/sort.rb +22 -7
  40. data/lib/z3/very_low_level.rb +1 -1
  41. data/spec/bitvec_expr_spec.rb +55 -0
  42. data/spec/bitvec_sort_spec.rb +34 -0
  43. data/spec/bool_expr_spec.rb +65 -0
  44. data/spec/bool_sort_spec.rb +20 -0
  45. data/spec/{value_spec.rb → expr_spec.rb} +3 -3
  46. data/spec/int_expr_spec.rb +78 -0
  47. data/spec/int_sort_spec.rb +18 -0
  48. data/spec/integration/algebra_problems_spec.rb +19 -20
  49. data/spec/integration/basic_int_math_spec.rb +4 -5
  50. data/spec/integration/basic_logic_spec.rb +3 -4
  51. data/spec/integration/bit_tricks_spec.rb +2 -3
  52. data/spec/integration/bridges_spec.rb +2 -3
  53. data/spec/integration/four_hackers_puzzle_spec.rb +26 -0
  54. data/spec/integration/geometry_problem_spec.rb +10 -11
  55. data/spec/integration/kakuro_spec.rb +2 -3
  56. data/spec/integration/kinematics_problems_spec.rb +36 -37
  57. data/spec/integration/knights_puzzle_spec.rb +96 -0
  58. data/spec/integration/letter_connections_spec.rb +2 -3
  59. data/spec/integration/light_up_spec.rb +3 -4
  60. data/spec/integration/minisudoku_spec.rb +2 -3
  61. data/spec/integration/nonogram_spec.rb +26 -0
  62. data/spec/integration/selfref_spec.rb +2 -3
  63. data/spec/integration/sudoku_spec.rb +2 -3
  64. data/spec/integration/verbal_arithmetic_spec.rb +2 -3
  65. data/spec/model_spec.rb +13 -6
  66. data/spec/printer_spec.rb +22 -0
  67. data/spec/real_expr_spec.rb +64 -0
  68. data/spec/real_sort_spec.rb +24 -0
  69. data/spec/solver_spec.rb +11 -0
  70. data/spec/spec_helper.rb +39 -64
  71. metadata +81 -18
  72. data/lib/z3/value/real_value.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b616eadc04d3acebed002b5e30969543ec87288
4
- data.tar.gz: 51b12ceac3ca413e11d445c21524aa709e0d0c86
3
+ metadata.gz: b373cf9a8caebd1c5a05420660ac2301528613eb
4
+ data.tar.gz: 4c45e3aa7326aeaefddee63a17c8a4eeb1fe57e0
5
5
  SHA512:
6
- metadata.gz: 755c4a975d120202b93efe203d3eeea53dc7898a0a15b8008c287bc7821c773f5e88a73680dd32eb4acfcd55208659c55591ee9151a06056884e2760101492a3
7
- data.tar.gz: 5bc45fa1e31598830ce699b55cccbfac82ed9f83b72d9700fd454ecc2d05f68800bad0d2f8c2dc9f6485163b1ada994cfdd233323a52e49af1f142fa8107673e
6
+ metadata.gz: 6a2bd8245fcb32c67f0c609b477842a6f804fcc45d514f7d8d7a8ec047c8e6e70875167562aef1c51d04b637c900f001d4ad8e35368005f9aa3c7990239ab841
7
+ data.tar.gz: 73efd05c6292e8ef129c8a711a7de4b5a377c585991a11f4fe6930763c9e9e9ead23cc36fa98caba35966d9d7e5f897509ee7bb5bf003ff355d3ad2001b731f0
data/README.md CHANGED
@@ -27,3 +27,7 @@ On other systems use appropriate package manager.
27
27
  Ruby API tries to catch the most common mistakes, but if you use API in a weird way you can get C crash instead of nice Ruby exception.
28
28
 
29
29
  Memory will leak a good deal. Generally avoid in long running processes.
30
+
31
+ ### Python versions
32
+
33
+ Most of example solvers have Python versions available from https://github.com/taw/puzzle-solvers
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require "pathname"
3
4
  require_relative "../lib/z3"
4
5
 
5
6
  class Bridges
6
- def initialize(data)
7
+ def initialize(path)
8
+ data = Pathname(path).read
7
9
  data = data.strip.split("\n").map do |line|
8
10
  line.split.map{|c| c == "_" ? nil : c.to_i}
9
11
  end
@@ -119,14 +121,5 @@ class Bridges
119
121
  end
120
122
  end
121
123
 
122
- Bridges.new(
123
- """
124
- 3 _ _ 6 _ _ 4
125
- _ 1 _ _ _ _ _
126
- _ _ _ 4 _ 4 _
127
- _ _ _ _ _ _ _
128
- _ _ 1 _ 3 _ _
129
- 1 _ _ _ _ 2 _
130
- _ 3 _ _ 5 _ 3
131
- """
132
- ).solve!
124
+ path = ARGV[0] || Pathname(__dir__) + "bridges-1.txt"
125
+ Bridges.new(path).solve!
@@ -0,0 +1,7 @@
1
+ 3 _ _ 6 _ _ 4
2
+ _ 1 _ _ _ _ _
3
+ _ _ _ 4 _ 4 _
4
+ _ _ _ _ _ _ _
5
+ _ _ 1 _ 3 _ _
6
+ 1 _ _ _ _ 2 _
7
+ _ 3 _ _ 5 _ 3
@@ -62,8 +62,9 @@ class LogicPuzzle
62
62
  add_assertions!
63
63
  if @solver.check == :sat
64
64
  @solver.model.each do |k,v|
65
+ k = k.to_s.gsub("|", "")
65
66
  i, name = k.split("-", 2)
66
- puts "#{k} = #{@dict[name][v.to_i]}"
67
+ puts "#{k} = #{@dict[name][v.to_s.to_i]}"
67
68
  end
68
69
  else
69
70
  puts "Puzzle has no solutions"
@@ -10,10 +10,12 @@ class Hash
10
10
  end
11
11
  end
12
12
 
13
+ require "pathname"
13
14
  require_relative "../lib/z3"
14
15
 
15
16
  class Kakuro
16
- def initialize(data)
17
+ def initialize(path)
18
+ data = Pathname(path).read
17
19
  data = data.strip.split("\n").map do |line|
18
20
  line.split.map{|cell| parse_cell(cell)}
19
21
  end
@@ -108,17 +110,5 @@ class Kakuro
108
110
  end
109
111
  end
110
112
 
111
- Kakuro.new(
112
- """
113
- x x x 10/ 24/ 29/ x 11/ 21/ 10/
114
- x 11/ 19/24 _ _ _ /6 _ _ _
115
- /31 _ _ _ _ _ 10/20 _ _ _
116
- /4 _ _ 22/18 _ _ _ 23/13 _ _
117
- /18 _ _ _ 24/26 _ _ _ _ _
118
- x 19/ 30/16 _ _ 10/11 _ _ 11/ 20/
119
- /34 _ _ _ _ _ 23/23 _ _ _
120
- /7 _ _ 9/16 _ _ _ 3/4 _ _
121
- /24 _ _ _ /23 _ _ _ _ _
122
- /10 _ _ _ /11 _ _ _ x x
123
- """
124
- ).solve!
113
+ path = ARGV[0] || Pathname(__dir__) + "kakuro-1.txt"
114
+ Kakuro.new(path).solve!
@@ -0,0 +1,10 @@
1
+ x x x 10/ 24/ 29/ x 11/ 21/ 10/
2
+ x 11/ 19/24 _ _ _ /6 _ _ _
3
+ /31 _ _ _ _ _ 10/20 _ _ _
4
+ /4 _ _ 22/18 _ _ _ 23/13 _ _
5
+ /18 _ _ _ 24/26 _ _ _ _ _
6
+ x 19/ 30/16 _ _ 10/11 _ _ 11/ 20/
7
+ /34 _ _ _ _ _ 23/23 _ _ _
8
+ /7 _ _ 9/16 _ _ _ 3/4 _ _
9
+ /24 _ _ _ /23 _ _ _ _ _
10
+ /10 _ _ _ /11 _ _ _ x x
@@ -12,13 +12,13 @@ class KnightsPuzzle
12
12
  @moves = @num_moves.times.map{|t| move(t)}
13
13
  setup_board @boards[0], """
14
14
  bbb.
15
- xb.w
15
+ xbxw
16
16
  ..ww
17
17
  x.xw
18
18
  """
19
19
  setup_board @boards[-1], """
20
20
  www.
21
- xw.b
21
+ xwxb
22
22
  ..bb
23
23
  x.xb
24
24
  """
@@ -29,11 +29,11 @@ class KnightsPuzzle
29
29
 
30
30
  def print_board(t)
31
31
  puts "State #{t}:"
32
- puts @boards[t].transpose.map{|row| row.map{|c| ".wbx"[@model[c].to_i]}.join }.join("\n")
32
+ puts @boards[t].transpose.map{|row| row.map{|c| ".wbx"[@model[c].to_s.to_i]}.join }.join("\n")
33
33
  end
34
34
 
35
35
  def print_move(t)
36
- move = Hash[@moves[t].map{|k,v| [k,@model[v].to_i]}]
36
+ move = Hash[@moves[t].map{|k,v| [k,@model[v].to_s.to_i]}]
37
37
  figure = " wbx"[move[:figure]]
38
38
  puts "#{figure}: #{move[:start_x]},#{move[:start_y]} -> #{move[:end_x]},#{move[:end_y]}"
39
39
  puts ""
@@ -17,10 +17,13 @@
17
17
  #
18
18
  # This affects few enough real world puzzles and would complicate solution enough
19
19
  # that I let it be.
20
+
21
+ require "pathname"
20
22
  require_relative "../lib/z3"
21
23
 
22
24
  class LetterConnections
23
- def initialize(data)
25
+ def initialize(path)
26
+ data = Pathname(path).read
24
27
  data = data.strip.split("\n")
25
28
  @xsize = data[0].size
26
29
  @ysize = data.size
@@ -183,17 +186,5 @@ class LetterConnections
183
186
  end
184
187
  end
185
188
 
186
- LetterConnections.new(
187
- """
188
- ..KJ....FE
189
- .FJ.......
190
- ......G...
191
- .K.I......
192
- .......D..
193
- IE......B.
194
- ...H...CD.
195
- ...A..C...
196
- .A.H......
197
- ...B.G....
198
- """
199
- ).solve!
189
+ path = ARGV[0] || Pathname(__dir__) + "letter_connections-1.txt"
190
+ LetterConnections.new(path).solve!
@@ -0,0 +1,10 @@
1
+ ..KJ....FE
2
+ .FJ.......
3
+ ......G...
4
+ .K.I......
5
+ .......D..
6
+ IE......B.
7
+ ...H...CD.
8
+ ...A..C...
9
+ .A.H......
10
+ ...B.G....
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require "pathname"
3
4
  require_relative "../lib/z3"
4
5
 
5
6
  class LightUp
6
- def initialize(data)
7
+ def initialize(path)
8
+ data = Pathname(path).read
7
9
  data = data.split.map{|line| line.strip.chars}
8
10
  @xsize = data[0].size
9
11
  @ysize = data.size
@@ -92,14 +94,5 @@ class LightUp
92
94
  end
93
95
  end
94
96
 
95
- LightUp.new(
96
- """
97
- ....0..
98
- .......
99
- x.2.x..
100
- ...3...
101
- ..x.x.3
102
- .......
103
- ..1....
104
- """
105
- ).solve!
97
+ path = ARGV[0] || Pathname(__dir__) + "light_up-1.txt"
98
+ LightUp.new(path).solve!
@@ -0,0 +1,7 @@
1
+ ....0..
2
+ .......
3
+ x.2.x..
4
+ ...3...
5
+ ..x.x.3
6
+ .......
7
+ ..1....
@@ -1,9 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require "pathname"
3
4
  require_relative "../lib/z3"
4
5
 
5
6
  class MiniSudokuSolver
6
- def initialize(data)
7
+ def initialize(path)
8
+ data = Pathname(path).read
9
+ data = data.strip.split("\n").map do |line|
10
+ line.split.map{|c| c == "_" ? nil : c.to_i}
11
+ end
7
12
  @data = data
8
13
  @solver = Z3::Solver.new
9
14
  end
@@ -52,13 +57,5 @@ class MiniSudokuSolver
52
57
  end
53
58
  end
54
59
 
55
- _ = nil
56
- minisudoku = MiniSudokuSolver.new([
57
- [_, _, _, 4, _, _],
58
- [_, _, 4, 1, 2, _],
59
- [_, _, 6, 5, 4, _],
60
- [_, 5, 2, 3, _, _],
61
- [_, 2, 3, 6, _, _],
62
- [_, _, 1, _, _, _],
63
- ])
64
- minisudoku.solve!
60
+ path = ARGV[0] || Pathname(__dir__) + "minisudoku-1.txt"
61
+ MiniSudokuSolver.new(path).solve!
@@ -0,0 +1,6 @@
1
+ _ _ _ 4 _ _
2
+ _ _ 4 1 2 _
3
+ _ _ 6 5 4 _
4
+ _ 5 2 3 _ _
5
+ _ 2 3 6 _ _
6
+ _ _ 1 _ _ _
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ class Nonogram
6
+ def initialize(columns, rows)
7
+ @columns = columns
8
+ @rows = rows
9
+ @solver = Z3::Solver.new
10
+ @xsize = @columns.size
11
+ @ysize = @rows.size
12
+ end
13
+
14
+ def print_answer!
15
+ @ysize.times do |y|
16
+ @xsize.times do |x|
17
+ v = @model[@cells[y][x]]
18
+ if v.to_s == "true"
19
+ print "\u2588"
20
+ else
21
+ print "\u00b7"
22
+ end
23
+ end
24
+ print "\n"
25
+ end
26
+ end
27
+
28
+ def print_all_answers!
29
+ if @solver.check == :sat
30
+ @model = @solver.model
31
+ print_answer!
32
+ current_solution = []
33
+ # TODO: Model API doesn't currently have functionality necessary
34
+ # to make uniqueness checks work
35
+ # @model.each do |var, val|
36
+ # current_solution << (@model[var] != val)
37
+ # end
38
+ # @solver.assert Z3.Or(*current_solution)
39
+ # if @solver.check == :sat
40
+ # puts "solution is not unique"
41
+ # print_answer!
42
+ # else
43
+ # puts "solution is unique"
44
+ # end
45
+ else
46
+ puts "failed to solve"
47
+ end
48
+ end
49
+
50
+ def setup_vars(nums, prefix, size)
51
+ results = []
52
+ (0...nums.size).each do |i|
53
+ vs = Z3.Int("#{prefix},#{i+1},s")
54
+ ve = Z3.Int("#{prefix},#{i+1},e")
55
+ @solver.assert ve-vs == nums[i]-1
56
+ @solver.assert vs >= 0
57
+ @solver.assert ve < size
58
+ results << [vs,ve]
59
+ end
60
+
61
+ (0...nums.size-1).each do |i|
62
+ ve0 = results[i][1]
63
+ vs1 = results[i+1][0]
64
+ @solver.assert vs1 >= ve0+2
65
+ end
66
+
67
+ results
68
+ end
69
+
70
+ def setup_cell_constraints(cells, cvars)
71
+ (0...cells.size).each do |i|
72
+ rule_i = Z3.Or(
73
+ *cvars.map{|vs,ve| Z3.And(vs <= i, i <= ve) }
74
+ )
75
+ @solver.assert cells[i] == rule_i
76
+ end
77
+ end
78
+
79
+ def solve!
80
+ @cells = (0...@ysize).map{|y|
81
+ (0...@xsize).map{|x|
82
+ Z3.Bool("cell[#{x+1},#{y+1}]")
83
+ }
84
+ }
85
+
86
+ @column_vars = (0...@xsize).map{|i|
87
+ setup_vars(@columns[i], "c%d" % (i+1), @ysize)
88
+ }
89
+ @row_vars = (0...@ysize).map{|i|
90
+ setup_vars(@rows[i], "r%d" % (i+1), @xsize)
91
+ }
92
+
93
+ @xsize.times do |i|
94
+ setup_cell_constraints(@cells.map{|row| row[i]}, @column_vars[i])
95
+ end
96
+
97
+ @ysize.times do |i|
98
+ setup_cell_constraints(@cells[i], @row_vars[i])
99
+ end
100
+
101
+ print_all_answers!
102
+ end
103
+ end
104
+
105
+ nonogram = Nonogram.new(
106
+ [
107
+ [2],
108
+ [1,2],
109
+ [2,3],
110
+ [2,3],
111
+ [3,1,1],
112
+ [2,1,1],
113
+ [1,1,1,2,2],
114
+ [1,1,3,1,3],
115
+ [2,6,4],
116
+ [3,3,9,1],
117
+ [5,3,2],
118
+ [3,1,2,2],
119
+ [2,1,7],
120
+ [3,3,2],
121
+ [2,4],
122
+ [2,1,2],
123
+ [2,2,1],
124
+ [2,2],
125
+ [1],
126
+ [1],
127
+ ],
128
+ [
129
+ [3],
130
+ [5],
131
+ [3,1],
132
+ [2,1],
133
+ [3,3,4],
134
+ [2,2,7],
135
+ [6,1,1],
136
+ [4,2,2],
137
+ [1,1],
138
+ [3,1],
139
+ [6],
140
+ [2,7],
141
+ [6,3,1],
142
+ [1,2,2,1,1],
143
+ [4,1,1,3],
144
+ [4,2,2],
145
+ [3,3,1],
146
+ [3,3],
147
+ [3],
148
+ [2,1],
149
+ ]
150
+ )
151
+
152
+ nonogram.solve!
File without changes
@@ -1,9 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+
4
+ require "pathname"
3
5
  require_relative "../lib/z3"
4
6
 
5
7
  class SudokuSolver
6
- def initialize(data)
8
+ def initialize(path)
9
+ data = Pathname(path).read
10
+ data = data.strip.split("\n").map do |line|
11
+ line.split.map{|c| c == "_" ? nil : c.to_i}
12
+ end
7
13
  @data = data
8
14
  @solver = Z3::Solver.new
9
15
  end
@@ -52,16 +58,5 @@ class SudokuSolver
52
58
  end
53
59
  end
54
60
 
55
- _ = nil
56
- sudoku = SudokuSolver.new([
57
- [_, 6, _, 5, _, 9, _, 4, _],
58
- [9, 2, _, _, _, _, _, 7, 6],
59
- [_, _, 1, _, _, _, 9, _, _],
60
- [7, _, _, 6, _, 3, _, _, 9],
61
- [_, _, _, _, _, _, _, _, _],
62
- [3, _, _, 4, _, 1, _, _, 7],
63
- [_, _, 6, _, _, _, 7, _, _],
64
- [2, 4, _, _, _, _, _, 6, 5],
65
- [_, 9, _, 1, _, 8, _, 3, _],
66
- ])
67
- sudoku.solve!
61
+ path = ARGV[0] || Pathname(__dir__) + "sudoku-1.txt"
62
+ SudokuSolver.new(path).solve!