From Concept to Code: Domain-Driven Design with F# in a Chess Game Engine
Explore the implementation of domain-driven design principles in F# through the creation of a chess game engine, utilizing discriminated unions and records for robust domain modeling.
10 novembre 2024
Published
Hugo Mufraggi
Author

Creating A Chess Engine In F# Using Domain Driven Design Principles
This is my second article about F#. Today, I’ll introduce you to domain modeling in F#.
Case Study
I’ll try to make a Chess Game system. In the short term, I want to create just a chess engine, and in the long term, maybe use the game engine alongside an API.
Domain Chess Game Engine
First, I’ll define all my Entities, and then I’ll define all the possible actions.
I’ll leverage discriminated types — a very powerful feature of F#. Discriminated unions allow you to define a type that can be one of several distinct cases. This is useful when you want to represent a value that can have multiple possible states and ensure that the value is always valid. Discriminated unions are defined using the type keyword, followed by the name of the type and a list of possible cases, separated by the | symbol. Each case can optionally have associated data of any type.
Entities
Chess Board
First, I’ll define my Column, Row, and Position.
type File = A | B | C | D | E | F | G | H
type Rank = One | Two | Three | Four | Five | Six | Seven | Eight
type Position = File * Rank
The Position is a tuple of File and Rank. In practice, you can define a Position easily like this:
let myPosition: Position = (D, Four)
Pieces
What is a Piece? For me, it combines two things: the Color and the PieceType.
type Color = White | Black
type PieceType = King | Queen | Rook | Bishop | Knight | Pawn
type Piece = Color * PieceType
I can easily define a black Rook like this:
let blackRook: Piece = (Black, Rook)
Move and GameState
The Move type is quite simple to define. It includes a starting From position, an ending To position, and a Piece. I won’t use a tuple here to make the type more explicit. Instead, I’ll define it as a record, allowing me to specify From and To fields clearly, reducing the risk of errors.
type Move = { From: Position
To: Position
Piece: Piece }
For the GameState, I’m not entirely sure about the final structure. I think my GameState is currently incomplete; in the future, I might add states like the different options for castling (e.g., CastleKingSide or CastleQueenSide).
type GameStatus =
| NotStarted
| InProgress
| Check of Color
| Checkmate of Color
| Stalemate
| Draw
f
type GameState = {
Board: Map<Position, Piece>
CurrentTurn: Color
Status: GameStatus
MoveHistory: Move list
CapturedPieces: Piece list
}
The Board type is a perfect example of the power of F#’s type system. I use a simple Map with Position as the key and Piece I can represent each position and its occupying piece and color as the value.
Conclusion
In the following article, we’ll dive into the gameplay loop and the functions that support the core logic of the chess engine. We’ll see how to handle turns, validate moves, and manage the game state as pieces interact on the board. If you don’t want to miss Part 2, subscribe to stay updated!