My solution can be found at http://phrogz.net/RubyLibs/GKChess.rb

My solution does not allow castling or en-passant. (Castling would be  
far easier if you didn't have to determine if any other piece  
challenged the intermediary space :p En-passant is annoying because  
of the turn-based restriction.) It also does not check for checkmate.

Things I'm proud of:
* Subclassing Piece for each type to delegate the possible moves  
calculation.

* Some of the DRY stuff in calculating possible moves along various  
axes for everyone but the Pawn

* Some nice robustness and messages when you try to pick an invalid  
piece, or send a piece somewhere it can't go. (After you pick the  
piece to move, if you give it an invalid destination it tells you  
what moves are legal.)

* Subclassing RuntimeError for the first time only and using it to my  
advantage. (OutOfBoundsError and IllegalMoveError).

I'm actually not sure if I should be proud of the last one...is it  
'good' to run code you know will throw an error and then catch that  
as a way of detecting bounds, versus calculating the bounds properly?

When calculating the moves that a rook could do, for example, I moved  
along a particular axis until I found another piece, adding each  
square to the possible moves. However, if no pieces existed between  
me and the edge of the board, I let the algorithm charge off the edge  
of the board, waiting for the Move constructor to throw an  
OutOfBoundsError as another way to let me know that it was time to stop.




Things I'm not so proud of:
* Sometimes I use 'a2' to access information, sometimes ['a','2'];  
consequently a lot of my methods have code like:
   def foo( col, row=nil )
       col, row = (col.to_s+row.to_s).split('')
   so that I can pass the information around as separate arguments,  
one string, or an array. Tricky, but hacky.

* The pieces know what they are allowed to do on the board...but  
don't know anything about check or checkmate. That's up to the Board.  
Consequently, pieces think they can make a move that would result in  
self-check, and list those choices among the options. The error  
message when you try to make a move that would put your own king in  
check is not helpful.

* I use #deep_clone with the Marshal hack to set up a scenario to  
detect check without altering the current board. This feels  
wrong...the board should be able to try out a move and then undo it  
without having to clone itself. (I was concerned about putting pieces  
back on the board afterwards.) Further, when I make the clone, I  
can't the real #move method to try out the change, because that  
method has all sorts of warnings that I'd have to hide. Ugly.


The trickiest part for me (that I ended up not solving) is figuring  
out where the logic of the game goes. Shouldn't each piece know what  
it can do? But then it needs to know about the entire board. And (for  
en-passant) the recent history of moves for each piece. Ugly.


Thanks for a challenging puzzle. Having refactored some pieces twice,  
it was a very useful exercise in thinking about roles and  
responsibilities, and my own need to plan a little more before coding.