Code kata 5: SOLID chess

The kata

Let’s get this clear: we’re not going to write a chess-playing AI in this kata. The idea is a bit more humble: to write a program that can work out valid moves for a set of chess pieces.

The purpose of this kata is to practise writing SOLID code. Use TDD, but concentrate on getting the code design right. So, refactor relentlessly at each step, using the SOLID principles as a way of examining your code.

I’ve laid out the steps you might take as follows.

  1. Place a single white bishop anywhere on a chess board
  2. Determine the set of squares to which the bishop can move (e.g. bishop, b6 -> {a5,c7,d8,a7,c5,d4,e3,f2,g1})
  3. Add a white castle to the board and determine the squares to which it can move. Include a test where the bishop obstructs the castle’s movement
  4. Now add a black bishop to the board, and determine the squares to which the white castle can move.  Include a test where the castle is able to take the black bishop
  5. Now add a white queen and determine the squares to which she can move
  6. Do the same for a white knight
  7. Now add a white pawn and allow it to move forward one square (for now, ignore the two-square advance for a pawn’s first move). Include a test to ensure it is blocked by a black piece immediately in front of it
  8. Allow the white pawn to capture a black piece by moving diagonally forward one square
  9. Now allow a pawn to move either one or two spaces forward for its first move
  10. Now allow a pawn to capture a piece en passant
  11. Add a king and the ability to castle

Don’t aim to finish; there is almost certainly more here than can be done in the time. On the other hand, don’t spend too much the first two steps; try to get onto step 3 quickly, as this is the point where the kata becomes more interesting.

How did it go?

What a fun kata 🙂

No surprise that none of the pairs completed it, although one got as far as capturing en passant. And everyone got to the heart of the problem: figuring out what responsibilities there were and where they all belonged. Some of the pairs thought this out up front while others tried to figure it out as they went. Not all were equally successful…

One pair ended up with a Board class that knew pretty much everything, which they agreed was less than completely SOLID. We took a look at the code and asked questions such as, should Piece be a base class or an interface? Where should the movement rules go? Is a queen basically a rook and a bishop, or is it a Piece that can go wherever a rook or a bishop could go?

Other pairs had looked into these kind of questions. One had a Board that provided a general mechanism for managing the interactions between the Pieces, and Pieces that had their own movement rules and capturing rules. The Board had the logic that detected where Pieces were blocked by another. While the result wasn’t ideal, we all found it very nice to read and it had a clear sense of where the boundaries between different classes lay.

Another solution had a Board with no logic at all. All the moves were calculated by a separate class, while each Piece knew its position and how it could move. This pair had also had the idea of moving the movement rules out of Piece and into their own classes, so that Piece would only know its position, but they didn’t get as far as implementing it.

Another pair had done this, though. They took a very a clean approach by separating the basic movement from the number of times a Piece could do that movement. So for example, a knight could hop by moving two squares forward and one to the side and it could do this just once, while a Bishop could move one square forward and one to the side, and do this repeatedly.

Most pairs had used TDD and reckoned that this had helped them write SOLID code as you run into problems pretty quickly and find it hard to test the bits you want if your code isn’t SOLID.

The feedback from the group was that SOLID was too big to take on all at once, and that they’d like to try a kata where they consider just one of the SOLID principles. I think we’ll give it a go.