z3 0.0.20181229 → 0.0.20211213
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/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/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/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/light_up +2 -2
- data/examples/minisudoku +2 -2
- data/examples/miracle_sudoku +135 -0
- data/examples/miracle_sudoku-1.txt +9 -0
- data/examples/mortal_coil_puzzle +2 -2
- 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_solver +2 -2
- data/examples/regexp_solver +2 -2
- 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/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/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/expr/expr.rb +3 -3
- data/lib/z3/low_level_auto.rb +138 -10
- data/lib/z3/optimize.rb +1 -0
- data/lib/z3/very_low_level.rb +5 -1
- data/lib/z3/very_low_level_auto.rb +35 -3
- data/spec/integration/abc_path_spec.rb +21 -0
- data/spec/integration/aquarium_spec.rb +27 -0
- data/spec/integration/cats_organized_neatly_spec.rb +14 -0
- data/spec/integration/color_nonogram_spec.rb +28 -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/futoshiki_spec.rb +23 -0
- data/spec/integration/kakurasu_spec.rb +18 -0
- data/spec/integration/killer_sudoku_spec.rb +10 -0
- data/spec/integration/knights_puzzle_spec.rb +11 -11
- data/spec/integration/kropki_spec.rb +19 -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/regexp_crossword_solver_spec.rb +1 -1
- data/spec/integration/renzoku_spec.rb +23 -0
- data/spec/integration/sandwich_sudoku_spec.rb +15 -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/suguru_spec.rb +23 -0
- data/spec/integration/yajilin_spec.rb +25 -0
- data/spec/optimize_spec.rb +3 -1
- data/spec/set_expr_spec.rb +15 -9
- data/spec/solver_spec.rb +1 -2
- data/spec/spec_helper.rb +15 -0
- metadata +86 -7
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c73d508e9644e0c32681754a8f33959bc54d838c21d13f896223225e8b275a76
         | 
| 4 | 
            +
              data.tar.gz: 12e017b721b97aaec2d8e1a75145f0c5d3f0769eee2badc89e08e2b0ff11fef1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d4993e91a238ad4e1c70e78bfe12301e1bd83759d9ad610fca64f89267868b9c8959c6e51ba98f175836235702c66f9cde8b19d73ddcd2cb760da75aed1f564d
         | 
| 7 | 
            +
              data.tar.gz: cca1389567fb61df3d4dc2fa6281b833edcc3c3a2c5872fec2b71089c9f9c927ba0d3e2d164127d5be167c2bb1cc4fd86f2360718631e0a8fbc473bda68ec90b
         | 
    
        data/README.md
    CHANGED
    
    | @@ -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,6 +5,13 @@ 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 17 | 
             
              sh "./api/gen_api api/definitions.h"
         | 
| @@ -53,4 +60,4 @@ task "coverage:missing" do | |
| 53 60 | 
             
              open("missing_apis.txt", "w") do |file|
         | 
| 54 61 | 
             
                file.puts missing
         | 
| 55 62 | 
             
              end
         | 
| 56 | 
            -
            end
         | 
| 63 | 
            +
            end
         | 
    
        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
         | 
    
        data/examples/algebra_problems
    CHANGED
    
    | @@ -21,7 +21,7 @@ class AlgebraProblem | |
| 21 21 | 
             
            end
         | 
| 22 22 |  | 
| 23 23 | 
             
            class AlgebraProblem01 < AlgebraProblem
         | 
| 24 | 
            -
              def  | 
| 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  | 
| 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  | 
| 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  | 
| 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  | 
| 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  | 
| 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. | 
| 121 | 
            -
            AlgebraProblem03.new. | 
| 122 | 
            -
            AlgebraProblem04.new. | 
| 123 | 
            -
            AlgebraProblem05.new. | 
| 124 | 
            -
            AlgebraProblem06.new. | 
| 125 | 
            -
            AlgebraProblem10.new. | 
| 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  | 
| 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). | 
| 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,32 @@ | |
| 1 | 
            +
             .......
         | 
| 2 | 
            +
             .......
         | 
| 3 | 
            +
            ........
         | 
| 4 | 
            +
            ........
         | 
| 5 | 
            +
            ........
         | 
| 6 | 
            +
            ........
         | 
| 7 | 
            +
            .......
         | 
| 8 | 
            +
              .....
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            aaaaa
         | 
| 11 | 
            +
            aaaaa
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            bbb
         | 
| 14 | 
            +
            bbbb
         | 
| 15 | 
            +
            bbbb
         | 
| 16 | 
            +
             | 
| 17 | 
            +
             ccc
         | 
| 18 | 
            +
            cccc
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ddd
         | 
| 21 | 
            +
            ddd
         | 
| 22 | 
            +
            ddd
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            eee
         | 
| 25 | 
            +
             e
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ffff
         | 
| 28 | 
            +
            f
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            gggg
         | 
| 31 | 
            +
            gggg
         | 
| 32 | 
            +
            gggg
         | 
    
        data/examples/circuit_problems
    CHANGED
    
    | @@ -45,7 +45,7 @@ class CircuitProblem | |
| 45 45 | 
             
                @solver.assert @pins[a][:voltage] == @pins[b][:voltage]
         | 
| 46 46 | 
             
              end
         | 
| 47 47 |  | 
| 48 | 
            -
              def  | 
| 48 | 
            +
              def call(*vars)
         | 
| 49 49 | 
             
                setup_flow_rules!
         | 
| 50 50 | 
             
                with_solved_model do |model|
         | 
| 51 51 | 
             
                  model.each do |n,v|
         | 
| @@ -115,7 +115,7 @@ def problem_1! | |
| 115 115 | 
             
              problem.connect "R1b", "R2a"
         | 
| 116 116 | 
             
              problem.connect "R2b", "R3a"
         | 
| 117 117 | 
             
              problem.connect "R3b", "V+"
         | 
| 118 | 
            -
              problem. | 
| 118 | 
            +
              problem.call "I V"
         | 
| 119 119 | 
             
            end
         | 
| 120 120 |  | 
| 121 121 | 
             
            def problem_2!
         | 
| @@ -131,7 +131,7 @@ def problem_2! | |
| 131 131 | 
             
              problem.connect "V+", "R1b"
         | 
| 132 132 | 
             
              problem.connect "V+", "R2b"
         | 
| 133 133 | 
             
              problem.connect "V+", "R3b"
         | 
| 134 | 
            -
              problem. | 
| 134 | 
            +
              problem.call "I V"
         | 
| 135 135 | 
             
            end
         | 
| 136 136 |  | 
| 137 137 | 
             
            def problem_3!
         | 
| @@ -156,7 +156,7 @@ def problem_3! | |
| 156 156 | 
             
                problem.connect "D3+", "V+"
         | 
| 157 157 | 
             
                problem.connect "D4-",      "Lb"
         | 
| 158 158 | 
             
                problem.connect "D4+", "V-"
         | 
| 159 | 
            -
                problem. | 
| 159 | 
            +
                problem.call "I V", "I L"
         | 
| 160 160 | 
             
              end
         | 
| 161 161 | 
             
            end
         | 
| 162 162 |  |