Skip to content
All projects

Personal project, 2025

Chess

A two-player chess game written in C++ with the full rules, from castling and en passant to checkmate and stalemate. Play it in the console or in the browser.

The chess game in a terminal, waiting for White to castle, next to the test suite passing
  • Every rule of chess: castling with all its conditions, en passant, promotion, checkmate and stalemate.
  • Each piece is its own class that only answers one question: can I move from here to there?
  • 50 tests that play real games, from fool's mate to a known 10-move stalemate.

What it is

A chess game for two players sharing one keyboard. You type a move as its origin and destination square, like e2e4, and the game checks it, plays it and redraws the board. White pieces are capital letters, black pieces are lowercase, and every square is drawn three characters wide so the board reads like a real one.

I built the first version on my own in 2025. In 2026 I came back to it with an AI pair programmer: we fixed the rule bugs, rebuilt the engine around a plain 8x8 board and added a test suite.

How it’s built

One class per piece. Pawn, Knight, Bishop, Rook, Queen and King all derive from Piece. A piece doesn’t know where it stands. It only answers whether it could go from one square to another on a given board, so its rules are a few lines and never change anything.

One helper for sliding pieces. Rooks, bishops and queens share a single path_clear check that walks the squares between origin and destination. The difference between them is just which directions they’re allowed.

The board is a plain value. The position is an 8x8 grid of piece pointers. Copying it is cheap, and that shapes the whole design.

Castling: White types e1g1, and the king and rook land on g1 and f1

Engineering decisions

Try the move on a copy instead of undoing it. A move is legal if the piece is allowed to make it and your own king isn’t attacked afterwards. The game plays the move on a copy of the grid and asks that one question. The first version moved pieces and then tried to put everything back, and most of its bugs lived in that undo code.

One question drives the rules. square_attacked asks whether any enemy piece could reach a square. Check, checkmate, stalemate and castling are all built on it: a king may not castle out of, through or into an attacked square.

Special moves stay in the board. Castling and en passant depend on history (has this rook moved? did that pawn just jump two squares?), so the board tracks that state and the pieces stay simple.

En passant: White’s e5 pawn captures on d6 and Black’s d5 pawn disappears

Clear messages for illegal moves. A pinned piece, a castle through check or a mistyped move each get their own explanation, and the turn doesn’t pass until a legal move is played.

In the browser

The same C++ engine also runs on the web. Emscripten compiles it to a 21 KB WebAssembly file, and a small page draws the board and forwards clicks. The page doesn’t know any chess: it asks the engine for the legal moves of the side to play, highlights them when you pick a piece, and shows the engine’s own messages, like “Black king would be in check!”. You can still type moves the console way.

Testing

The tests don’t poke at internals. Each one starts a fresh board and plays real moves through the same entry point the game uses, then checks the outcome:

  • fool’s mate and scholar’s mate end in checkmate, and Sam Loyd’s 10-move stalemate ends in a draw
  • a pinned pawn can’t move, and a king can’t step into an attacked square
  • castling works on both sides, and is refused after the king or rook has moved, through check, or with a piece in the way
  • en passant works for both colors, but only on the very next move
  • promotion to any piece, and bad input is rejected without losing your turn

Illegal moves explained on the left, and fool’s mate ending the game on the right