Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Rust Chess — Full-Stack Chess Application in Rust

Rust Chess — Full-Stack Chess Application in Rust

November 19, 2023

Overview​

Rust Chess is a complete chess application where every line of core logic is written in Rust. The project is organised as a monorepo with two sub-repositories: one for the chess engine + frontend and one for the multiplayer backend.

The chess engine compiles to WebAssembly via wasm-pack, then links into a Gatsby.js / React frontend through npm. This gives the browser native-speed move generation, validation, check/checkmate detection, castling, en passant, and pawn promotion — with zero JavaScript handling game state.

The backend is an Actix-web server built on WebSocket actors, providing real-time communication for online multiplayer, chat rooms, and session management. A text-based command protocol over WebSockets lets clients create games, join lobbies, and relay moves between opponents.


Key Features​

  • Full Rust chess engine compiles to WASM: Move generation, validation, check/checkmate detection, en passant, castling, and pawn promotion — all running at near-native speed in the browser
  • Strategy pattern for piece moves: Each piece type implements a PieceMoveStrategy trait, making the engine extensible and testable
  • Algebraic notation parser and writer: MoveParser / MoveReader / MoveWriter translate between standard chess notation and internal move results
  • Online multiplayer via WebSocket: Real-time game creation, joining, move relay, and opponent notification
  • Chat rooms and lobby system: Multiple themed rooms (mountain, ocean, sky, space), lobby for game discovery, and in-game chat
  • Session management with heartbeat: WsSession actors maintain client connections with ping/pong heartbeats and automatic timeout cleanup
  • Local single-player support: Full board playable offline against a local opponent
  • Self-info and user profiles: Sessions can query their own room/game state and list available games

Architecture​

A two-repository architecture splitting the WASM frontend and the WebSocket backend:

RepositoryRole
chess-frontendRust chess engine (WASM) + Gatsby.js / React UI
chess-apiActix-web WebSocket server + game/room/session management

The chess engine lives inside the frontend repository: Rust source in chess-frontend/src/ compiles to pkg/ via wasm-pack. The Gatsby app in chess-frontend/www/ links the WASM package via npm and calls into Rust bindings for all game logic.

The API server manages no game state of its own — it acts purely as a relay between connected clients. Each WsSession actor handles text commands (/new-game, /join-game, /game-move, etc.) and delegates to ChatServer and GameManager for room and game orchestration.

┌──────────────────────────────────────────┐
│ Browser │
│ Gatsby.js / React UI │
│ │ │
│ ▼ │
│ chess-lib (WASM) — full chess engine │
│ Board → MoveValidator → PieceStrategy │
└──────────────┬───────────────────────────┘
│ WebSocket
▼
┌──────────────────────────────────────────┐
│ chess-api (Actix-web) │
│ │
│ WsSession actors │
│ │ │
│ ▼ │
│ ChatServer → rooms, sessions, broadcast │
│ GameManager → SessionGame state │
└──────────────────────────────────────────┘

Chess Engine Detail​

The chess engine is the heart of the frontend. Written in pure Rust and exposed to JavaScript via wasm-bindgen, it handles every aspect of the game:

Board (board.rs)​

An 8×8 grid of Tile structs, each holding an optional Piece. The board tracks:

  • Last en passant coordinate — set when a pawn moves two squares, cleared after the next move if not captured
  • King castling state — a KingCastleBoardState struct tracking whether each side's king and rooks have moved
  • Tile highlighting — visual move suggestions computed by the engine and rendered by React

The board implements move_piece(from, to) as the main entry point, which delegates to handle_move_piece() — a complex method handling all move types, checking validity, executing captures, and returning a MoveResult.

Pieces (pieces/)​

Each piece type gets its own strategy module implementing the PieceMoveStrategy trait:

  • PawnMoveStrategy — forward moves, double-push from start rank, diagonal captures, en passant detection
  • RookMoveStrategy — sliding moves in four directions, blocked by first occupied square
  • KnightMoveStrategy — eight L-shaped moves, ignoring intervening pieces
  • BishopMoveStrategy — sliding diagonal moves
  • QueenMoveStrategy — combination of rook and bishop movement
  • KingMoveStrategy — single-step in any direction, plus castle validation via KingCastleValidator

The StrategyBuilder factory pattern creates the correct strategy from a piece's type at runtime.

Move Validation (strategy.rs)​

The MoveValidator struct runs multiple checks before any move is accepted:

  1. Is the target square in bounds?
  2. Is the move within the piece's legal movement pattern?
  3. Is the target occupied by a friendly piece?
  4. For king moves: does this move expose the king to check?
  5. For castling: are the path squares empty and not under attack?
  6. For en passant: is the target coordinate the correct en passant square?

Move Parsing (parser.rs)​

MoveReader::parse_move() converts standard algebraic notation strings (e.g. "Ng1f3", "e5xd6+", "0-0", "b7b8=Q#") into MoveResult structs. MoveWriter::write_move() does the reverse, enabling the board state to be serialised into displayable move history.


Backend: WebSocket Commands​

The API uses a text-based command protocol over WebSockets. Each command is prefixed with / and processed by the WsSession::handle_command() dispatch:

CommandDescription
/list-roomsList available chat rooms
/list-usersList users in the current room
/join-room <name>Join a chat room
/new-gameCreate a new chess game (hosted under your username)
/join-game <name>Join an existing game by name
/leave-gameLeave the current game, return to main room
/game-move <notation>Send a move in algebraic notation to the opponent
/list-available-gamesList joinable games (not yet started)
/list-all-gamesList all games regardless of state
/delete-gameDelete a game you created
/self-infoGet your session profile (username, room, game)

Game Lifecycle​

  1. Player A sends /new-game — a SessionGame is created with Player A as white
  2. Player B sends /join-game <A's username> — joins as black, game status flips to "started"
  3. Both players send /game-move <notation> to relay moves to each other
  4. Either player sends /leave-game — opponent is notified, game is cleaned up if empty

Session Management​

Each WebSocket connection spawns a WsSession actor. A heartbeat ping is sent every 5 seconds; if the client does not respond within 10 seconds, the session is disconnected and cleaned up — removed from its room and any active games.


Frontend: React + WASM Integration​

The frontend is a Gatsby.js (v5) single-page application with React 18 and TypeScript. It consumes the WASM chess library as an npm-linked package.

Pages​

RoutePagePurpose
/index.tsxLanding page with sections for online play, local play, chat, and tech showcase
/gamegame.tsxFull-screen chessboard with move controls
/lobbylobby.tsxGame lobby — create/join online games
/chatchat.tsxChat room interface with room management
/contactcontact.tsxContact page

Context Providers​

  • ConnectionContext — Manages the WebSocket connection, exposes connect(), disconnect(), sendMoveMsg(), joinGame(), newGame(), and session persistence via sessionStorage
  • GameContext — Holds the WASM Game instance, manages player turn, move history, and online/local mode
  • BoardContext — Holds the WASM Board instance, handles tile selection, highlighting, and move execution
  • ChatContext — Message parsing and display for the chat interface
  • ModalContext — Controls the pawn promotion modal overlay

Key Components​

ComponentDescription
GameContainerRenders the chessboard, move history, and controls
ChatContainerChat room with message list and input
LobbyContainerGame lobby showing available and active games
ConnectControlUsername input and connect/disconnect button
PromotePieceModalPiece selection modal for pawn promotion
TechnologySectionShowcases the Rust + WASM + Gatsby tech stack

Tech Stack​

Frontend (chess-frontend)​

LayerTechnology
Chess EngineRust (compiled to WASM via wasm-pack)
JS Bridgewasm-bindgen, serde-wasm-bindgen
UI FrameworkGatsby.js 5, React 18
LanguageTypeScript 4
StylingSCSS, Bootstrap 5, React Bootstrap, Emotion
Buildwasm-pack build + gatsby develop
Serialisationbincode, serde, serde_json

Backend (chess-api)​

LayerTechnology
HTTP/WS ServerActix-web 4, Actix-web-actors 4.1
WebSocketactix-web-actors WebSocket actors
ConcurrencyArc<Mutex<ChatServer>>, AtomicUsize
Serialisationserde, serde_json
Session IDsuuid v4 with fast-rng
CORSactix-cors (permissive)
Configurationdotenv for .env-based host/port
Loggingenv_logger, log

Development Workflow​

Chess Engine (WASM rebuild on change)​

cd chess-frontend
cargo watch -i .gitignore -i "pkg/*" -i "www/*" -s "wasm-pack build"

Frontend Dev Server (hot-reloads on WASM rebuild)​

cd chess-frontend/www
npm run watch # Gatsby dev server + npm-watch on pkg/ changes

Backend Dev Server​

cd chess-api
cargo watch -x "run"

Editing Rust files triggers a wasm-pack rebuild of the chess engine, which in turn triggers a Gatsby restart since the linked npm package changes. Editing React/TypeScript files hot-reloads as normal through Gatsby's dev server.


Known Improvements & Roadmap​

From the project's own README, there is a clear list of planned improvements:

  1. Fix king castle validation — king should not be allowed to move to castle squares if the castle option is no longer valid
  2. Refactor board move logic — the handle_move_piece method is too large and should be split into smaller, testable units
  3. Split GameManager from ChatServer — introduce a dedicated RoomManager for room logic and keep GameManager focused on game state
  4. Better client-side message updates — current polling-based approach for DOM updates should be replaced with proper reactive state management
  5. Session persistence across page reloads — maintain session on the server with heartbeat checks, allowing clients to resume after a page refresh
  6. User profile creation — proper user accounts with persistent profiles
  7. Persistent game storage — save games in backend storage for long-running or paused games
  8. Game timer — add chess clock functionality
  9. Chat box between opponents — direct messaging during active games