Coding Chess with TDD
Tags: testing, javascript, tdd
Published: 2021-02-13 16:57:13

TDD is a pretty simple idea, you write your tests before you write any code and you write just enough code to make that failing test pass.

There are three Laws of TDD

You must write a failing test before you write any production code
You must not write more of a test than is sufficient to fail or fail to compile.
You must not write more production code than is sufficient to make the currently failing test pass.
Recently, I picked up the book Clean Coder by Uncle Bob – among many brilliant points made in the book I was immediately intrigued by the enthusiasm placed on TDD.

# Using TDD
Lately I've been playing quite a lot of [chess](https://www.chess.com/), however, I'm pretty shite. I thought, what better way to learn the game than to code it? And whilst I'm at it I'll give that TDD lark a good stab.

All the code I wrote is open-source and can be found on my [GitHub](https://github.com/LukeGarrigan/p5js-chess).

The framework I'm using for writing tests is [Jest](https://jestjs.io/) and the canvas library is [p5.js](https://p5js.org/).

## Creating the tiles
So, what do I need? I need a board which has tiles and there needs to be 8 tiles per row, let's create a failing test. 

*Note: the cycle time was a bit smaller than I'm showing in these examples, I would write just enough code to produce a failing test and then write just enough production code to make that test pass &mdash; so in the example below I'd have created the board class straight after writing `new Board()`.*
![Image of first failing test](https://i.imgur.com/unRpKoN.png)
Now we've got ourselves a failing test let's write the code to get that test to pass.

![https://i.imgur.com/oFSVHKD.png](https://i.imgur.com/oFSVHKD.png)

Brilliant, the test is now passing and we've got ourselves a two dimensional array that represents the chess board!

**Displaying the board**
I should note that I didn't write any tests for actually rendering the board as p5.js does that heavy lifting for me &mdash; which also explains why the coverage is not quite 100%.

![](https://i.imgur.com/DWlxZ0e.png =400x400)

## Creating Pieces
The next logical step was to get some pieces on the board. Let's start with pawns.

Firstly let's start with writing a failing test to check that the Pawn exists on the board at the start of the game:
![https://i.imgur.com/alresn7.png](https://i.imgur.com/alresn7.png)

Let's now write just enough code to get this test to pass.
![https://i.imgur.com/NoYzYXa.png](https://i.imgur.com/NoYzYXa.png)
Brilliant, I then repeated the process for the white pawn.

And we have ourselves some pawns on the board! 
![Chess board with pawns](https://i.imgur.com/KH9w8Gy.png =400x400)

The next logical step is to find possible moves for the pawn, but before we do that I need some way to distinguish between the black and the white pieces. So let's right a test to ensure that the pawns at the bottom are white and the pawns at the top are black.

![https://i.imgur.com/llIn9xZ.png](https://i.imgur.com/llIn9xZ.png)
So in this test I've introduced a new constant for the colour of the pieces. Next I need to write just enough code to make this pass, so the simplest path here is to add the new colour property to the `Pawn` class, and doing that will make the test pass. Now that I've got this test in place I can refactor, I *know* that every piece is going to require a colour, so it would make sense &mdash; rather than repeating this code in each chess piece (Bishop, King, Queen, Rook, Knight) &mdash; to create a base class called `Piece` that deals with this.

![https://i.imgur.com/DpqDMZX.png](https://i.imgur.com/DpqDMZX.png)
And I simply know this works by just re-running my test suite, TDD gives you the power to refactor with confidence!

### Finding possible moves
So, in chess what can a Pawn do?
1. It can move forward 1 square
2. Move diagonally capturing an enemy piece
3. Move two squares if it's the first move

And a couple moves I'll ignore for now:
- Promotion - when you reach the end of the board
- Can perform an [En passant](https://en.wikipedia.org/wiki/En_passant)  which is a move you make out of principle to show your opponent that, yes, I know what En passant is.

Let's write our first test to check when a Pawn has just one move:
![Pawn has one move code](https://i.imgur.com/cqFq7mm.png)
So I've added a couple things here, a new flag which denotes whether a Pawn has moved or not and a new method on `Pawn` class which should find the legal moves for us, let's write the production code to make this test pass:

![Just enough production code to make the Pawn test pass](https://i.imgur.com/44Pwb7x.png)
So here we're just checking to see if a piece exists in front of the pawn and if it does that means we can't move there which also means we can't move two spaces ahead if it were our first move!

One might think I was a little naughty here as I'd written too much production code just to get the test passing, and you'd be right. This code is enough to get the following tests to pass too.

![should find two possible moves if the white pawn hasnt moved](https://i.imgur.com/0zkLquS.png)

![Should not find a legal move if there is a piece in front of white](https://i.imgur.com/KfkQZpC.png)
This is one of the key lessons I've learnt from practicing TDD, **don't get ahead of yourself** &mdash; write **just enough** code to get the test to pass and nothing more.

A good image and explanation from codecademy.com in their blog [Red, Green, Refactor](https://www.codecademy.com/articles/tdd-red-green-refactor)

>-   Red — think about  _what_  you want to develop
>-   Green — think about  _how_  to make your tests pass
>-   Refactor — think about  _how_  to improve your existing implementation

![Image of Red, green, refactor](https://content.codecademy.com/programs/tdd-js/articles/red-green-refactor-tdd.png)

If you get ahead of yourself like I did then you miss the "Refactor" step. *Yes*, you can still refactor after you've written all the production code, but refactoring just 3 lines rather than 30 is surely a simpler operation, TDD enforces this.

Now that we've covered a pawn moving forward and a pawn moving two squares on its intitial move, let's add a test to cover attacking. 

![Should show 3 legal moves if enemy pawn diagonal left to current pawn that hasnt moved yet](https://i.imgur.com/ReaFmRT.png)
Let's write the production code to return the diagonal left attacking move:

![diagonal left attacking move](https://i.imgur.com/Em2lRxK.png)
Brilliant, this test is passing but what happens if our pawn is on the very left of the board? I'm pretty sure the code would error because it'll try to get the value from `tiles[-1][y]`, let's write a test to check this:
![Should show 2 legal if a side pawn](https://i.imgur.com/o87elpF.png)

Just as I expected:
`TypeError: Cannot read property '5' of undefined`

Let's remedy this by adding a check to see if the current Pawn is at the end of the board:
![Added an if check to make sure it is not at the edge of the board](https://i.imgur.com/2asYjig.png)
Brilliant, now our test passes! I then repeat the previous steps for diagonal right, you can imagine what that looked like.

Now we have pawns that can move, I added a little visual code so that when you select a pawn it displays the possible moves.
![Shows the possible moves in green](https://i.imgur.com/UWXBT6M.png =400x400)


# Rinse and repeat
I then repeated the steps that I took for the Pawn in finding its possible moves for the rook:
![Rooks added](https://www.evernote.com/shard/s712/res/4064c6f6-c95f-9c9c-3c57-cbb88f972f94 =400x400)
And then the Bishops:
![Bishops added](https://i.imgur.com/Zh6SuEL.png =400x400)

And the Knights, Kings and Queens:
![Knights, King and Queen added](https://i.imgur.com/4g9NX7A.png =400x400)

And prettied them up a little, who knew Unicode had chess pieces? https://www.wikiwand.com/en/Chess_symbols_in_Unicode

![Unicode chess pieces](https://i.imgur.com/F9aSqL8.png =400x400)

# Finally
I continued the process of writing the tests before writing any code and by the end of it I've got a functioning Chess game, yes there are definitely some minor things I've likely missed, but this was just merely an exercise to put TDD to practice. The one takeaway I've learnt from this &mdash; and isn't mentioned enough &mdash; is that TDD is **fun**, and I mean a lot of fun. There's nothing more gratifying than seeing your broken test flick to green. The instant release of endorphins makes TDD is almost addictive. Having a reliable suite of tests that runs in under a second gives you certainty when refactoring or adding new code, it's a massive safety net. And because you've written your tests before any production code you can be confident the holes are minimal, and if any, most certainly would have been there if you weren't using TDD.

I hope this blog inspires you to give TDD a go, I know I'm going to be using it by default in the future, as I said, It's bloody fun.

Thank you, if you like my rambling check out my personal blogging site at https://codeheir.com/