If you’re looking for interesting software projects, Sebastian Lague has some great content. One of which is a series on building a chess engine. I’m terrible at chess, but I found some of the concepts technically interesting, so I dove in.
Building an implementation of the game of chess is somewhat straightforward. The data structures are pretty obvious, and the rules are extremely well defined. With a bit of time and effort, you can get the basic game running. In order to ensure you’ve gotten all the rules implemented correctly, it’s quite convenient to use an existing chess engine to validate legal moves by counting them up to a certain depth for a given position. I found quite a few bugs in my implementation this way, mostly around castling.
Slap a terminal UI on top and you’ve got something pretty good! Although, you’re going to need a bot to play against…
The next step is to implement the engine, and where this get obviously really hard. Many chess engines are implemented by searching the game tree and using a heuristic to evaluate the position, and this is the approach I took.
The first thing to implement is the search algorithm. Remember when I said that it’s convenient to count all legal moves to a certain depth to validate our implementation? Well, that’s the starting point for the search algorithm. However, the trick to making search fast is to avoid searching the entire tree. There are a few techniques to do this, and I won’t go into each, but these are the ones I implemented:
So far we’ve focused on reducing our search space, but there’s another way to speed up search: improve search performance. There are a couple of techniques to do this that I implemented, and again I won’t go into too much detail for each:
Now we’ve got reasonably fast search, but that’s useless if we’re no good at evaluating how good a position is.
The next step is to implement the evaluation function. This is a function that takes a position and returns a score for that position. The score is a measure of how good the position is for the player to move. The higher the score, the better the position is for the player to move. The lower the score, the worse the position is for the player to move. The evaluation function is a huge topic, and there are many different ways to implement it. I won’t go into too much detail, but here are a few of the techniques I implemented:
I didn’t spend too much time on this to be honest, and there is a lot of room for improvement.
There’s a couple final things we throw in to the final implementation:
This engine is very very simple in comparison to proper chess engines, but it’s good enough to beat me easily. I end up re-implementing this engine in Rust, and make some further improvements to the search and evaluation functions.