Implementing a Chess engine from scratch | by Micaël Paquier | Towards Data Science

As a humble Chess amateur, I gave myself this challenge: to develop a simple, good-looking Chess game with AI that can beat me, without Machine-Learning. This article is about my journey to achieve it and is composed of 4 parts: rules, computation, strategy, and playing.
And just to make things clear, I also decided not to read theoretical or algorithmic explanations on Chess engines, I wanted to build my own algorithm, based on my common sense and personal experience.
I named it Bobby, as a tribute to Robert “Bobby” James Fischer, who has been World Chess Champion and one of my heroes as a young player.
Chess is played by two opponents, one color for each, white and black pieces. Then for each side, there are the following pieces:
♔ — A king
♕ — A queen
♖ — Two rooks
♗— Two bishops
♘ — Two knights
♙ — Eight pawns
At the beginning of the game, the initial position is always the same, strictly defined. A range of pawns in front of the other pieces, the royal couple in the center, then symmetrically come the bishops, knights, and finally rooks.
The board is a squared grid of 8x8 cases (i.e. two-dimensional array), alternating dark and light backgrounds. Now is the time for a visual representation of that board.
When I thought about drawing the pieces, I first started considering free graphic resources. But that would force me to manage pictures, which was not desirable for quick development. Fortunately, I discovered that each symbol is part of the Unicode: a basic text label is thus sufficient to draw a piece! Perfect, I was already able to print the board in the console logs to visualize it:
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟     ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
Funny, but not as good-looking as what I had in mind, so I decided to create a basic GUI: the board is a single frame, with a grid layout, driven by a MVC architecture.
I started with the definition of the dark and light backgrounds, where each square is a label containing either an empty value or the Unicode symbol of a piece.
If you are familiar with Chess you probably know that the board grid has a coordinate system made of letters (horizontal axis, or files) and digits (vertical axis, ranks) which is used for game notation, i.e. to script the moves. I finally added them on the sides of my board, and here is the result, a simple 10x10 grid:
Let’s continue. What are the rules? What actions are allowed? There are basically 4 types of moves, that may be combined according to the piece:
Straight (used for rooks, queen and king)Diagonal (bishops, queen, king)«L» (knights)Pawns moves
Straight (used for rooks, queen and king)
Diagonal (bishops, queen, king)
«L» (knights)
Pawns moves
Note that special moves en passant, pawn promotion, and castling are deliberately ignored for the sake of simplicity but have been implemented later. The exhaustive description of moves can be found on Wikipedia.
Some additional constraints to implement a move: a piece cannot jump over another piece, except for knights, and when a piece reaches an enemy piece, it may capture it and take its place on the board.
I also designed an optional colored border around squares to highlight moves:
Red — currently selected piece
Blue — possible destination of the selected piece
Green — last move played by the opponent
Now that we can move pieces, we must take into account the rules of the game: there are things that can be done, as well as things that must be done (typically, escaping check).
It is out of scope to explain every single rule here but I tried to implement most of the constraints, among others:
Players’ turns;
Detection of whether a king is in check, and when there is no way to escape from this, determining that the king is checkmate (game lost);
Both kings always being spaced a square apart;
Now how does the game finish? There are two scenarios: either a player wins, or neither win (draw).
As already mentioned, you lose when your king is in check and that there is no possible move to escape being in check.
But you draw if:
There is no possible move but your king is not in check (stalemate); or
Both players repeat the 3 same moves in a row; or
Both kings are alone on the board (or with insufficient material to win)
I also implemented another rule to avoid never-ending games: if there has been neither pawn move, nor a capture within the last 50 moves, the game is a draw. Note that this rule is largely accepted by the Chess community.
With the above rules, two humans can play a game, but we want to play against the computer, so let’s get to the heart of the matter.
What is the lowest intelligence possible? Choosing an allowed move without any strategy. This was the first level of “intelligence” I implemented as a bot: compute all possible moves and randomly select one of them. OK, that was fun, but let’s be serious now.
How do does a human determine which move should be played? He/she selects the move that will give an advantage, which can be translated by an improvement of the situation. We thus need a function to evaluate the current position, try a move and reevaluate it afterwards.
Basically, and according to the game rules, when a move leads to checkmating, then stop searching because that is the best move. On the other hand, I implemented a penalty score if the move leads to a draw because I wanted to make the AI as aggressive as possible.
Then, how to compute the score? Generally, the easiest strategy for beginners is to capture pieces. The implementation is to give points to every type of piece, so you can compute your current score by summing the points of all your pieces, and do the same with opponent’s pieces. It is then easy to estimate how much you dominate (or not) your opponent by comparing both scores.
But no need to be a Chess grandmaster to understand that it is not as simple as this. After your move, it will be your opponent’s turn. Say you captured a pawn with your queen and then your opponent captures your queen, you have lost the most important piece, so in the end, this was a terrible move.
To avoid this, the move selection function has to be recursive so that after having tried a move, the function is called again but for the opponent: the idea is to guess what the answer should be. And evaluate the resulting situation.
As this is a recursive function, this can be applied a few times, allowing us to try to imagine the probable situation in 2, 3, or more turns. But the cost here is exponential since we compute all possible moves, and in practice, I had to bound it to a depth of 3 moves, otherwise it was taking too much time to play.
Note: I will later learn that this kind of computation is part of the Minimax algorithms family — try to maximize your minimal gain, given your opponent’s responses.
Among other things, predicting 1, 2, or 3 moves allows the AI to:
Play a game-winning move leading to an immediate checkmate (depth of 1)
Play a move preventing a loss right after (depth of 2)
And this is exactly what I used as testing: setup a game that can lead to checkmate in 1 or 2 moves and let it find the best move to win and, respectively, avoid the defeat.
The logic above works finely in many situations but is inefficient to eventually win by checkmating the opponent’s king. Some additional intelligence is required in the evaluation function.
I often asked myself what were the tips I was given when I started playing, i.e. what made a move good or bad.
As simple as it seems, one of the keys to winning is to control the center of the board. Placing your pieces in the center, or at least making them cover the center, helps to develop the army, to attack as well as to defend.
I implemented this with the concept of a heat map:
The 4 most centered squares are worth 2 pts each;
The 12 squares around them are worth 1 pt;
All remaining squares are worth 0 pt.
Then, one has to compute the move of every piece, and according to the location they can reach, sum up all points above to get a final score.
I noticed that my AI was able to play quite well at the beginning and in the middle of the game but faced difficulties closing games because it had no heuristic to play moves that lead to attacking the opponent’s king.
I decided to reuse the idea above to tackle this problem, that is to say, define a heat map focused on the opposing king:
3 pts when hitting the king, because it is very good to have the king in check;
2 pts around the king, because you prevent him from escaping;
1 pt around the king area, because it eventually leads to restricting his moves;
0 for all remaining squares.
Like in many games or sports, there is a theoretical part that must not be neglected. The strategy based on the heuristics above is good but may not be sufficient for a good start, and an imperfect start in Chess may quickly lead to losing the battle. In order to avoid an early poor move, players learn openings by heart, and this is what I implemented. Of course, there are libraries with tons of openings, but as my objective was to make an AI comparable to me, I chose to let it know only 15 major openings, with 2–3 moves each.
I used a tree structure to classify the openings: a move leads to a node and a leaf is the last known move for a given opening.
When the AI has to choose a path from a given node, it randomly selects one. I later noticed how appreciable this random selection was, because it introduced variants in each game, avoiding boring predictable paths. When the tree does not know the move that was just played, the AI falls back to its default computation.
Some hints are given to beginners to avoid big mistakes in the early game. For example, castling (a special move consisting in moving both the king and a rook at once) is highly recommended, since it shelters the king whilst activates the rook close to the center. I thus implemented bonus and penalties according to game history.
A bonus is given when:
Castling is done
A penalty is given when:
The king has moved before castling, since the right to castling is lost
Either the king, the queen or a rook (castling excepted) is moved during the opening (5 first moves)
The same piece (pawns excepted) is moved more than once during the opening
Although I played many times during the development phase, I finally decided to create a short tournament, opposing bots and their creator:
A stupid bot, randomly playing
Bots respectively computing moves with a depth of 1, 2 and 3
Another bot computing moves with a depth of 3, but also experienced of some openings
Me, an amateur Chess player
All participants have been facing each other, playing twice with White, and twice with Black in every match. Here is the final scoreboard:
Not sure if it is good or bad news to remain stronger than the AI that was developed but for sure I won most of the games against the AI. I however did drop a point against each of the three best bots, probably due to a loss of concentration and/or playing too fast, which remains a kind of satisfaction since it proves that the AI can beat me.
Unsurprisingly, bots with the highest computation level are runners-up, and the random one finished last. A fun fact is that the latter was able to earn 0.5 pts by making a draw against a stronger bot. As for both bots with level 3, they almost defeated all other bots, and their head-to-head was concluded on a 2–2 score, which is quite logical too. Finally, one cannot say that mastering theoretical openings gives a significant advantage, at least not against other bots playing the same strategy, but I am confident it does make a difference when facing humans.
Now that you know how I built my own Chess intelligence, you might wish to try it. Of course, the code is public and can be found on my GitHub repository.
Running it locally on your computer gives the best experience but a web version is also provided, with some limitations though: only one session (i.e. one player) at a time, because of limited resources, and some graphical latency when moving pieces. But it is free and online:
www.bobby-chess.com
Note: if you are interested in reading how I exposed a Java Swing desktop app through a website, I have written another article dedicated to this technical aspect:
codeburst.io
I am aware that my Chess engine has room for improvement, and is extremely far from the best computer engines. But as I said in the introduction, I wanted to program it with my own insights. This is why I refused to use theoretical material to make it stronger, but I do recommend having a look at Chess programming if you are interested in reading this topic.
Moreover, I am fully convinced that nothing is better than Machine Learning for such games, or maybe a subtle combination of Machine Learning and traditional computation engines. AlphaGo was the first ML-based engine to beat the World Champion at Go. Then in 2017 AlphaZero was introduced and largely defeated the best traditional Chess engine, namely StockFish.
But as my father was kindly testing my Chess engine, he made an interesting statement:
State-of-the-art Chess engines are boring.
And this is particularly true for two reasons: first, they systematically beat us, amateur players. Second, some of them may always play the same moves in a given situation, so they actually can be predictable.
Bobby engine tries to escape from this, thanks to the randomness in the openings, as well as when computing equivalent moves.
And according to the reasonable limitation to 3-moves depth, an amateur Chess player has a chance to win with a bit of creativity when imagining traps.
One of the weaknesses of my engine is its poor development at the beginning of the game, right after the opening: it tries to attack as soon as possible, like a newbie, and if the opponent resists with a correct defense, this may quickly turn into a strong disadvantage for the AI, since an important piece can be captured early as a result of this greedy behavior.
My initial challenge was to build my own Chess game, with a nice UI and the capability to defeat me, and I think it is fair to say that this goal was achieved.
I am pretty pleased with the final result although I believe many things could be improved in the future. I would particularly be interested in developing a Deep Learning AI and having them battle it out.
Bobby on GitHub — code repository
https://www.bobby-chess.com/bobby/ — play online to my Chess engine
Enhancing a Java Swing App to a clean, elegant Web App without changing the code — another post on the technical side of my Chess engine
Chess Programming Wiki — theory and algorithms explained for Chess engines
AlphaZero — a machine-learning Chess engine
Chess symbols in Unicode — chars for Chess pieces
Chess / Movement — Wikipedia page about Chess