/js-chess-engine

Simple JavaScript chess engine without dependencies written in NodeJs. It can be used on both, server or client (web browser) and do not need persistent storage - handy for serverless solutions like AWS Lambda. This engine includes configurable AI computer logic.

Primary LanguageJavaScriptMIT LicenseMIT

JS-CHESS-ENGINE

GitHub package.json version Test CI JavaScript Style Guide License: MIT

Simple JavaScript chess engine without dependencies written in NodeJs. It can be used on both, server or client (web browser) and do not need persistent storage - handy for serverless solutions like AWS Lambda. This engine also includes configurable basic AI computer logic.

Install

Install with npm

npm i js-chess-engine --save

or install with yarn

yarn add js-chess-engine

Example

const jsChessEngine = require('js-chess-engine')
const game = new jsChessEngine.Game()
game.printToConsole()

more about importing this library.

Examples

js-chess-engine-app - React application example with js-chess-engine REST API backend (without persistent storage) - GitHub or LIVE DEMO

More examples
Simple Fastify server
Console
PC vs PC match

Documentation

Import

In this documentation I am using CommonJS require known from Node.js, but there are several ways how to import this library.

Node.js (ESM)

import jsChessEngine from 'js-chess-engine'
const game = new jsChessEngine.Game()

Node.js (CommonJS)

const jsChessEngine = require('js-chess-engine')
const game = new jsChessEngine.Game()

React

import { Game, move, status, moves, aiMove, getFen } from 'js-chess-engine'
const game = new Game()

You can find more about UMD here.

Basically, you have two options how to use this engine.


Option 1 - With in-memory

Use the Game class and create a new game.

const jsChessEngine = require('js-chess-engine')
const game = new jsChessEngine.Game()

You can control your game with game object. In this mode, many things on the chessboard are cached (in-memory), so it is faster. You can still export your game to JSON or FEN and you can use this JSON or FEN to continue your game later.

API description

constructor

new Game(configuration) - Create a new game, init players and in-game situation.

Params

  • configuration Object or String (optional) - Is a chess board configuration. Default value is a configuration for new game.

move

game.move(from, to) - Perform a move on a chessboard and recalculates in-game situation. Returns played move {"H7":"H5"}

Params

  • from String (mandatory) - Location on a chessboard where move starts (like A1,B3,...)
  • to String (mandatory) - Location on a chessboard where move ends (like A1,B3,...)

moves

game.moves(from) - Return possible moves for playing player.

Params

  • from String (optional) - Location on a chessboard (like A1,B3,...). When not provided, returns all possible moves.

setPiece

game.setPiece(location, piece) - New chess piece is added to provided location. Piece on provided location is replaced.

Params

  • location String (mandatory) - Location on a chessboard (like A1,B3,...).
  • piece String (mandatory) - A chess piece you need add (pieces syntax is same as FEN notation).

removePiece

game.removePiece(location) - Remove piece on provided location.

Params

  • location String (mandatory) - Location on a chessboard (like A1,B3,...).

aiMove

game.aiMove(level) - Calculates and perform next move by computer player. game.move(from, to) is called internally. Returns played move {"H7":"H5"}

Params

  • level Integer (optional) - Computer player skill from 0 to 3. Read more about computer AI. Default 2.

getHistory

game.getHistory(reversed) - Returns all played moves in array with chess board configuration like [{from:'A2',to:'A3',configuration:{...}},{from:'A7',to:'A6',configuration:{...}}].

configuration object is a previous chess board configuration (before that move was played) and can be used to start new game with new Game(configuration).

Params

  • reversed Boolean (optional) - When false, last move is the last element in returned array. When true, last move is first. Default false.

printToConsole

game.printToConsole() - Print a chessboard to console standard output.

exportJson

game.exportJson() - Return in-game situation represented by JSON configuration.

exportFEN

game.exportFEN() - Return in-game situation represented by FEN.


Option 2 - Without in-memory

It is possible to avoid using a Game object and call stateless functions directly. Every function needs configuration of your chessboard to work properly. This approach needs little more computing time to create and calculate everything from scratch for every call. But this can be handy in stateless environments.

const jsChessEngine = require('js-chess-engine')
const { move, status, moves, aiMove, getFen } = jsChessEngine

API description

moves

moves(boardConfiguration) - Returns an Object with possible moves for playing player {"B1":["A3","C3"],"G1":["F3","H3"]}.

Params

  • boardConfiguration Object or String (mandatory) - Is a chess board configuration. Default value is a configuration for new game.

status

status(boardConfiguration) - Return calculated JSON board configuration. You can use this function to convert your FEN to JSON.

Params

  • boardConfiguration Object or String (mandatory) - Is a chess board configuration. Default value is a configuration for new game.

getFEN

getFEN(boardConfiguration) - Return FEN string, representation of your chessboard.

Params

  • boardConfiguration Object or String (mandatory) - Is a chess board configuration. Default value is a configuration for new game.

move

move(boardConfiguration, from, to) - Perform a move on a chessboard and recalculates in-game situation. New configuration of your chessboard is returned.

Params

  • boardConfiguration Object or String (mandatory) - Is a chess board configuration. Default value is a configuration for new game.
  • from String (mandatory) - Location on a chessboard where move starts (like A1,B3,...)
  • to String (mandatory) - Location on a chessboard where move ends (like A1,B3,...)

aiMove

aiMove(boardConfiguration, level) - Return computed move as an object like {"H7":"H5"}. Use move(yourBoardConfiguration, from, to) to play this move.

Params

  • boardConfiguration Object or String (mandatory) - Is a chess board configuration. Default value is a configuration for new game.
  • level Integer (optional) - Computer player skill from 0 to 3. Read more about computer AI. Default 2.

Board Configuration

Board configuration could be described by JSON Object or FEN.

JSON

This can be handy for modern application, where is a state represented by an Object (like in React, Redux,...). You can easily merge returned state with your app state and get a new updated chessboard.

{
    "turn": "black",
    "pieces": {
        "E1": "K",
        "C1": "B",
        "E8": "k"
    },
    "moves": {
      "E8": ["E7", "F8", "F7", "D8", "D7"]
    },
    "isFinished": false,
    "check": false,
    "checkMate": false,
    "castling": {
        "whiteLong": true,
        "whiteShort": true,
        "blackLong": true,
        "blackShort": true    
    },
    "enPassant": "E6",
    "halfMove": 0,
    "fullMove": 1
}

turn - Player which plays next. Values white (default) or black.

isFinished - true when playing player cannot move (checkmate or draw). Default false.

check - true when playing player is in check. Default false.

checkMate - true when playing player has checkmate. Default false.

castling - Indicators if castling is still possible. true means yes. Default true.

  • whiteLong (queenside) - White king moves from E1 to C1.
  • whiteShort(kingside) - White king moves from E1 to G1.
  • blackLong (queenside) - Black king moves from E8 to C8.
  • blackShort (kingside) - Black king moves from E8 to C8.

enPassant - If a pawn has just made a two-square move, this is the position "behind" the pawn. This is an indicator for enPassant special pawn move. Default null.

halfMove - This is the number of halfmoves since the last capture or pawn advance. This is used to determine if a draw can be claimed under the fifty-move rule. Default 0.

fullMove - The number of the full move. It starts at 1, and is incremented after Black's move. Default 1.

moves - Is added to server response when moves() or move() was called. It indicates possible moves for playing player (turn).

{
    "A7": ["A6", "A5"],
    "B7": ["B6", "B5"]
}

Means A7 can move to A6 and A5. B7 can move to B6 and B5.

pieces - Pieces on your chessboard. Syntax is same as FEN notation.

Piece White Black
Pawn P p
Knight N n
Bishop B b
Rook R r
Queen Q q
King K k

FEN

You can also use the Forsyth–Edwards Notation (FEN).

const jsChessEngine = require('js-chess-engine')
const { move } = jsChessEngine    
const newFen = move('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1', 'H7', 'H5')
console.log(newFen)
// rnbqkbnr/ppppppp1/8/7p/4P3/8/PPPP1PPP/RNBQKBNR w KQkq h6 0 2

Computer AI

This engine includes configurable AI computer logic based on Minimax algorithm. There are five possible levels at this time.

Level Alias Moves to the future HW requirements Approx. time to move (s)*
0 Well-trained monkey 1-2 None <0.01
1 Beginner 2-4 Very low <0.1
2 Intermediate 2-4 Low 0.7
3 Advanced 3-5 Medium 4.6
4 Experienced 4-5 High 9.5

*Approx. time to move (s) - This number represent the average amount of seconds needed for one move during a chess game on t3.nano AWS instance. T3.nano is a low-cost machine with 0.5 GiB RAM and basic CPU performance. Please note, amount of time needed for calculations heavily depends on in-game situation (number of chess pieces still on a chessboard).


HINTS

  • When a pawn reaches an end of a chessboard, he is automatically converted and calculated as a Queen. If you like, player can pick a new chess piece in your app, and you can send an updated chessboard. For in-memory approach please check setPiece function.
  • Castling can be played by a King. You can receive this special move with moves call. When a move is recognized as a castling - played with a king across two chess fields, rook moves automatically with a king.
  • Halfmoves are calculated on the server, but the fifty-move rule is not handled by a server. You can handle this situation in your app if you need to.

Collaboration

Collaborators are welcome. Please do not forget to check ESLint and run tests before submitting a new pull request (npm run test).

If it is possible, also use commit message prefixes like feat: or fix: - changelog is generated from this.


TODO

  • Calculation and result caching
  • Forsyth–Edwards Notation (FEN) validation

CHANGELOG

Changelog can be found HERE .


In conclusion - why another chess engine?

I am not a chess pro. My father is. When I was ten, I had an Atari (with Turbo Basic), and I was hoping for new PC. My father told me, make me a computer program, which beat me in chess, and I buy you a new PC. Obviously, it was a trap and I failed. It comes to my mind after twenty years, and I would like to finish what I started before.