How to Build your own Chess Engine! (Overview)

Today I’m going to talk about one of my favourite hobbies: chess engines. There’s nothing like seeing an engine that you’ve made rise up in ELO until it’s finally able to beat you. It’s also, as I’ve found, incredibly addictive - you won’t be able to stop searching for small tweaks and optimisations to speed up and strengthen it! As some background, I made my first chess engine in Java around 3 years ago. It made … moves, to say the least, but it wasn’t significantly better than choosing random ones. To it’s credit, it never flouted the rules of chess, but I wanted something that could at least match my Elo (not a seemingly tall order, given that I was only around 1200 Lichess rating). Eventually I started from scratch in C and managed to achieve this, because instead of the naïve but intuitive methods used in the Java version, I used a faster, more modern framework to form the backbone of the engine. Around late 2019, I was rather inspired by the Rust community, so I translated it to idiomatic Rust and tacked on some improvements. Later on, I made a new engine in C, ported that to C++, then started focussing on incrementally upgrading it after it knocked the Rust version out of the park in a head-to-head. While optimising for speed is one thing, optimising for program size is another area that I’ve been interested, so predictably I am a huge fan of H. G. Mueller and Oscar Toledo.

There aren’t a huge number of dedicated resources for chess programming: there is the eponymous Chess Programming Wiki, which is quite hit-and-miss with its explanations, as well as a few questions on Chess Stack Exchange and Stack Overflow - but the best place to look is the old talkchess forums, where you could actually talk to pioneers of computer chess. Unfortunately its webpages seem to load far less frequently than they used to. That said, exploring and learning to debug properly for yourself (I made a series of ridiculously specific debugging tools) is a fantastic learning experience - and seeing your chess engine come to life for the first time is exhilarating.

Fair warning: I’m going to assume hereon that you’re familiar with the fundamental rules of chess (though I’ll definitely explain the more obscure tournament rules)$^1$. Also, rest assured that making a basic, miniproject chess engine is definitely doable, but in that case the methods in this blog series might be overkill. Here I’ll be explaining the tools and techniques in professional, state-of-the-art chess engines - so that you can make your own one!

But enough history - what actually goes into a chess engine? It goes without saying that the ultimate purpose of a chess engine is to input a position and get back the optimal move for the side to play, hopefully leading to checkmate. This takes some work: we need to be able to define what “position”, “move” and even “optimal” mean! To this end, there are four main components of a chess engine: the main data structure being Position, and the main algorithms being Move generation, Evaluation and Search. There’s also some bureaucratic stuff regarding standardisation with a chess playing interface, but everybody just puts it off until the last minute, so I’ll ignore it.

  • Position: This is the data structure that represents the state of the chess board at any given moment. It forms the basic framework of the chess engine that I alluded to earlier. Getting this stage right is integral to a solid engine, because no matter how optimised and clever the three functional stages are, they all ultimately have to interface with the Position and its bottlenecks. Anyway, here’s a riddle for you: what do chess boards and CPU registers have in common? Find out in this blog post.

  • Move generation: Given any chess position, the first stage to finding the best move is to know which moves we’re even allowed to play in the situation. Considering that 4-year olds can learn the rules of chess fairly simply, this doesn’t sound like a huge challenge. Oh, but it is. A lot of intuitive notions regarding pins, checks, castling, promotion are both tedious and annoyingly fiendish to implement without having your clock cycles biting on granite. And dare I mention en passant? This stage is actually the only purely objective routine, and so competition for fast move generation is commonplace. I personally found optimising this quite fun, but you don’t need to worry: move generation typically takes up only 5-10% of the total cycles, so it’s not a deal-breaker. Read more …

  • Evaluation: Now that we have a position, we need some way of determining how “good” some position is. Somewhat unintuitively, we have to determine this without thinking about what future positions will look like! In contrast to move generation, evaluation is wildly subjective, and you can employ whichever heuristics you want to find a good balance between accuracy and speed. In practice, this is the hardest stage to get right, amusingly because it’s the one aspect of the game that relies largely on human understanding of the game (and also because of its many free parameters)$^2$. Read more …

  • Search: You might already be familiar with this stage if you’ve ever made AI for other turn-based games! This stage has the largest amount of material available (at least for the fundamental features), much of it taught at CS courses at universities. Imagine the starting position - there are 24 legal moves you can make as White. If you play, say, e2-e4, then Black has 24 different ways to respond, and so on. It’s not difficult to imagine that the set of possible chess games can be represented as a tree with its root at the starting position. Now here’s where the concept of “looking ahead” comes in: to determine the best move, we shouldn’t just rely on the “static” evaluation at the root, we should probe deeper into the game tree to see what actually ends up happening to us. In other words, we search the game tree. This stage involves some simply beautiful algorithms to do this rapidly, and so it’s my favourite part of the engine. Read more …

$^1$ For the love of god, pinned pieces can still give check

$^2$ Actually, AlphaZero doesn’t do this - only traditional engines like Stockfish and Komodo do. But the fact that AlphaZero would have gotten its motherboard handed to it by Stockfish 13 shows that human intuition about chess is reasonably good

String Theory Again (gone mathematical)

I’ve primarily been exploring mathematical physics these days, following a breakthrough in my understanding of classifying spaces - this opened up a lot of doors to interesting stuff like K-theory, Chern-Weil theory, Yang-Mills instantons (BPST for now, the nLab pages are fantastic) and geometric quantisation, which looks amazing so far. I’m continuing with bosonic string theory, and topological string theory also struck me as elegant, so I picked that up too - on the mathematical side of things, I’ve been learning about (very obviously string-theory inspired) orbifolds, Kahler geometry, moduli spaces and Riemann surface classification. I also wanted a more comprehensive understanding of conformal field theory than merely a single chapter from a string theory book, so I’m learning it from a dedicated book.

String Theory!

My current course of action has inevitably led me to the grand culmination that is string theory. Rather than poring over lecture notes (not really that terse, introductory string theory lecture notes are surprisingly some of the most accessible ones I’ve seen), I opted for reviews and talks on the string theory and M-theory scene by some of the legends - Polchinski, Schwarz, Witten (actually I was reading his paper on 2+1D topological gravity - the Donoghue papers really piqued my interest in quantum gravity. Sooner or later I’ll get to the Jones polynomial one), and lots of semi-popular questions on SE. In terms of complete references, Tong, BBS and Polchinski will be my go-to’s, and I’ll try and find something for 11D SUGRA. I’m not picking a side here, I just want to appreciate the inherent mathematical beauty behind string theory. I’ll also be trying to learn cohomology properly as well (AT in general), if only to understand the BRST formalism and the Whitehead tower.

Additionally, I was looking at supersymmetric mutiplets and neutralino decay - just some simple BSM stuff, as well as revising $\text{U}(1)$ bundle gauge theory. I was also able to cut down on my CNN forward convolution routine by 80%, and the GPU acceleration was far beyond what I expected - I’m considering write a paper on the algorithm and its formulation!

Return of the King™

Hey everyone, I’m back after a short hiatus. I’ve decided to change the format a little: instead of documenting a complete learning journey (which, honestly, was quite strenuous), I’ll be giving a weekly (fortnightly?) summary of the things I’ve been working on/reading up on.

Since October, I’ve been doing a lot of physics, not much mathematics or programming at all - but this has changed recently after I started proper differential geometry: fibre bundles and connections (for mathematical gauge theory, etc.) - naturally, this lead to characteristic classes (Chern classes are useful for Calabi-Yau manifolds). I’m also looking at spontaneous symmetry breaking, cf. the Higgs mechanism and chiral symmetry breaking. 2+1D gravity also piqued my interest (particularly BTZ black holes) but I left it aside for the time being since I couldn’t find any resources that didn’t involve Chern-Simons theory (something that I should learn properly, now that my DG is also up to speed - not full-fledged cobordism TQFT though!). I also started a bit of string theory, from David Tong’s Lecture Notes (currently I am taking refuge in the Conformal Field Theory chapter!). Finally, I’m reading on the Petrov classification of Weyl tensors and, a blast from the past: Regge Theory! (since it’s from the early string theory days, and it also precedes the analyticity of the S-matrix). I was active quite a bit on Physics SE - I managed to gain around 1,000 reputation in a little over a month (mostly QFT and particle physics answers).