My Apprenticeship - Monday, August 9, 2004
This summer I'm revisiting my short apprenticeship at Object Mentor. I'll be posting commentary on all my posts from the summer of 2004 exactly 5 years later to the day.
XPAU is only a week away and most of the things that need to be shipped have been shipped. So Monday was the calm before the storm. Micah had a hundred or so neglected emails to answer and some odds-and-end work to do. David and Paul were pairing on the dot Net FitNesse project (making FitNesse work with C#). And I have been asked (dared) to write a chess program by Micah.
Before I get into the intricate parts of chess software, I should mention David's correction of my previous post. On the subject of acceptance testing vs. unit testing, he says that it's important to point out that the customer owns the ATs and developers own the UTs. A developer can have loads of passing UTs, but it's the ATs that the customer ultimately cares about.
On the subject of Chess, I'm finding that just getting the computer to moderate a game between two humans is a more interesting problem than I thought. The main culprits are the pawn and the king. Here's a list of king behavior the computer must enforce: kings can move one square in any direction (including diagonals), they cannot move into check (a square that is threatened by an opposing piece), they can castle ( king side castle: move two square to the right and have the king side rook move two square to the left. Queen side castle: king moves two squares to the left and the queen side rook ends up on its immediate right.) but only if the king hasn't moved yet, the rook hasn't moved yet, there are no pieces in the way, the king isn't castling out of check, or through a space that would be check, or into check. A little bit conditional, eh? I've got all that programming done except for the moving the rook part. In my design, pieces can tell the board object if a given move is a legal move and then the board can use that information to decide whether to move the piece. The problem is that if the king castles, I have to have some way of telling the rook to move. I could program that into the board object, but then some of the king's logic is stored in the board object. This is very bad programming form. Latter on if I, or somebody else, wants to create some sort of chess variant where the king moves differently this person won't think to look for part of the information about the king in the board object. So if they try to change how castling works, the board will keep trying to move the rook and that could cause all sorts of hard to find bugs. And I would have to go sit in the bad programmer corner. I'm thinking about creating a 'move' command that can be issued to the pieces which, in most cases, would do nothing but in the case of the king and the pawn (which can turn into another piece if it reaches the back rank and has this weird way of taking another pawn without landing on its square called 'en passan') would do some additional things. What bugs me about this is first that it's dummy command for most of the pieces and that it doesn't really do what it says. 'Move' won't make the pieces move (the board takes care of that), it'll just get them to perform the actions of certain special moves. I need a better name.
Also there's the problem of checkmate. Check isn't so bad: I wrote an algorithm that looks at all the opposing pieces to see if they threaten a given square. Now checkmate might just seem like looking at all the possible moves of the king and seeing if they also put him in check, but it's a little more tricky than that. There's two other ways to get out of check besides moving out of the way: you can take the piece that is threatening your king, or you may move one of your pieces in front of the king to protect him. Both of which are interesting programming challenges.
Yes the Acceptance Tests should be owned by the customer. However, the developer often writes the Acceptance Tests because while the customer technically owns them he or she doesn't have the technical ability to make sure the tests do what the customer wants. This can lead to trouble if, for example, the customer wants to make sure a user can be created, logged in, change their profile, and logout. A developer writing that test might be tempted to just go right at the database to create a user (which really doesn't click through the app the way a customer would) and while the test would appear to be testing sign-up that page could be busted without the test failing. Getting a good Acceptance Test framework up and running, maintaining it, and making sure it tests what you care about is a daunting task and takes way more effort (and hardware) than most clients realize. The out of the box solutions that record clicks are either inflexible (you can't do loops or have variables or assert what you want), proprietary, slow, or (usually) all three. And custom solutions take time and precious developer resources. In short, automated acceptance testing is hard and needs a fair amount of resources.