From: Gavin Kistner Date: 2005-06-15T23:10:16+09:00 Subject: Re: [SOLUTION] Chess Variants (I) (#35) 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.