Strangely Consistent

Musings about programming, Perl 6, and programming Perl 6

July 2 2012 — implementing Hanoi

Yesterday was about the interface. Today is about the implementation.

Look, a game!

$ bin/crypt hanoi

     |            |            |     
     =            |            |     
    ===           |            |     
   =====          |            |     
  =======         |            |     
 =========        |            |     
-------------------------------------

> okay...
Sorry, the game doesn't recognize that command. :/
'help' if you're confused as well.

> help
Goal: get all the disks to the right rod.
You can never place a larger disk on a smaller one.
Available commands:
  add <disk> <target>
  move <source> <target>
  remove <disk>
  q[uit]
  h[elp]
  s[how]

Disks: tiny disk, small disk, medium disk, large disk, huge disk
Rods: left, middle, right

> show  

     |            |            |     
     =            |            |     
    ===           |            |     
   =====          |            |     
  =======         |            |     
 =========        |            |     
-------------------------------------

> move left right 

     |            |            |     
     |            |            |     
    ===           |            |     
   =====          |            |     
  =======         |            |     
 =========        |            =     
-------------------------------------

> move middle left
Cannot move from the middle rod because there is no disk there.

> move left middle

     |            |            |     
     |            |            |     
     |            |            |     
   =====          |            |     
  =======         |            |     
 =========       ===           =     
-------------------------------------

> move small disk right
Cannot put the small disk on the tiny disk.

> quit

Now, let's implement this.

Moving disks

Let's just pause here for a while and say a bit about this method of working. The tests exercise the Hanoi game. They do this by saying either "calling this method returns this event" (success) or "calling this method throws this exception" (failure). The Hanoi game keeps state around, but the attributes are completely private. There are no getter methods. The public methods (.move, and later .remove and .add) may read attributes, but the attributes are only written to in a private !apply method. (The ! means "private".) The !apply method is called with an event.

So the only way to actually change the game state is to send an event of some kind to the !apply method. This keeps us honest in a way; we have to publicize all the important internal updates of the Hanoi game as events.

Removing disks

Now this last point is interesting. We introduce .remove, and suddenly we have to go back to .move and patch a "vulnerability" that suddenly opened up. I think many bugs are of this kind; old operations which end up in new situations for which they were not designed.

Adding disks

Command-line client

As soon as I implemented the client that you see above, I got feedback from people. There's a lesson in there somewhere. I got many good bug reports and could put in things that I'd forgotten.

See how easy it is to miss some cases? One could argue that this is a great weakness of this way of developing, but I would argue the opposite: this way of focusing on verbs and their results enhances the ability to think in terms of these situations. Also note how small and self-contained each commit is, even the fixes.

I'm really proud how the client turned out. Have a look at the 'final' client. I especially call your attention to the fact that we don't just list the available commands, we ask the Hanoi game for them. (Yay introspection!) Also, the print_board subroutine turned out really nice, for something that does formatted output. There's a little "cheating" that makes the command parser treat things like 'large disk' as one argument even though it's two words.

So

75 tests pass and support this hanoi game. I can't guarantee there are no remaining bugs to find, but I'm very confident it'll be easy and even a bit fun to integrate what we now have into the adventure game when the time comes.

Also, I imagine the subsequent posts will be a bit smaller in scope than these two first ones. Still, this was probably a good introduction to the style of programming we'll be using for the rest of the month. Commands, events, and exceptions. We'll see more advantages as we go along.

Now we can leave Hanoi behind us for a while, and... let the adventure begin!