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 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.