Chess TDD 14: Figuring Out Blocking Pieces
In this installment, I’m looking to start defining the concept of “blocking” on a chess board. So far, I’ve been defining piece moves in a vacuum — put a rook down in (1, 1) and the rook can go anywhere in column 1 or row 1. But, what about when there’s a pawn at (1, 2)? That’s what’s on the docket in this episode.
Here’s what I accomplish in this clip:
- Started implementing the concept of pieces “blocking” one another.
- Expanded the blocking logic to include horizontal and vertical blocking in both positive and negative directions.
- Realized I’d been wrong about how Enumerable.Range() worked for a good long time because I had always used it for 0 indexed number generation, which masked my wrongness.
And here are some lessons to take away:
- If you’re anything like me, sometimes you’ll thrash a bit between micro design decisions. I introduced the concept of BoardCoordinate to avoid passing (int x, int y) to every method under the sun, but then I started to get annoyed by the ceremony of declaring new BoardCoordinate(x, y) everywhere. Result was (for now) to implemented BoardCoordinate.For(x, y) which struck me as at least slightly more readable and elegant. Point is, you probably won’t hit your most simple/elegant design on your first, second, or even third crack. Keep at it, don’t be afraid to experiment, and solicit opinions and advice. (Speaking of which, anyone with a good idea for this, please chime in!)
- “Do the simplest thing you can to make the test pass” is not just a function of what you do in the production code but also of what test you’ve written. In other words, make sure you don’t write an overly ambitious test like “test_that_all_end_user_requirements_are_satisfied” or the simplest thing you can do to make it pass will be really complicated.
- TDD is the ultimate in “fail fast.” I wasted about a minute of my time (twice) and a minute of yours (once) because I couldn’t figure out that I was calling Enumerable.Range(int, int) with the wrong second parameter. But, seeing NCrunch’s dots turn red told me something was wrong very, very quickly while what I did was still fresh in my mind. I didn’t know I was misremembering the method signature, but I did know that something about Enumerable.Range() in there was breaking things. Without TDD, I’d have gone merrily on writing code for minutes or even hours until I fired up the whole application and wondered why the blocked rook could randomly move to one spot on the board in front of the opponent’s pawns or something. I’d have wasted a lot more of both of our time trying to hunt down the problem then, when I had no clue which line of code was the offender.
- There are times when the code smell of duplication or duplicate effort appears, but the solution isn’t immediately obvious (such as the case in this video where I was doing similar things for horizontal and vertical). It’s my experience that you can find yourself in a blind alley when you try to abstract this to a common method where it becomes a snarl of conditionals or very dense, opaque mathematical logic. Eliminating duplication is extremely important, but beware of times when you have “duplication elimination fools’ gold” and the gains in readability/maintainability actually go into the negative. Be sure that your abstraction is actually making things simpler.
- I didn’t like a number of things about this code when the clock started getting pas the 20 minutes mark and I felt the pressure not to make this a horribly long episode. It might not be the same reason, but there are going to be times when you’re forced to get up and leave for the time being or for the day when you’re not thrilled with the code. It’s okay — you’ll have another crack at it tomorrow with a fresh mind, and the TDD will help you pick right up where you left off.