doku 1.0.0
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.
- data/Gemfile +32 -0
- data/README.rdoc +15 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/doku.rb +4 -0
- data/lib/doku/dancing_links.rb +561 -0
- data/lib/doku/grid.rb +241 -0
- data/lib/doku/hexadoku.rb +56 -0
- data/lib/doku/hexamurai.rb +95 -0
- data/lib/doku/puzzle.rb +250 -0
- data/lib/doku/solver.rb +103 -0
- data/lib/doku/sudoku.rb +42 -0
- data/spec/dancing_links_spec.rb +212 -0
- data/spec/hexadoku_spec.rb +71 -0
- data/spec/hexamurai_spec.rb +38 -0
- data/spec/puzzle_spec.rb +147 -0
- data/spec/solution_spec.rb +278 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/sudoku_spec.rb +52 -0
- data/spec/watch.rb +9 -0
- metadata +198 -0
data/spec/puzzle_spec.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'backports' unless defined? require_relative
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
|
4
|
+
class TestPuzzle < Doku::Puzzle
|
5
|
+
has_squares [1,2,3,4]
|
6
|
+
has_glyphs [true,false]
|
7
|
+
|
8
|
+
define_group [1,2]
|
9
|
+
define_group [2,3]
|
10
|
+
define_group [3,4]
|
11
|
+
define_group [4,1]
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Puzzle instance" do
|
15
|
+
before do
|
16
|
+
@puzzle = TestPuzzle.new(1 => true)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is basically a hash of square => glyph" do
|
20
|
+
@puzzle[1].should == true
|
21
|
+
@puzzle[4].should == nil
|
22
|
+
|
23
|
+
@puzzle[1] = false
|
24
|
+
@puzzle[1].should == false
|
25
|
+
|
26
|
+
@puzzle[1] = nil
|
27
|
+
@puzzle[1].should == nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has an each method that excludes nil values" do
|
31
|
+
TestPuzzle.new(2 => nil).each { raise }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "only allows squares as keys" do
|
35
|
+
lambda { @puzzle[0] }.should raise_error IndexError
|
36
|
+
lambda { @puzzle[0] = false }.should raise_error IndexError
|
37
|
+
end
|
38
|
+
|
39
|
+
it "only allows glyphs or nil as values" do
|
40
|
+
lambda { @puzzle[1] = 10 }.should raise_error ArgumentError
|
41
|
+
end
|
42
|
+
|
43
|
+
it "can not be initialized with bad data" do
|
44
|
+
lambda { TestPuzzle.new(:foo => true) }.should raise_error IndexError
|
45
|
+
lambda { TestPuzzle.new(1 => :foo) }.should raise_error ArgumentError
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "comparison operators say" do
|
49
|
+
it "A == B iff the square=>glyph pairs in A and B are the same" do
|
50
|
+
@puzzle.should_not == TestPuzzle.new
|
51
|
+
TestPuzzle.new.should_not == @puzzle.should
|
52
|
+
|
53
|
+
puzzle2 = TestPuzzle.new(1 => true)
|
54
|
+
@puzzle.should == puzzle2
|
55
|
+
puzzle2.should == @puzzle
|
56
|
+
|
57
|
+
(TestPuzzle.new(1 => true) != @puzzle).should == false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "A eql? B is the same as A == B" do
|
61
|
+
@puzzle.should_not eql TestPuzzle.new
|
62
|
+
TestPuzzle.new.should_not eql @puzzle.should
|
63
|
+
|
64
|
+
puzzle2 = TestPuzzle.new(1 => true)
|
65
|
+
@puzzle.should eql puzzle2
|
66
|
+
puzzle2.should eql @puzzle
|
67
|
+
end
|
68
|
+
|
69
|
+
it "A.subset?(B) iff square=>glyph pairs in A are a subset of those in B" do
|
70
|
+
@puzzle.should_not be_a_subset TestPuzzle.new
|
71
|
+
TestPuzzle.new.should be_a_subset @puzzle
|
72
|
+
|
73
|
+
@puzzle.should be_a_subset @puzzle
|
74
|
+
|
75
|
+
@puzzle.should be_a_subset TestPuzzle.new(1 => true, 3 => false)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#filled?" do
|
80
|
+
it "returns true iff all squares have been assigned a glyph" do
|
81
|
+
TestPuzzle.new(1 => true, 2=> true, 3 => false, 4 => false).should be_filled
|
82
|
+
@puzzle.should_not be_filled
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#valid?" do
|
87
|
+
it "returns false iff a group has the same glyph in it twice" do
|
88
|
+
@puzzle.should be_valid
|
89
|
+
TestPuzzle.new(1 => true, 2=> true).should_not be_valid
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#solution?" do
|
94
|
+
it "returns true iff it is filled and valid" do
|
95
|
+
puzzle2 = TestPuzzle.new(1 => false, 2 => true, 3 => false, 4 => true)
|
96
|
+
puzzle2.should be_a_solution
|
97
|
+
|
98
|
+
puzzle3 = TestPuzzle.new(1 => true, 2=> true, 3 => false, 4 => false)
|
99
|
+
puzzle3.should_not be_a_solution
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#solution_for?(puzzle)" do
|
104
|
+
it "returns true if this puzzle is a solution and a super set of the other puzzle" do
|
105
|
+
puzzle4 = TestPuzzle.new(1 => false, 2 => true, 3 => false, 4 => true)
|
106
|
+
puzzle4.should_not be_a_solution_for @puzzle
|
107
|
+
|
108
|
+
puzzle5 = TestPuzzle.new(1 => true, 2 => false, 3 => true, 4 => false)
|
109
|
+
puzzle5.should be_a_solution_for @puzzle
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
shared_examples_for "copied puzzle" do
|
114
|
+
it "produces an equal puzzle" do
|
115
|
+
@copy.should == @puzzle
|
116
|
+
@copy.should_not equal @puzzle
|
117
|
+
end
|
118
|
+
|
119
|
+
it "produces a puzzle with a different glyph_state hash" do
|
120
|
+
@copy.glyph_state.should_not equal @puzzle.glyph_state
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "dup" do
|
125
|
+
before do
|
126
|
+
@copy = @puzzle.dup
|
127
|
+
end
|
128
|
+
|
129
|
+
it_should_behave_like "copied puzzle"
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "clone" do
|
133
|
+
before do
|
134
|
+
@copy = @puzzle.clone
|
135
|
+
end
|
136
|
+
|
137
|
+
it_should_behave_like "copied puzzle"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "equality and hash codes are not affected by nil values" do
|
141
|
+
puzzle2 = TestPuzzle.new(1 => true, 3 => nil)
|
142
|
+
@puzzle[2] = nil
|
143
|
+
puzzle2.should == @puzzle
|
144
|
+
puzzle2.hash.should == @puzzle.hash
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'backports' unless defined? require_relative
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
|
4
|
+
describe "Puzzle#solve" do
|
5
|
+
context 'given the sudoku puzzle' do
|
6
|
+
before do
|
7
|
+
@puzzle = Doku::Sudoku.new <<END
|
8
|
+
...|..8|...
|
9
|
+
..7|.35|..9
|
10
|
+
5..|4.6|8..
|
11
|
+
---+---+---
|
12
|
+
...|..4|2..
|
13
|
+
4..|...|.37
|
14
|
+
8..|...|5..
|
15
|
+
---+---+---
|
16
|
+
.9.|.67|...
|
17
|
+
..3|...|1.5
|
18
|
+
...|...|..3
|
19
|
+
END
|
20
|
+
|
21
|
+
@solution = @puzzle.solve
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'solves the puzzle' do
|
25
|
+
@solution.solution_for?(@puzzle).should == true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'is the correct solution' do
|
29
|
+
@solution.to_grid_string.strip.should == <<END.strip
|
30
|
+
964|278|351
|
31
|
+
287|135|649
|
32
|
+
531|496|872
|
33
|
+
---+---+---
|
34
|
+
319|754|286
|
35
|
+
452|681|937
|
36
|
+
876|923|514
|
37
|
+
---+---+---
|
38
|
+
195|367|428
|
39
|
+
723|849|165
|
40
|
+
648|512|793
|
41
|
+
END
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can also find the solution using Donald Knuth's recursive DLX" do
|
45
|
+
sm = @puzzle.to_link_matrix
|
46
|
+
exact_cover = sm.find_exact_cover_recursive
|
47
|
+
solution = @puzzle.exact_cover_to_solution exact_cover
|
48
|
+
solution.should == @solution
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'given a sudoku puzzle with two solutions' do
|
53
|
+
before do
|
54
|
+
@puzzle = Doku::Sudoku.new <<END
|
55
|
+
...|..8|...
|
56
|
+
..7|.3.|..9
|
57
|
+
5..|4.6|8..
|
58
|
+
---+---+---
|
59
|
+
...|..4|2..
|
60
|
+
4..|...|.37
|
61
|
+
8..|...|5..
|
62
|
+
---+---+---
|
63
|
+
.9.|.67|...
|
64
|
+
..3|...|1.5
|
65
|
+
...|...|..3
|
66
|
+
END
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'finds two solutions' do
|
70
|
+
solutions = @puzzle.solutions.to_a
|
71
|
+
solutions.size.should == 2
|
72
|
+
|
73
|
+
solutions.should include Doku::Sudoku.new <<END
|
74
|
+
964|278|351
|
75
|
+
287|135|649
|
76
|
+
531|496|872
|
77
|
+
---+---+---
|
78
|
+
319|754|286
|
79
|
+
452|681|937
|
80
|
+
876|923|514
|
81
|
+
---+---+---
|
82
|
+
195|367|428
|
83
|
+
723|849|165
|
84
|
+
648|512|793
|
85
|
+
END
|
86
|
+
|
87
|
+
solutions.should include Doku::Sudoku.new <<END
|
88
|
+
964|278|351
|
89
|
+
287|531|649
|
90
|
+
531|496|872
|
91
|
+
---+---+---
|
92
|
+
359|714|286
|
93
|
+
412|685|937
|
94
|
+
876|923|514
|
95
|
+
---+---+---
|
96
|
+
195|367|428
|
97
|
+
723|849|165
|
98
|
+
648|152|793
|
99
|
+
END
|
100
|
+
|
101
|
+
solutions[0].should be_a_solution_for @puzzle
|
102
|
+
solutions[1].should be_a_solution_for @puzzle
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'given a sudoku puzzle with NO solutions' do
|
108
|
+
before do
|
109
|
+
@puzzle = Doku::Sudoku.new <<END
|
110
|
+
123|...|...
|
111
|
+
456|...|...
|
112
|
+
78.|...|...
|
113
|
+
---+---+---
|
114
|
+
..1|...|...
|
115
|
+
...|9..|...
|
116
|
+
..2|...|...
|
117
|
+
---+---+---
|
118
|
+
..4|...|...
|
119
|
+
...|9..|...
|
120
|
+
..5|...|...
|
121
|
+
END
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'finds no solutions' do
|
125
|
+
@puzzle.solve.should == nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'can not tell instantly there is no solution, in this case' do
|
129
|
+
sm = @puzzle.to_link_matrix
|
130
|
+
sc = sm.columns.min_by(&:size)
|
131
|
+
sc.size.should > 0
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'finds no solutions using the recursive algorithm' do
|
135
|
+
sm = @puzzle.to_link_matrix
|
136
|
+
sm.find_exact_cover_recursive.should == nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'given a hexadoku puzzle' do
|
141
|
+
before do
|
142
|
+
# Elektor Hexadoku 2011
|
143
|
+
@puzzle = Doku::Hexadoku.new <<END
|
144
|
+
2A.7|.C..|9D64|8...
|
145
|
+
..3.|A..D|7...|.2F.
|
146
|
+
.1..|..0.|8...|.4AB
|
147
|
+
....|.7.2|..BC|.0.3
|
148
|
+
----+----+----+----
|
149
|
+
C2.8|.D3.|..4E|....
|
150
|
+
..FA|7.2.|B.3.|1C04
|
151
|
+
....|4..F|..1.|...E
|
152
|
+
9..B|1...|....|23..
|
153
|
+
----+----+----+----
|
154
|
+
..8C|....|...0|3..D
|
155
|
+
6...|.F..|1..A|....
|
156
|
+
D37E|.0.1|.9.8|AF..
|
157
|
+
....|3B..|.2D.|C.80
|
158
|
+
----+----+----+----
|
159
|
+
F.B.|51..|2.A.|....
|
160
|
+
3CA.|...7|.E..|..6.
|
161
|
+
.E4.|...9|3..5|.D..
|
162
|
+
...1|F3A4|..9.|5.E2
|
163
|
+
END
|
164
|
+
|
165
|
+
@solution = @puzzle.solve
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'finds the correct solution' do
|
169
|
+
@solution.to_grid_string.should == <<END.strip
|
170
|
+
2A07|BCF3|9D64|8E15
|
171
|
+
B839|A54D|70E1|62FC
|
172
|
+
51CD|E906|8F23|74AB
|
173
|
+
46EF|8712|A5BC|D093
|
174
|
+
----+----+----+----
|
175
|
+
C218|6D3B|074E|9A5F
|
176
|
+
EDFA|7825|B639|1C04
|
177
|
+
0753|4A9F|C812|B6DE
|
178
|
+
946B|1EC0|DA5F|2378
|
179
|
+
----+----+----+----
|
180
|
+
158C|946A|EBF0|372D
|
181
|
+
6B20|CFD8|137A|E549
|
182
|
+
D37E|2051|49C8|AFB6
|
183
|
+
AF94|3B7E|52D6|C180
|
184
|
+
----+----+----+----
|
185
|
+
F9B6|51EC|24AD|0837
|
186
|
+
3CA5|D287|FE0B|4961
|
187
|
+
7E42|06B9|3185|FDCA
|
188
|
+
80D1|F3A4|6C97|5BE2
|
189
|
+
END
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "given a Hexamurai puzzle" do
|
194
|
+
before do
|
195
|
+
# From Elektor 2011-07 (with extra hints so it can be solved quickly).
|
196
|
+
@puzzle = Doku::Hexamurai.new <<END
|
197
|
+
|39DEA62B|80C1745F|
|
198
|
+
|..213...|5.B4....|
|
199
|
+
|..0.....|......2.|
|
200
|
+
|.....4.5|..9A1.E.|
|
201
|
+
|..3C....|.1654.0.|
|
202
|
+
|.1..2...|3......6|
|
203
|
+
|...90...|BD4.25..|
|
204
|
+
|2.57....|ACE03.D1|
|
205
|
+
--------+--------+--------+--------
|
206
|
+
52.0..3.|A.....B.|0.....3.|78A....6
|
207
|
+
4.8.1...|..9.....|...E..4.|2.063...
|
208
|
+
.7......|.4F.50.9|.7.2..8E|.1..4...
|
209
|
+
..6...0.|.....C3.|...6.7.0|5..3..21
|
210
|
+
A1D24E6B|9083C.5F|E62BAD14|.....8..
|
211
|
+
E0347.28|CFA5BD96|14830E72|.9....5A
|
212
|
+
8B96FC50|ED.2134A|950CB6F8|.2.1.4.3
|
213
|
+
7.FC3A9D|1B64E2.0|FA7..9C3|0..E.2..
|
214
|
+
--------+--------+--------+--------
|
215
|
+
.3.1..7.|B.5F2.E4|31......|B.6....4
|
216
|
+
C.......|4..7..0D|..6...B.|9.40.178
|
217
|
+
..28....|.C.A..73|...49..1|..3.5.0.
|
218
|
+
047.5...|.6.1....|...045..|1....3..
|
219
|
+
..036.4.|.1.C.EA.|60473..9|........
|
220
|
+
1......2|8.4D.96.|A3EF..2.|4.5.0...
|
221
|
+
..4..51.|.7.E3...|B291..5.|....C637
|
222
|
+
6.5...8.|...9...1|8DC5..0.|........
|
223
|
+
--------+--------+--------+--------
|
224
|
+
|...B..39|..0..2..|
|
225
|
+
|5.804..2|.6.3.E7.|
|
226
|
+
|...6....|....5.3.|
|
227
|
+
|...2.7..|C4.....0|
|
228
|
+
|..685340|.729A1..|
|
229
|
+
|7..51...|.....6.3|
|
230
|
+
|...4....|.B..0...|
|
231
|
+
|...3....|0......2|
|
232
|
+
END
|
233
|
+
end
|
234
|
+
|
235
|
+
it "can be solved correctly" do
|
236
|
+
solution = @puzzle.solve
|
237
|
+
solution.should be_solution_for @puzzle
|
238
|
+
solution.to_s.should == <<END.chomp
|
239
|
+
|39DEA62B|80C1745F|
|
240
|
+
|87213FDC|5EB4906A|
|
241
|
+
|450A79E1|63DF8B2C|
|
242
|
+
|6CBF8405|729A13ED|
|
243
|
+
|BA3CFE7D|21654809|
|
244
|
+
|014D25A8|39F7ECB6|
|
245
|
+
|F6E901C3|BD4825A7|
|
246
|
+
|28579B64|ACE03FD1|
|
247
|
+
--------+--------+--------+--------
|
248
|
+
52109D3F|AEC648B7|0F19D235|78A4BEC6
|
249
|
+
4E8D1BC5|73906AF2|D85EC14B|2F0637A9
|
250
|
+
37CA82E6|D4FB5019|C7326A8E|D19B40F5
|
251
|
+
B96FA704|5218DC3E|4BA6F790|5CE38D21
|
252
|
+
A1D24E6B|9083C75F|E62BAD14|C3F57890
|
253
|
+
E0347128|CFA5BD96|14830E72|69BDFC5A
|
254
|
+
8B96FC50|ED72134A|950CB6F8|A271E4D3
|
255
|
+
75FC3A9D|1B64E280|FA7D59C3|048E126B
|
256
|
+
--------+--------+--------+--------
|
257
|
+
D3A1087C|B95F26E4|31D870AC|B5629FE4
|
258
|
+
CFB526A3|48E7910D|5C6A23BF|9E40D178
|
259
|
+
9628E4B1|0CDAF573|2EB49861|FD375A0C
|
260
|
+
047E5FD9|3621ABC8|79F045ED|1AC863B2
|
261
|
+
FD036947|21BC8EA5|60473FD9|8B2CA51E
|
262
|
+
1AE7C3F2|854D096B|A3EF1C27|46590B8D
|
263
|
+
2849B51A|670E3FDC|B291845A|E0DFC637
|
264
|
+
6C5BD08E|FA397421|8DC5EB06|371A294F
|
265
|
+
--------+--------+--------+--------
|
266
|
+
|AF7BC839|E50D6214|
|
267
|
+
|5D804AF2|9613CE7B|
|
268
|
+
|94C6B01E|FA725D38|
|
269
|
+
|E312D756|C48BFA90|
|
270
|
+
|CB685340|D729A1FE|
|
271
|
+
|709512BF|48AED6C3|
|
272
|
+
|D2F4EC9A|1B360785|
|
273
|
+
|1EA36D87|0F5CB942|
|
274
|
+
END
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join File.dirname(__FILE__), '..', 'lib'
|
2
|
+
require 'rspec'
|
3
|
+
require 'doku'
|
4
|
+
|
5
|
+
# Change this to true if a test is failing and you want more clues about why.
|
6
|
+
EXTRA_ASSERTS = false
|
7
|
+
|
8
|
+
if EXTRA_ASSERTS
|
9
|
+
# Add any patches here that help check the validity of the algorithm but are
|
10
|
+
# not necessary to have in the production code.
|
11
|
+
|
12
|
+
class Doku::DancingLinks::LinkMatrix::Column
|
13
|
+
def size=(num)
|
14
|
+
raise "bad class" unless Fixnum === num
|
15
|
+
raise "negative value #{num}" if num < 0
|
16
|
+
@size = num
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/sudoku_spec.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'backports' unless defined? require_relative
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
|
4
|
+
describe Doku::Sudoku do
|
5
|
+
before do
|
6
|
+
@puzzle = Doku::Sudoku.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'has 81 squares' do
|
10
|
+
Doku::Sudoku.squares.size.should == 81
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has 9 squares in the first row' do
|
14
|
+
first_row = Doku::Sudoku.squares.select { |s| s.matches?(:y => 0) }
|
15
|
+
first_row.size.should == 9
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has the right number of groups' do
|
19
|
+
Doku::Sudoku.groups.size.should == 3*9
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has methods for getting and setting glyphs by coordinates' do
|
23
|
+
# (just like any puzzle class that includes the PuzzleOnGrid module)
|
24
|
+
|
25
|
+
@puzzle.get(0, 1).should == nil
|
26
|
+
@puzzle.set(0, 1, 4)
|
27
|
+
@puzzle.set(5, 7, 9)
|
28
|
+
@puzzle.set(6, 6, 1)
|
29
|
+
@puzzle.set(6, 6, nil)
|
30
|
+
@puzzle.get(0, 1).should == 4
|
31
|
+
@puzzle.to_s.should == <<END.strip
|
32
|
+
...|...|...
|
33
|
+
4..|...|...
|
34
|
+
...|...|...
|
35
|
+
---+---+---
|
36
|
+
...|...|...
|
37
|
+
...|...|...
|
38
|
+
...|...|...
|
39
|
+
---+---+---
|
40
|
+
...|...|...
|
41
|
+
...|..9|...
|
42
|
+
...|...|...
|
43
|
+
END
|
44
|
+
end
|
45
|
+
|
46
|
+
it "the get method has a good error message" do
|
47
|
+
msg = "Square not found in Doku::Sudoku: Square(19, david)."
|
48
|
+
lambda { @puzzle.get(19, 'david') }.should raise_error IndexError, msg
|
49
|
+
lambda { @puzzle.set(19, 'david', 10) }.should raise_error IndexError, msg
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|