Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 499 lines (433 sloc) 15.763 kB
35435dc @masak [crypt.pl] hanoi game, legal move
authored
1 use Test;
2
3 role Event {
4 method Str {
5 sub event() { self.^name }
6 sub name($attr) { $attr.name.substr(2) }
7 sub value($attr) { $attr.get_value(self) }
8
9 "{event}[{map { ":{name $_}<{value $_}>" }, self.^attributes}]"
10 }
11 }
12
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
13 class Hanoi::DiskMoved does Event {
0f569fa @masak [crypt.pl] size -> disk refactor
authored
14 has $.disk;
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
15 has $.source;
16 has $.target;
17 }
18
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
19 class Hanoi::AchievementUnlocked does Event {
35435dc @masak [crypt.pl] hanoi game, legal move
authored
20 }
21
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
22 class Hanoi::AchievementLocked does Event {
96d9cf3 @masak [crypt.pl] achievements can be locked, too
authored
23 }
24
a47664c @masak [crypt.pl] can remove disks
authored
25 class Hanoi::DiskRemoved does Event {
0f569fa @masak [crypt.pl] size -> disk refactor
authored
26 has $.disk;
a47664c @masak [crypt.pl] can remove disks
authored
27 has $.source;
28 }
29
89ce144 @masak [crypt.pl] adding a disk
authored
30 class Hanoi::DiskAdded does Event {
0f569fa @masak [crypt.pl] size -> disk refactor
authored
31 has $.disk;
89ce144 @masak [crypt.pl] adding a disk
authored
32 has $.target;
33 }
34
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
35 class X::Hanoi::LargerOnSmaller is Exception {
36 has $.larger;
37 has $.smaller;
38
39 method message($_:) {
40 "Cannot put the {.larger} on the {.smaller}"
41 }
42 }
43
4b7c1d2 @masak [crypt.pl] can't use rod names which don't exist
authored
44 class X::Hanoi::NoSuchRod is Exception {
45 has $.rod;
46 has $.name;
47
48 method message($_:) {
49 "No such {.rod} rod '{.name}'"
50 }
51 }
52
efa9ba6 @masak [crypt.pl] cannot move from a rod with no disks
authored
53 class X::Hanoi::RodHasNoDisks is Exception {
54 has $.name;
55
56 method message($_:) {
57 "Cannot move from the {.name} rod because there is no disk there"
58 }
59 }
60
0a4ebe5 @masak [crypt.pl] error when trying to move covered disk
authored
61 class X::Hanoi::CoveredDisk is Exception {
62 has $.disk;
63 has @.covered_by;
64
65 method message($_:) {
66 sub last_and(@things) {
67 map { "{'and ' if $_ == @things.end}@things[$_]" }, ^@things
68 }
8de2ebb @masak [crypt.pl] fixed CoveredDisk exception message
authored
69 my $disklist = @.covered_by > 1
70 ?? join ', ', last_and map { "the $_" }, @.covered_by
71 !! "the @.covered_by[0]";
0a4ebe5 @masak [crypt.pl] error when trying to move covered disk
authored
72 "Cannot move the {.disk}: it is covered by $disklist"
73 }
74 }
75
94cf486 @masak [crypt.pl] removing other disks is forbidden
authored
76 class X::Hanoi::ForbiddenDiskRemoval is Exception {
77 has $.disk;
78
79 method message($_:) {
80 "Removing the {.disk} is forbidden"
81 }
82 }
83
50e0179 @masak [crypt.pl] cannot remove an already removed disk
authored
84 class X::Hanoi::DiskHasBeenRemoved is Exception {
85 has $.disk;
86 has $.action;
87
88 method message($_:) {
89 "Cannot {.action} the {.disk} because it has been removed"
90 }
91 }
92
41501d6 @masak [crypt.pl] cannot add a disk with a made-up name
authored
93 class X::Hanoi::NoSuchDisk is Exception {
94 has $.disk;
95
96 method message($_:) {
97 "Cannot add a {.disk} because there is no such disk"
98 }
99 }
100
4d11a9d @masak [crypt.pl] cannot add a disk that's already there
authored
101 class X::Hanoi::DiskAlreadyOnARod is Exception {
102 has $.disk;
103
104 method message($_:) {
105 "Cannot add the {.disk} because it is already on a rod"
106 }
107 }
108
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
109 class Hanoi::Game {
0a4ebe5 @masak [crypt.pl] error when trying to move covered disk
authored
110 my @disks = map { "$_ disk" }, <tiny small medium large huge>;
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
111 my %size_of = @disks Z 1..5;
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
112
113 has %!state =
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
114 left => [reverse @disks],
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
115 middle => [],
116 right => [],
117 ;
118
6aa70d4 @masak [crypt.pl] post-unlocking achievement logic
authored
119 has $!achievement = 'locked';
120
e2a3e5d @masak [crypt.pl] allow move syntax specifying disks
authored
121 method move($source is copy, $target) {
122 if $source eq any @disks {
00629f1 @masak [crypt.pl] refactor; extract private method
authored
123 $source = self!rod_with_disk($source, 'move');
e2a3e5d @masak [crypt.pl] allow move syntax specifying disks
authored
124 }
00629f1 @masak [crypt.pl] refactor; extract private method
authored
125 die X::Hanoi::NoSuchRod.new(:rod<source>, :name($source))
126 unless %!state.exists($source);
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
127 die X::Hanoi::NoSuchRod.new(:rod<target>, :name($target))
128 unless %!state.exists($target);
00629f1 @masak [crypt.pl] refactor; extract private method
authored
129 my @source_rod := %!state{$source};
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
130 die X::Hanoi::RodHasNoDisks.new(:name($source))
131 unless @source_rod;
132 my @target_rod := %!state{$target};
133 my $moved_disk = @source_rod[*-1];
134 if @target_rod {
135 my $covered_disk = @target_rod[*-1];
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
136 if %size_of{$moved_disk} > %size_of{$covered_disk} {
137 die X::Hanoi::LargerOnSmaller.new(
138 :larger($moved_disk),
139 :smaller($covered_disk)
140 );
141 }
142 }
0f569fa @masak [crypt.pl] size -> disk refactor
authored
143 my @events
144 = Hanoi::DiskMoved.new(:disk($moved_disk), :$source, :$target);
caea86f @masak [crypt.pl] refactor - private apply method
authored
145 if %!state<right> == @disks-1
146 && $target eq 'right'
147 && $!achievement eq 'locked' {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
148 @events.push(Hanoi::AchievementUnlocked.new);
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
149 }
0f569fa @masak [crypt.pl] size -> disk refactor
authored
150 if $moved_disk eq 'small disk' && $!achievement eq 'unlocked' {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
151 @events.push(Hanoi::AchievementLocked.new);
96d9cf3 @masak [crypt.pl] achievements can be locked, too
authored
152 }
caea86f @masak [crypt.pl] refactor - private apply method
authored
153 self!apply($_) for @events;
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
154 return @events;
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
155 }
caea86f @masak [crypt.pl] refactor - private apply method
authored
156
a47664c @masak [crypt.pl] can remove disks
authored
157 method remove($disk) {
00629f1 @masak [crypt.pl] refactor; extract private method
authored
158 my $source = self!rod_with_disk($disk, 'remove');
159 die X::Hanoi::ForbiddenDiskRemoval.new(:$disk)
0f569fa @masak [crypt.pl] size -> disk refactor
authored
160 unless $disk eq 'tiny disk';
161 my @events = Hanoi::DiskRemoved.new(:$disk, :$source);
00629f1 @masak [crypt.pl] refactor; extract private method
authored
162 self!apply($_) for @events;
163 return @events;
164 }
165
89ce144 @masak [crypt.pl] adding a disk
authored
166 method add($disk, $target) {
41501d6 @masak [crypt.pl] cannot add a disk with a made-up name
authored
167 die X::Hanoi::NoSuchDisk.new(:$disk)
168 unless $disk eq any(@disks);
2fecc91 @masak [crypt.pl] refuse to add to a nonexistent rod
authored
169 die X::Hanoi::NoSuchRod.new(:rod<target>, :name($target))
170 unless %!state.exists($target);
4d11a9d @masak [crypt.pl] cannot add a disk that's already there
authored
171 die X::Hanoi::DiskAlreadyOnARod.new(:$disk)
172 if grep { $disk eq any(@$_) }, %!state.values;
0f569fa @masak [crypt.pl] size -> disk refactor
authored
173 my @events = Hanoi::DiskAdded.new(:$disk, :$target);
89ce144 @masak [crypt.pl] adding a disk
authored
174 self!apply($_) for @events;
175 return @events;
176 }
177
00629f1 @masak [crypt.pl] refactor; extract private method
authored
178 # The method will throw X::Hanoi::CoveredDisk if the disk is not topmost,
179 # or X::Hanoi::DiskHasBeenRemoved if the disk isn't found on any rod.
180 method !rod_with_disk($disk, $action) {
181 for %!state -> (:key($rod), :value(@disks)) {
a47664c @masak [crypt.pl] can remove disks
authored
182 if $disk eq any(@disks) {
b6edc9c @masak [crypt.pl] cannot remove covered disks
authored
183 sub smaller_disks {
184 grep { %size_of{$_} < %size_of{$disk} }, @disks;
185 }
186 die X::Hanoi::CoveredDisk.new(:$disk, :covered_by(smaller_disks))
187 unless @disks[*-1] eq $disk;
00629f1 @masak [crypt.pl] refactor; extract private method
authored
188 return $rod;
a47664c @masak [crypt.pl] can remove disks
authored
189 }
190 }
00629f1 @masak [crypt.pl] refactor; extract private method
authored
191 die X::Hanoi::DiskHasBeenRemoved.new(:$disk, :$action);
a47664c @masak [crypt.pl] can remove disks
authored
192 }
193
caea86f @masak [crypt.pl] refactor - private apply method
authored
194 # RAKUDO: private multimethods NYI
195 method !apply(Event $_) {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
196 when Hanoi::DiskMoved {
caea86f @masak [crypt.pl] refactor - private apply method
authored
197 my @source_rod := %!state{.source};
198 my @target_rod := %!state{.target};
199 @target_rod.push( @source_rod.pop );
200 }
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
201 when Hanoi::AchievementUnlocked {
caea86f @masak [crypt.pl] refactor - private apply method
authored
202 $!achievement = 'unlocked';
203 }
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
204 when Hanoi::AchievementLocked {
caea86f @masak [crypt.pl] refactor - private apply method
authored
205 $!achievement = 'locked';
206 }
a47664c @masak [crypt.pl] can remove disks
authored
207 when Hanoi::DiskRemoved {
208 my @source_rod := %!state{.source};
209 @source_rod.pop;
210 }
4d11a9d @masak [crypt.pl] cannot add a disk that's already there
authored
211 when Hanoi::DiskAdded {
212 my @target_rod := %!state{.target};
0f569fa @masak [crypt.pl] size -> disk refactor
authored
213 @target_rod.push(.disk);
4d11a9d @masak [crypt.pl] cannot add a disk that's already there
authored
214 }
caea86f @masak [crypt.pl] refactor - private apply method
authored
215 }
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
216 }
217
66e182d @masak [crypt.pl] test function refactor
authored
218 sub throws_exception(&code, $ex_type, $message, &followup?) {
b0affd2 @masak [crypt.pl] larger disk on smaller not allowed
authored
219 &code();
220 ok 0, $message;
221 if &followup {
222 diag 'Not running followup because an exception was not triggered';
223 }
224 CATCH {
225 default {
226 ok 1, $message;
227 my $type_ok = $_.WHAT === $ex_type;
228 ok $type_ok , "right exception type ({$ex_type.^name})";
229 if $type_ok {
230 &followup($_);
231 } else {
232 diag "Got: {$_.WHAT.gist}\n"
233 ~"Expected: {$ex_type.gist}";
234 diag "Exception message: $_.message()";
235 diag 'Not running followup because type check failed';
236 }
237 }
35435dc @masak [crypt.pl] hanoi game, legal move
authored
238 }
239 }
240
241 multi MAIN('test', 'hanoi') {
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
242 {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
243 my $game = Hanoi::Game.new();
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
244
245 is $game.move('left', 'middle'),
0f569fa @masak [crypt.pl] size -> disk refactor
authored
246 Hanoi::DiskMoved.new(
247 :disk('tiny disk'),
248 :source<left>,
249 :target<middle>
250 ),
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
251 'moving a disk (+)';
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
252
253 throws_exception
254 { $game.move('left', 'middle') },
255 X::Hanoi::LargerOnSmaller,
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
256 'moving a disk (-) larger disk on smaller',
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
257 {
258 is .larger, 'small disk', '.larger attribute';
259 is .smaller, 'tiny disk', '.smaller attribute';
260 is .message,
261 'Cannot put the small disk on the tiny disk',
262 '.message attribute';
263 };
264
265 throws_exception
266 { $game.move('gargle', 'middle') },
267 X::Hanoi::NoSuchRod,
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
268 'moving a disk (-) no such source rod',
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
269 {
270 is .rod, 'source', '.rod attribute';
271 is .name, 'gargle', '.name attribute';
272 is .message,
273 q[No such source rod 'gargle'],
274 '.message attribute';
275 };
276
277 throws_exception
278 { $game.move('middle', 'clown') },
279 X::Hanoi::NoSuchRod,
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
280 'moving a disk (-) no such target rod',
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
281 {
282 is .rod, 'target', '.rod attribute';
283 is .name, 'clown', '.name attribute';
284 is .message,
285 q[No such target rod 'clown'],
286 '.message attribute';
287 };
288
289 throws_exception
290 { $game.move('right', 'middle') },
291 X::Hanoi::RodHasNoDisks,
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
292 'moving a disk (-) rod has no disks',
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
293 {
294 is .name, 'right', '.name attribute';
295 is .message,
296 q[Cannot move from the right rod because there is no disk there],
297 '.message attribute';
298 };
299 }
35435dc @masak [crypt.pl] hanoi game, legal move
authored
300
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
301 {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
302 my $game = Hanoi::Game.new();
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
303
304 multi hanoi_moves($source, $, $target, 1) {
305 # A single disk, easy; just move it directly.
306 $source, 'to', $target
307 }
308 multi hanoi_moves($source, $helper, $target, $n) {
309 # $n-1 disks on to; move them off to the $helper rod first...
310 hanoi_moves($source, $target, $helper, $n-1),
311 # ...then move over the freed disk at the bottom...
312 hanoi_moves($source, $helper, $target, 1),
313 # ...and finally move the rest from $helper to $target.
314 hanoi_moves($helper, $source, $target, $n-1)
315 }
316
317 # Let's play out the thing to the end. 32 moves.
318 my @moves = hanoi_moves("left", "middle", "right", 5);
319 # RAKUDO: .splice doesn't do WhateverCode yet: wanted *-3
320 my @last_move = @moves.splice(@moves.end-2);
321
322 lives_ok {
323 for @moves -> $source, $, $target {
324 my ($event, @rest) = $game.move($source, $target);
325 die "Unexpected event type: {$event.name}"
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
326 unless $event ~~ Hanoi::DiskMoved;
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
327 die "Unexpected extra events: @rest"
328 if @rest;
329 }
330 }, 'making all the moves to the end of the game works';
35435dc @masak [crypt.pl] hanoi game, legal move
authored
331
efa9ba6 @masak [crypt.pl] cannot move from a rod with no disks
authored
332 {
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
333 my ($source, $, $target) = @last_move;
334 is $game.move($source, $target), (
0f569fa @masak [crypt.pl] size -> disk refactor
authored
335 Hanoi::DiskMoved.new(:disk('tiny disk'), :$source, :$target),
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
336 Hanoi::AchievementUnlocked.new(),
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
337 ), 'putting all disks on the right rod unlocks achievement';
6aa70d4 @masak [crypt.pl] post-unlocking achievement logic
authored
338
339 $game.move($target, $source);
340 is $game.move($source, $target), (
0f569fa @masak [crypt.pl] size -> disk refactor
authored
341 Hanoi::DiskMoved.new(:disk('tiny disk'), :$source, :$target),
6aa70d4 @masak [crypt.pl] post-unlocking achievement logic
authored
342 ), 'moving things back and forth does not unlock achievement again';
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
343 }
96d9cf3 @masak [crypt.pl] achievements can be locked, too
authored
344
345 {
346 $game.move('right', 'middle');
347 is $game.move(my $source = 'right', my $target = 'left'), (
0f569fa @masak [crypt.pl] size -> disk refactor
authored
348 Hanoi::DiskMoved.new(:disk('small disk'), :$source, :$target),
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
349 Hanoi::AchievementLocked.new(),
96d9cf3 @masak [crypt.pl] achievements can be locked, too
authored
350 ), 'removing two disks from the right rod locks achievement';
351 }
b9caade @masak [crypt.pl] all disks on the right rod => win
authored
352 }
efa9ba6 @masak [crypt.pl] cannot move from a rod with no disks
authored
353
e2a3e5d @masak [crypt.pl] allow move syntax specifying disks
authored
354 {
df311c2 @masak [crypt.pl] name refactor: Hanoi::
authored
355 my $game = Hanoi::Game.new();
e2a3e5d @masak [crypt.pl] allow move syntax specifying disks
authored
356
357 is $game.move('tiny disk', my $target = 'middle'),
0f569fa @masak [crypt.pl] size -> disk refactor
authored
358 Hanoi::DiskMoved.new(:disk('tiny disk'), :source<left>, :$target),
e2a3e5d @masak [crypt.pl] allow move syntax specifying disks
authored
359 'naming source disk instead of the rod (+)';
360 }
361
0a4ebe5 @masak [crypt.pl] error when trying to move covered disk
authored
362 {
363 my $game = Hanoi::Game.new();
364
365 throws_exception
366 { $game.move('large disk', 'right') },
367 X::Hanoi::CoveredDisk,
368 'naming source disk instead of the rod (-)',
369 {
370 is .disk, 'large disk', '.disk attribute';
371 is .covered_by, ['medium disk', 'small disk', 'tiny disk'],
372 '.covered_by attribute';
373 is .message,
374 'Cannot move the large disk: it is covered by '
375 ~ 'the medium disk, the small disk, and the tiny disk',
376 '.message attribute';
377 };
378 }
379
a47664c @masak [crypt.pl] can remove disks
authored
380 {
381 my $game = Hanoi::Game.new();
382
8de2ebb @masak [crypt.pl] fixed CoveredDisk exception message
authored
383 throws_exception
384 { $game.move('small disk', 'right') },
385 X::Hanoi::CoveredDisk,
386 'naming source disk instead of the rod (-) no and for one-item lists',
387 {
388 is .message,
389 'Cannot move the small disk: it is covered by the tiny disk',
390 '.message attribute';
391 };
392 }
393
394 {
395 my $game = Hanoi::Game.new();
396
a47664c @masak [crypt.pl] can remove disks
authored
397 is $game.remove('tiny disk'),
0f569fa @masak [crypt.pl] size -> disk refactor
authored
398 Hanoi::DiskRemoved.new(:disk('tiny disk'), :source<left>),
a47664c @masak [crypt.pl] can remove disks
authored
399 'removing a disk (+)';
94cf486 @masak [crypt.pl] removing other disks is forbidden
authored
400
401 throws_exception
402 { $game.remove('small disk') },
403 X::Hanoi::ForbiddenDiskRemoval,
404 'removing a disk (-) removing disk is forbidden',
405 {
406 is .disk, 'small disk', '.disk attribute';
407 is .message,
408 'Removing the small disk is forbidden',
409 '.message attribute';
410 };
b6edc9c @masak [crypt.pl] cannot remove covered disks
authored
411
412 throws_exception
413 { $game.remove('medium disk') },
414 X::Hanoi::CoveredDisk,
415 'removing a disk (-) the disk is covered',
416 {
417 is .disk, 'medium disk', '.disk attribute';
418 is .covered_by, ['small disk'],
419 '.covered_by attribute';
420 };
26f2c6b @masak [crypt.pl] uncovered, removal of a disk still forbidden
authored
421
422 $game.move('small disk', 'middle');
423 throws_exception
424 { $game.remove('medium disk') },
425 X::Hanoi::ForbiddenDiskRemoval,
426 'removing a disk (-) uncovered, removal is still forbidden',
427 {
428 is .disk, 'medium disk', '.disk attribute';
429 };
a47664c @masak [crypt.pl] can remove disks
authored
430 }
431
50e0179 @masak [crypt.pl] cannot remove an already removed disk
authored
432 {
433 my $game = Hanoi::Game.new();
434
435 $game.remove('tiny disk');
436
437 throws_exception
438 { $game.remove('tiny disk') },
439 X::Hanoi::DiskHasBeenRemoved,
440 'removing a disk (-) the disk had already been removed',
441 {
442 is .disk, 'tiny disk', '.disk attribute';
443 is .action, 'remove', '.action attribute';
444 is .message,
445 'Cannot remove the tiny disk because it has been removed',
446 '.message attribute';
447 };
c9e01dd @masak [crypt.pl] cannot move a disk that has been removed
authored
448
449 throws_exception
450 { $game.move('tiny disk', 'middle') },
451 X::Hanoi::DiskHasBeenRemoved,
452 'moving a disk (-) the disk had already been removed',
453 {
454 is .disk, 'tiny disk', '.disk attribute';
455 is .action, 'move', '.action attribute';
456 is .message,
457 'Cannot move the tiny disk because it has been removed',
458 '.message attribute';
459 };
89ce144 @masak [crypt.pl] adding a disk
authored
460
2fecc91 @masak [crypt.pl] refuse to add to a nonexistent rod
authored
461 throws_exception
462 { $game.add('tiny disk', 'pineapple') },
463 X::Hanoi::NoSuchRod,
464 'moving a disk (-) the rod does not exist',
465 {
466 is .rod, 'target', '.rod attribute';
467 is .name, 'pineapple', '.name attribute';
468 };
469
89ce144 @masak [crypt.pl] adding a disk
authored
470 is $game.add('tiny disk', 'left'),
0f569fa @masak [crypt.pl] size -> disk refactor
authored
471 Hanoi::DiskAdded.new(:disk('tiny disk'), :target<left>),
89ce144 @masak [crypt.pl] adding a disk
authored
472 'adding a disk (+)';
41501d6 @masak [crypt.pl] cannot add a disk with a made-up name
authored
473
474 throws_exception
475 { $game.add('humongous disk', 'middle') },
476 X::Hanoi::NoSuchDisk,
477 'adding a disk (-) there is no such disk',
478 {
479 is .disk, 'humongous disk', '.disk attribute';
480 is .message,
481 'Cannot add a humongous disk because there is no such disk',
482 '.message attribute';
483 };
4d11a9d @masak [crypt.pl] cannot add a disk that's already there
authored
484
485 throws_exception
486 { $game.add('tiny disk', 'right') },
487 X::Hanoi::DiskAlreadyOnARod,
488 'adding a disk (-) the disk is already on a rod',
489 {
490 is .disk, 'tiny disk', '.disk attribute';
491 is .message,
492 'Cannot add the tiny disk because it is already on a rod',
493 '.message attribute';
494 };
50e0179 @masak [crypt.pl] cannot remove an already removed disk
authored
495 }
496
35435dc @masak [crypt.pl] hanoi game, legal move
authored
497 done;
498 }
Something went wrong with that request. Please try again.