The game of chess written in Gleam
⚠️🛠️ This project is a work in progress! While all features are complete, significant documentation and API verification efforts are underway!
This is the game of chess implemented as a Gleam library using a purely functional programming paradigm.
Great care has been taken to design an easy to use API with strong type safety and descriptive error returns. (Check out the docs!)
This project has no UI or user-facing I/O. There is, however, a basic text renderer to let you experiment before building your own UI! See the chapter Module Structure below.
Extensive snapshot-testing is done through birdie. See the chapter Testing below.
Features
Play chess acoording to professional chess rules!
-
Start
- in standard starting position
- in a verified legal custom position
-
Move the figures
- Request all legal moves of a given figure
- Prohibit moves that leave the king in check
- Pawn Promotion
- En passant
- Castling
-
Win/Lose the game
- by checkmate
- by player forfeit
-
Draw the game1
- by mutual player agreement
- by stalemate
- by insufficient material
- by threefold repititon
- by the 50 move rule
-
Track game history
- Request a history of moves
- Go back in time to past board positions/game sates
- Describe moves with standard algebraic chess notation
1 The rule of achieving a draw by a dead position is omitted as implementation is quite complex and could have significant impact on performance.
This should however not impact the actual user experience of playing chess much, as being in a dead position eventually results in a draw through either mutual agreement, threefold repetition or the 50 move rule anyway.
Let me know if you know a nifty algorithm for this!
Example Usage
This example showcases Gleam code, though the project may be used with any Erlang or JavaScript runtime through Gleam’s compiler options.
import chess
import chess/coordinates as coord
pub fn main() {
// Create a new game in standard chess starting position
let game = chess.new_game()
echo chess.get_status(game)
// => GameOngoing(next_player: White)
// White gets all legal moves of pawn on E2
echo chess.get_moves(game, coord.e2)
// => E3 and E4
// White moves pawn E2 to E4
let move =
chess.PlayerMovesFigure(chess.StandardFigureMove(
from: coord.e2,
to: coord.e4,
))
let assert Ok(game) = chess.player_move(game, move)
// Now it's black's turn
echo chess.get_status(game)
// => GameOngoing(next_player: Black)
// Black tries to illegally move king to E5
let illegal_move =
chess.PlayerMovesFigure(chess.StandardFigureMove(
from: coord.e8,
to: coord.e5,
))
echo chess.player_move(game, illegal_move)
// => Error(PlayerMoveIsIllegal)
// Black is frustrated and forfeits the game
let forfeit_move = chess.PlayerForfeits
let assert Ok(game) = chess.player_move(game, forfeit_move)
echo chess.get_status(game)
// => GameEnded(Victory(winner: White, by: Forfeit))
}
Module Structure
Use the main module chess
for all the necessary types and functions.
All other submodules are strictly optional, but are here for your convenience:
The submodule chess/coordinates
provides constants to quickly reference all squares on the chess board without constructing those values yourself.
chess/algebraic_notation
allows you to express chess moves in standard algebraic chess notation, as in e4
, Bxc3+
or exd8=R#
.
Lastly, the module chess/text_renderer
provides a basic text renderer to express chess positions as a string - with or without ANSI codes to highlight a selected figure and its possible moves. See the example screenshot of in the chapter Testing below.
Development
To compile/change the project yourself install Gleam and download the source:
git clone git@github.com:OlZe/Functional-Chess.git
cd Functional-Chess
gleam test
The html docs are built and deployed automatically on push to main
through GitHub Actions. If you want to build your own html docs locally use:
gleam docs build --open
Testing
Tests are automatically evaluated on push to main
through GitHub Actions.
Birdie is used to allow for snapshot testing, where test scenarios manipulate the chess board, pretty print it to the console, and require the user to view and confirm that the scenarios were executed correctly. See the example below.
Once a snapshot was confirmed to be correct, it will no longer require confirmation, unless the output changes, in which case birdie will show a diff between the old confirmed output and the new changed output.
When cloning this repo, all snapshots should already be confirmed to be correct.
To run the tests locally, use:
gleam test
If there are any snapshots that require confirmation use the following to view them:
gleam run -m birdie