z3 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9155c71b227576436cf55fe332fb04748bf8a2ed
4
+ data.tar.gz: bf1c5200d857ed21311d2f5cb468e8cceac2f627
5
+ SHA512:
6
+ metadata.gz: 67c4b499c50f0c288900667f13bcdcca543629d55332a6247011c8e135b1d9b41a4865f9900e2884155aa28844492c3fcb1d5aacad775e1b556e3d1a6a265d3a
7
+ data.tar.gz: 88e74e1966920896bc2ca0ec8a2712c8180aeb79a97be8551aceb69b21016df907a579091fded5b688f8ebc8bdd3ad4ede8037f6a4458b7fa1831081b5f2bcee
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ This is Ruby interface for Z3 [ https://github.com/Z3Prover/z3 ].
2
+
3
+ It's in extremely early stages of development. Pull requests always welcome.
4
+
5
+ ### Interface
6
+
7
+ `Z3::VeryLowLever` / `Z3::LowLevel` are low level FFI interface, and they shouldn't be used directly.
8
+
9
+ The rest of `Z3` is high level API, but the interface is extremely unstable at this point, and it's pretty much guaranteed to change many times. Check specs or `examples/` directory for usage.
10
+
11
+ You can use most Ruby operators to construct ASTs, but use `~ | &` instead of `! || &&` for boolean operators.
12
+
13
+ As for API internals, attributes starting with `_` are FFI internals you shouldn't touch, other attributes are generally legitimate Ruby objects.
14
+
15
+ ### Requirements
16
+
17
+ To use it, you'll need to install `z3`. On OSX that would be:
18
+
19
+ brew install z3
20
+
21
+ On other systems use appropriate package manager.
22
+
23
+ ### Known Bugs
24
+
25
+ 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.
26
+
27
+ Memory will leak a good deal. Generally avoid in long running processes.
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ class AlgebraProblem
6
+ attr_reader :solver
7
+ def initialize
8
+ @solver = Z3::Solver.new
9
+ end
10
+
11
+ def print_solution!(number)
12
+ puts("Solution to problem %s:" % number)
13
+ if @solver.check() == :sat
14
+ @solver.model.each do |n,v|
15
+ puts "* #{n} = #{v}"
16
+ end
17
+ else
18
+ puts "* Can't solve the problem"
19
+ end
20
+ end
21
+ end
22
+
23
+ class AlgebraProblem01 < AlgebraProblem
24
+ def solve!
25
+ x = Z3::Ast.real("x")
26
+ solver.assert 5*(-3*x - 2) - (x - 3) == -4*(4*x + 5) + 13
27
+ print_solution! "01"
28
+ # No nice way to say x is unconstrained
29
+ end
30
+ end
31
+
32
+ class AlgebraProblem03 < AlgebraProblem
33
+ def declare_abs(var, expr)
34
+ solver.assert Z3::Ast.or(
35
+ Z3::Ast.and(var == expr, expr >= 0),
36
+ Z3::Ast.and(var == -expr, expr <= 0)
37
+ )
38
+ end
39
+
40
+ def solve!
41
+ x = Z3::Ast.real("x")
42
+ xm2abs = Z3::Ast.real("|x-2|")
43
+ declare_abs(xm2abs, x-2)
44
+ m6abs = Z3::Ast.real("|-6|")
45
+ declare_abs(m6abs, -6)
46
+ solver.assert x < 2
47
+ solver.assert xm2abs == 4*m6abs
48
+ print_solution! "03"
49
+ end
50
+ end
51
+
52
+ class AlgebraProblem04 < AlgebraProblem
53
+ def declare_distance(a_b, ax, ay, bx, by)
54
+ solver.assert a_b >= 0
55
+ solver.assert a_b**2 == (ax-bx)**2 + (ay-by)**2
56
+ end
57
+
58
+ def solve!
59
+ ax = Z3::Ast.real("ax")
60
+ ay = Z3::Ast.real("ay")
61
+ bx = Z3::Ast.real("bx")
62
+ by = Z3::Ast.real("by")
63
+ a_b = Z3::Ast.real("|a-b|")
64
+ declare_distance(a_b, ax, ay, bx, by)
65
+ solver.assert ax == -4
66
+ solver.assert ay == -5
67
+ solver.assert bx == -1
68
+ solver.assert by == -1
69
+ print_solution! "04"
70
+ end
71
+ end
72
+
73
+ class AlgebraProblem05 < AlgebraProblem
74
+ def solve!
75
+ x = Z3::Ast.real("x")
76
+ y = Z3::Ast.real("y")
77
+ solver.assert 2*x - 4*y == 9
78
+ solver.assert y == 0
79
+ print_solution! "05"
80
+ end
81
+ end
82
+
83
+ class AlgebraProblem06 < AlgebraProblem
84
+ def declare_f(x,y)
85
+ solver.assert y == 6*x + 1
86
+ end
87
+
88
+ def solve!
89
+ x1 = Z3::Ast.real("x1")
90
+ y1 = Z3::Ast.real("y1")
91
+ x2 = Z3::Ast.real("x2")
92
+ y2 = Z3::Ast.real("y2")
93
+ answer = Z3::Ast.real("answer")
94
+ solver.assert x2 == 2
95
+ solver.assert x1 == 1
96
+ declare_f(x1, y1)
97
+ declare_f(x2, y2)
98
+ solver.assert answer == y2 - y1
99
+ print_solution! "06"
100
+ end
101
+ end
102
+
103
+ class AlgebraProblem10 < AlgebraProblem
104
+ def declare_abs(var, expr)
105
+ solver.assert Z3::Ast.or(
106
+ Z3::Ast.and(var == expr, expr >= 0),
107
+ Z3::Ast.and(var == -expr, expr <= 0)
108
+ )
109
+ end
110
+
111
+ def solve!
112
+ x = Z3::Ast.real("x")
113
+ y = Z3::Ast.real("|-2x + 2|")
114
+ declare_abs(y, -2*x + 2)
115
+ solver.assert y - 3 == -3
116
+ print_solution! "10"
117
+ end
118
+ end
119
+
120
+ AlgebraProblem01.new.solve!
121
+ AlgebraProblem03.new.solve!
122
+ AlgebraProblem04.new.solve!
123
+ AlgebraProblem05.new.solve!
124
+ AlgebraProblem06.new.solve!
125
+ AlgebraProblem10.new.solve!
126
+
127
+ """
128
+ http://www.analyzemath.com/Algebra1/Algebra1.html
129
+
130
+
131
+ Problem 1: Solve the equation
132
+ 5(-3x - 2) - (x - 3) = -4(4x + 5) + 13
133
+
134
+ Problem 3: If x <2, simplify
135
+ |x - 2| = 4|-6|
136
+ (actually I misread original, whatever)
137
+
138
+ Problem 4: Find the distance between the points (-4 , -5) and (-1 , -1).
139
+
140
+ Problem 5: Find the x intercept of the graph of the equation
141
+ 2x - 4y = 9
142
+
143
+ Problem 6: Evaluate f(2) - f(1)
144
+ f(x) = 6x + 1
145
+
146
+ Problem 10: Solve the equation
147
+ |-2x + 2| -3 = -3
148
+ """
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ def check_multiplication_laws_1
6
+ a = Z3::Ast.int("a")
7
+ b = Z3::Ast.int("b")
8
+ solver = Z3::Solver.new
9
+ puts "Checking if (a+b)(a-b)==a*a-b*b"
10
+ solver.prove!(
11
+ (a+b)*(a-b) == (a*a - b*b)
12
+ )
13
+ end
14
+
15
+ def check_inequalities
16
+ a = Z3::Ast.int("a")
17
+ b = Z3::Ast.int("b")
18
+ solver = Z3::Solver.new
19
+ puts "Checking if a+b >= a"
20
+ solver.prove!(a+b >= a)
21
+
22
+ solver = Z3::Solver.new
23
+ solver.assert(a >= 0)
24
+ solver.assert(b >= 0)
25
+ puts "Checking if a+b >= a if a,b >= 0"
26
+ solver.prove!(a+b >= a)
27
+ end
28
+
29
+ check_multiplication_laws_1
30
+ check_inequalities
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ def check_if_true_is_true
6
+ a = Z3::Ast.true
7
+ b = Z3::Ast.true
8
+ solver = Z3::Solver.new
9
+ puts "Checking if true == true"
10
+ solver.prove!(a == b)
11
+ end
12
+
13
+ def check_if_true_is_false
14
+ a = Z3::Ast.true
15
+ b = Z3::Ast.false
16
+ solver = Z3::Solver.new
17
+ puts "Checking if true == false"
18
+ solver.prove!(a==b)
19
+ end
20
+
21
+ # We prove it by checking that negation is unsatisfiable
22
+ def check_de_morgan_law_1
23
+ a = Z3::Ast::bool("a")
24
+ b = Z3::Ast::bool("b")
25
+ solver = Z3::Solver.new
26
+ puts "Proving ~(a & b) == (~a | ~b)"
27
+ solver.prove!(~(a & b) == (~a | ~b))
28
+ end
29
+
30
+ def check_de_morgan_law_2
31
+ a = Z3::Ast::bool("a")
32
+ b = Z3::Ast::bool("b")
33
+ solver = Z3::Solver.new
34
+ puts "Proving ~(a | b) == (~a & ~b)"
35
+ solver.prove!(~(a | b) == (~a & ~b))
36
+ end
37
+
38
+ check_if_true_is_true
39
+ check_if_true_is_false
40
+ check_de_morgan_law_1
41
+ check_de_morgan_law_2
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ solver = Z3::Solver.new
6
+
7
+ ax = Z3::Ast.real("a.x")
8
+ ay = Z3::Ast.real("a.y")
9
+ bx = Z3::Ast.real("b.x")
10
+ by = Z3::Ast.real("b.y")
11
+ cx = Z3::Ast.real("c.x")
12
+ cy = Z3::Ast.real("c.y")
13
+ dx = Z3::Ast.real("d.x")
14
+ dy = Z3::Ast.real("d.y")
15
+
16
+ a_c = Z3::Ast.real("|a-c|")
17
+ b_d = Z3::Ast.real("|b-d|")
18
+
19
+ solver.assert(bx == 0)
20
+ solver.assert(by == 0)
21
+ solver.assert(cx == 10)
22
+ solver.assert(cy == 0)
23
+ solver.assert(dx == cx)
24
+ solver.assert(ay == dy)
25
+ solver.assert(ax == 0)
26
+
27
+ # BD is on the circle
28
+ solver.assert(b_d**2 == (bx-dx)**2 + (by-dy)**2)
29
+ solver.assert(b_d == 20)
30
+
31
+ solver.assert(a_c**2 == (ax-cx)**2 + (ay-cy)**2)
32
+
33
+ if solver.check == :sat
34
+ model = solver.model
35
+ model.each do |n,v|
36
+ puts "* #{n} = #{v}"
37
+ end
38
+ else
39
+ puts "Can't solve the problem"
40
+ end
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Problems from: http://www.physicsclassroom.com/class/1DKin/Lesson-6/Sample-Problems-and-Solutions
4
+
5
+ require_relative "../lib/z3"
6
+
7
+ class KinematicsProblem
8
+ attr_reader :solver
9
+ def initialize
10
+ @solver = Z3::Solver.new
11
+ end
12
+
13
+ def assert(ast)
14
+ @solver.assert(ast)
15
+ end
16
+
17
+ def print_solution!(number)
18
+ puts("Solution to problem %s:" % number)
19
+ if @solver.check() == :sat
20
+ @solver.model.each do |n,v|
21
+ puts "* #{n} = #{v}"
22
+ end
23
+ else
24
+ puts "* Can't solve the problem"
25
+ end
26
+ end
27
+ end
28
+
29
+ class KinematicsProblem01 < KinematicsProblem
30
+ """
31
+ An airplane accelerates down a runway at 3.20 m/s2 for 32.8 s until is finally lifts off the ground.
32
+ Determine the distance traveled before takeoff.
33
+ """
34
+ def solve!
35
+ a = Z3::Ast.real("a")
36
+ t = Z3::Ast.real("t")
37
+ d = Z3::Ast.real("d")
38
+ assert a == 3.20
39
+ assert t == 32.8
40
+ assert d == a*t*t/2
41
+ print_solution! "01"
42
+ end
43
+ end
44
+
45
+ class KinematicsProblem02 < KinematicsProblem
46
+ """
47
+ A car starts from rest and accelerates uniformly over a time of 5.21 seconds for a distance of 110 m. Determine the acceleration of the car.
48
+ """
49
+ def solve!
50
+ a = Z3::Ast.real("a")
51
+ t = Z3::Ast.real("t")
52
+ d = Z3::Ast.real("d")
53
+ assert d == a*t*t/2
54
+ assert t == 5.21
55
+ assert d == 110
56
+ print_solution! "02"
57
+ end
58
+ end
59
+
60
+ class KinematicsProblem03 < KinematicsProblem
61
+ """
62
+ Upton Chuck is riding the Giant Drop at Great America.
63
+ If Upton free falls for 2.60 seconds, what will be his final velocity and how far will he fall?
64
+ """
65
+ def solve!
66
+ a = Z3::Ast.real("a")
67
+ t = Z3::Ast.real("t")
68
+ v = Z3::Ast.real("v")
69
+ d = Z3::Ast.real("d")
70
+ assert a == 9.81
71
+ assert d == a*t*t/2
72
+ assert v == a*t
73
+ assert t == 2.60
74
+ print_solution! "03"
75
+ end
76
+ end
77
+
78
+ class KinematicsProblem04 < KinematicsProblem
79
+ """
80
+ A race car accelerates uniformly from 18.5 m/s to 46.1 m/s in 2.47 seconds. Determine the acceleration of the car and the distance traveled.
81
+ """
82
+ def solve!
83
+ a = Z3::Ast.real("a")
84
+ t = Z3::Ast.real("t")
85
+ vs= Z3::Ast.real("vs")
86
+ ve = Z3::Ast.real("ve")
87
+ d = Z3::Ast.real("d")
88
+ assert vs == 18.5
89
+ assert ve == 46.1
90
+ assert t == 2.47
91
+ assert a == (ve-vs)/t
92
+ assert d == (vs+ve)/2*t
93
+ print_solution! "04"
94
+ end
95
+ end
96
+
97
+ class KinematicsProblem05 < KinematicsProblem
98
+ """
99
+ A feather is dropped on the moon from a height of 1.40 meters.
100
+ The acceleration of gravity on the moon is 1.67 m/s2.
101
+ Determine the time for the feather to fall to the surface of the moon.
102
+ """
103
+ def solve!
104
+ a = Z3::Ast.real("a")
105
+ t = Z3::Ast.real("t")
106
+ d = Z3::Ast.real("d")
107
+ assert a == 1.67
108
+ assert d == 1.40
109
+ assert t >= 0
110
+ assert d == a*t*t/2
111
+ print_solution! "05"
112
+ end
113
+ end
114
+
115
+ class KinematicsProblem06 < KinematicsProblem
116
+ """
117
+ Rocket-powered sleds are used to test the human response to acceleration.
118
+ If a rocket-powered sled is accelerated to a speed of 444 m/s in 1.83 seconds,
119
+ then what is the acceleration and what is the distance that the sled travels?
120
+ """
121
+ def solve!
122
+ a = Z3::Ast.real("a")
123
+ t = Z3::Ast.real("t")
124
+ d = Z3::Ast.real("d")
125
+ v = Z3::Ast.real("v")
126
+ assert v == 444
127
+ assert t == 1.83
128
+ assert v == a*t
129
+ assert d == a*t*t/2
130
+ print_solution! "06"
131
+ end
132
+ end
133
+
134
+ class KinematicsProblem07 < KinematicsProblem
135
+ """
136
+ A bike accelerates uniformly from rest to a speed of 7.10 m/s over a distance of 35.4 m.
137
+ Determine the acceleration of the bike.
138
+ """
139
+ def solve!
140
+ a = Z3::Ast.real("a")
141
+ t = Z3::Ast.real("t")
142
+ d = Z3::Ast.real("d")
143
+ v = Z3::Ast.real("v")
144
+ assert v == 7.10
145
+ assert d == 35.4
146
+ assert a*t == v
147
+ assert t*v/2 == d
148
+ print_solution! "07"
149
+ end
150
+ end
151
+
152
+ class KinematicsProblem08 < KinematicsProblem
153
+ """
154
+ An engineer is designing the runway for an airport.
155
+ Of the planes that will use the airport, the lowest acceleration rate is likely to be 3 m/s2.
156
+ The takeoff speed for this plane will be 65 m/s.
157
+ Assuming this minimum acceleration, what is the minimum allowed length for the runway?
158
+ """
159
+ def solve!
160
+ a = Z3::Ast.real("a")
161
+ t = Z3::Ast.real("t")
162
+ d = Z3::Ast.real("d")
163
+ v = Z3::Ast.real("v")
164
+ assert a == 3
165
+ assert v == 65
166
+ assert a*t == v
167
+ assert t*v/2 == d
168
+ print_solution! "08"
169
+ end
170
+ end
171
+
172
+ class KinematicsProblem09 < KinematicsProblem
173
+ """
174
+ A car traveling at 22.4 m/s skids to a stop in 2.55 s.
175
+ Determine the skidding distance of the car (assume uniform acceleration).
176
+ """
177
+ def solve!
178
+ t = Z3::Ast.real("t")
179
+ d = Z3::Ast.real("d")
180
+ v = Z3::Ast.real("v")
181
+ assert v == 22.4
182
+ assert t == 2.55
183
+ assert d == t*v/2
184
+ print_solution! "09"
185
+ end
186
+ end
187
+
188
+ class KinematicsProblem10 < KinematicsProblem
189
+ """
190
+ A kangaroo is capable of jumping to a height of 2.62 m.
191
+ Determine the takeoff speed of the kangaroo.
192
+ """
193
+ def solve!
194
+ a = Z3::Ast.real("a")
195
+ t = Z3::Ast.real("t")
196
+ d = Z3::Ast.real("d")
197
+ v = Z3::Ast.real("v")
198
+ assert d == 2.62
199
+ assert a == -9.81
200
+ assert d == v*t/2
201
+ assert v + a*t == 0
202
+ assert t >= 0
203
+ print_solution! "10"
204
+ end
205
+ end
206
+
207
+ # This is actually param for Python Z3 printer, can't work until we get that
208
+ # Z3.set_param("rational_to_decimal", true)
209
+
210
+ KinematicsProblem01.new.solve!
211
+ KinematicsProblem02.new.solve!
212
+ KinematicsProblem03.new.solve!
213
+ KinematicsProblem04.new.solve!
214
+ KinematicsProblem05.new.solve!
215
+ KinematicsProblem06.new.solve!
216
+ KinematicsProblem07.new.solve!
217
+ KinematicsProblem08.new.solve!
218
+ KinematicsProblem09.new.solve!
219
+ KinematicsProblem10.new.solve!
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ class LightUp
6
+ def initialize(data)
7
+ data = data.split.map{|line| line.strip.chars}
8
+ @xsize = data[0].size
9
+ @ysize = data.size
10
+ @data = map_coordinates{|x,y| data[y][x]}
11
+ @solver = Z3::Solver.new
12
+ end
13
+
14
+ def solve!
15
+ @lamps = map_coordinates{|x,y| int01(x,y)}
16
+
17
+ # No lamps on walls
18
+ # Numbers count lamps next to node (diagonals don't count)
19
+ (0...@ysize).each do |y|
20
+ (0...@xsize).each do |x|
21
+ v = @data[[x,y]]
22
+ next if v == "."
23
+ @solver.assert @lamps[[x,y]] == 0
24
+ if ("0".."4").include?(v)
25
+ @solver.assert Z3::Ast.add(*lamps_next_to_cell(x,y)) == v.to_i
26
+ end
27
+ end
28
+ end
29
+
30
+ # Every cell is in raycast of a lamp
31
+ # No lamp is in raycast of another lamp
32
+ (0...@ysize).each do |y|
33
+ (0...@xsize).each do |x|
34
+ raycast = Z3::Ast.add(*raycast_cells(x,y))
35
+ @solver.assert Z3::Ast.implies(@lamps[[x,y]] == 0, raycast >= 1)
36
+ @solver.assert Z3::Ast.implies(@lamps[[x,y]] == 1, raycast == 0)
37
+ end
38
+ end
39
+
40
+ if @solver.check == :sat
41
+ @model = @solver.model
42
+ print_answer!
43
+ else
44
+ puts "failed to solve"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def lamps_next_to_cell(x0,y0)
51
+ [[x0-1,y0],[x0+1,y0],[x0,y0-1],[x0,y0+1]].map{|x,y| @lamps[[x,y]]}.compact
52
+ end
53
+
54
+ def raycast_cells(x0,y0)
55
+ result = []
56
+ [[1,0],[-1,0],[0,1],[0,-1]].each do |dx,dy|
57
+ x = x0+dx
58
+ y = y0+dy
59
+ while @data[[x,y]] == "."
60
+ result << @lamps[[x,y]]
61
+ x += dx
62
+ y += dy
63
+ end
64
+ end
65
+ result
66
+ end
67
+
68
+ def map_coordinates
69
+ Hash[(0...@xsize).to_a.product((0...@ysize).to_a).map{|x,y| [[x,y],yield(x,y)]}]
70
+ end
71
+
72
+ def print_answer!
73
+ (0...@ysize).each do |y|
74
+ (0...@xsize).each do |x|
75
+ if @data[[x,y]] != "."
76
+ print(@data[[x,y]])
77
+ elsif @model[@lamps[[x,y]]].to_s == "1"
78
+ print("*")
79
+ else
80
+ print(" ")
81
+ end
82
+ end
83
+ puts "\n"
84
+ end
85
+ end
86
+
87
+ def int01(x,y)
88
+ v = Z3::Ast.int("l#{x},#{y}")
89
+ @solver.assert v >= 0
90
+ @solver.assert v <= 1
91
+ v
92
+ end
93
+ end
94
+
95
+ LightUp.new(
96
+ """
97
+ ....0..
98
+ .......
99
+ x.2.x..
100
+ ...3...
101
+ ..x.x.3
102
+ .......
103
+ ..1....
104
+ """
105
+ ).solve!
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ class MiniSudokuSolver
6
+ def initialize(data)
7
+ @data = data
8
+ @solver = Z3::Solver.new
9
+ end
10
+
11
+ def solve!
12
+ @cells = (0..5).map do |j|
13
+ (0..5).map do |i|
14
+ cell_var(@data[j][i], i, j)
15
+ end
16
+ end
17
+
18
+ @cells.each do |row|
19
+ @solver.assert Z3::Ast.distinct(*row)
20
+ end
21
+ @cells.transpose.each do |column|
22
+ @solver.assert Z3::Ast.distinct(*column)
23
+ end
24
+ @cells.each_slice(2) do |rows|
25
+ rows.transpose.each_slice(3) do |square|
26
+ @solver.assert Z3::Ast.distinct(*square.flatten)
27
+ end
28
+ end
29
+
30
+ if @solver.check == :sat
31
+ @model = @solver.model
32
+ print_answer!
33
+ else
34
+ puts "failed to solve"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def cell_var(cell, i, j)
41
+ v = Z3::Ast.int("cell[#{i+1},#{j+1}]")
42
+ @solver.assert v >= 1
43
+ @solver.assert v <= 6
44
+ @solver.assert v == cell if cell != nil
45
+ v
46
+ end
47
+
48
+ def print_answer!
49
+ @cells.each do |row|
50
+ puts row.map{|v| @model[v]}.join(" ")
51
+ end
52
+ end
53
+ end
54
+
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!