Node Chess is an algebraic notation driven chess engine for validating board position and understanding viable moves.
Node chess is available via npm:
$ npm install chess
To create a client that lists valid moves in algebraic notation:
const
chess = require('chess'),
game = chess.create();
To get available moves, simply call the method getStatus():
let
status = game.getStatus(),
validMoves = status.notatedMoves;
Object.keys(client.notatedMoves).forEach((notation) => {
console.log(notation);
});
Each valid move is represented as a hash in the notatedMoves property where the key of the hash is the notation itself. The following is an example of the details available for each move:
{
"Na3": {
"src": {
"file": "b",
"rank": 1,
"piece": {
"moveCount": 0,
"side": {
"name": "white"
},
"type": "knight",
"notation": "N"
}
},
"dest": {
"file": "a",
"rank": 3,
"piece": null
}
},
// ... results abbreviated
Making a move is as simple as calling the move() function with the proper notation:
const
chess = require('chess'),
game = chess.create();
// move the White Pawn located on square a2 forward two squares
let move = game.move('a4');
The move function returns an object that helps further understand state. This object also contains a method to undo the move.
move.capturedPiecemove.castlemove.enPassantmove.postSquaremove.prevSquareundo()Here is the serialized output (with description) of the return object from move():
{
move: {
// the captured piece (if capture occurred)
capturedPiece: null,
// was the move a castle?
castle: false,
// was the move en Passant?
enPassant: false,
// tje square a piece was moved to
postSquare: {
file: 'a',
rank: 4,
piece: {
moveCount: 1,
side: {
name: 'white'
},
type: 'pawn',
notation: 'R'
}
},
// the square that the piece came from
prevSquare: {
file: 'a',
rank: 2,
piece: null
}
},
// undo() can be used to back out the previous move
undo: __function__
}
The getStatus function returns the current state of the board, all valid moves and an object graph with all the squares of the board (with pieces).
boardisCheckisCheckmateisRepetitionnotatedMoves may contain valid moves.isStalematenotatedMovesHere is the serialized output (with description) of the return object from getStatus():
{
// this is the top level board
board: {
// an array of all squares on the board
squares: [{
file: 'a',
rank: 1,
piece: {
moveCount: 0,
side: {
name: 'white'
},
type: 'rook',
notation: 'R'
}
},
/* the rest of the squares... */
]
},
isCheck: false, // is the King currently in check?
isCheckmate: false, // is the King currently in checkmate?
isRepetition: false, // has 3-fold repetition occurred?
isStalemate: false, // is the board in stalemate?
// all possible moves (notated) with details for each move
notatedMoves: {
a3: {
src: {
file: 'a'
rank: 2,
piece: {
moveCount: 0,
side: {
name: 'white'
},
type: 'pawn',
notation: 'R'
}
},
dest: {
file: 'a',
rank: 3,
piece: null
}
},
/* the rest of the available moves... */
}
}
The game emits various events throughout the course of play.
capturemove object (as returned from the move()> function) is provided with this event.castlemove object (as returned from the move()> function) is provided with this event.checkattackingSquare and kingSquare).checkmateattackingSquare and kingSquare).enPassantmove object (as returned from the move()> function) is provided with this event.movemove object (as returned from the move()> function) is provided with this event.promotesquare object is provided with this event (the object contains fields for rank, file, and piece).Here is an example of the events being consumed:
const chess = require('chess');
// create a game client
const gameClient = chess.create({ PGN : true });
// when a piece is captured
gameClient.on('capture', (move) => {
console.log('A piece has been captured!');
console.log(move);
});
// when a King is placed in check
gameClient.on('check', (attack) => {
console.log('The King is under attack!');
console.log(attack);
});
// when King is placed in checkmate
gameClient.on('checkmate', (attack) => {
console.log('The game has ended due to checkmate!');
console.log(attack);
});
// when a castle occurs
gameClient.on('castle', (move) => {
console.log('A castle has occured!');
console.log(move);
});
// when en Passant occurs
gameClient.on('enPassant', (move) => {
console.log('An en Passant has occured!');
console.log(move);
});
// when a move occurs on the board
gameClient.on('move', (move) => {
console.log('A piece was moved!');
console.log(move);
});
// when a Pawn promotion occurs
gameClient.on('promote', (square) => {
console.log('A Pawn has been promoted!');
console.log(square);
});
This game is quite interesting as it resulted in a draw due to 3-fold repetition!
const
chess = require('chess'),
game = chess.create();
// 1. e4 e6
game.move('e4');
game.move('e6');
// 2. d4 d5
game.move('d4');
game.move('d5');
// 3. Nc3 Nf6
game.move('Nc3');
game.move('Nf6');
// 4. Bg5 dxe4
game.move('Bg5');
game.move('dxe4');
// 5. Nxe4 Be7
game.move('Nxe4');
game.move('Be7');
// 6. Bxf6 gxf6
game.move('Bxf6');
game.move('gxf6');
// 7. g3 f5
game.move('g3');
game.move('f5');
// 8. Nc3 Bf6
game.move('Nc3');
game.move('Bf6');
// 9. Nge2 Nc6
game.move('Nge2');
game.move('Nc6');
// 10. d5 exd5
game.move('d5');
game.move('exd5');
// 11. Nxd5 Bxb2
game.move('Nxd5');
game.move('Bxb2');
// 12. Bg2 O-O
game.move('Bg2');
game.move('0-0');
// 13. O-O Bh8
game.move('0-0');
game.move('Bh8');
// 14. Nef4 Ne5
game.move('Nef4');
game.move('Ne5');
// 15. Qh5 Ng6
game.move('Qh5');
game.move('Ng6');
// 16. Rad1 c6
game.move('Rad1');
game.move('c6');
// 17. Ne3 Qf6
game.move('Ne3');
game.move('Qf6');
// 18. Kh1 Bg7
game.move('Kh1');
game.move('Bg7');
// 19. Bh3 Ne7
game.move('Bh3');
game.move('Ne7');
// 20. Rd3 Be6
game.move('Rd3');
game.move('Be6');
// 21. Rfd1 Bh6
game.move('Rfd1');
game.move('Bh6');
// 22. Rd4 Bxf4
game.move('Rd4');
game.move('Bxf4');
// 23. Rxf4 Rad8
game.move('Rxf4');
game.move('Rad8');
// 24. Rxd8 Rxd8
game.move('Rxd8');
game.move('Rxd8');
// 25. Bxf5 Nxf5
game.move('Bxf5');
game.move('Nxf5');
// 26. Nxf5 Rd5
game.move('Nxf5');
game.move('Rd5');
// 27. g4 Bxf5
game.move('g4');
game.move('Bxf5');
// 28. gxf5 h6
game.move('gxf5');
game.move('h6');
// 29. h3 Kh7
game.move('h3');
game.move('Kh7');
// 30. Qe2 Qe5
game.move('Qe2');
game.move('Qe5');
// 31. Qh5 Qf6
game.move('Qh5');
game.move('Qf6');
// 32. Qe2 Re5
game.move('Qe2');
game.move('Re5');
// 33. Qd3 Rd5
game.move('Qd3');
game.move('Rd5');
// 34. Qe2
game.move('Qe2');
console.log(JSON.stringify(game.getStatus(), 0, 2));
The above code will output the following:
{ board:
{ squares:
[ { file: 'a', rank: 1, piece: null },
{ file: 'b', rank: 1, piece: null },
{ file: 'c', rank: 1, piece: null },
{ file: 'd', rank: 1, piece: null },
{ file: 'e', rank: 1, piece: null },
{ file: 'f', rank: 1, piece: null },
{ file: 'g', rank: 1, piece: null },
{ file: 'h',
rank: 1,
piece:
{ moveCount: 2,
side: { name: 'white' },
type: 'king',
notation: 'K' } },
{ file: 'a',
rank: 2,
piece:
{ moveCount: 0,
side: { name: 'white' },
type: 'pawn',
notation: '' } },
{ file: 'b', rank: 2, piece: null },
{ file: 'c',
rank: 2,
piece:
{ moveCount: 0,
side: { name: 'white' },
type: 'pawn',
notation: '' } },
{ file: 'd', rank: 2, piece: null },
{ file: 'e',
rank: 2,
piece:
{ moveCount: 6,
side: { name: 'white' },
type: 'queen',
notation: 'Q' } },
{ file: 'f',
rank: 2,
piece:
{ moveCount: 0,
side: { name: 'white' },
type: 'pawn',
notation: '' } },
{ file: 'g', rank: 2, piece: null },
{ file: 'h', rank: 2, piece: null },
{ file: 'a', rank: 3, piece: null },
{ file: 'b', rank: 3, piece: null },
{ file: 'c', rank: 3, piece: null },
{ file: 'd', rank: 3, piece: null },
{ file: 'e', rank: 3, piece: null },
{ file: 'f', rank: 3, piece: null },
{ file: 'g', rank: 3, piece: null },
{ file: 'h',
rank: 3,
piece:
{ moveCount: 1,
side: { name: 'white' },
type: 'pawn',
notation: '' } },
{ file: 'a', rank: 4, piece: null },
{ file: 'b', rank: 4, piece: null },
{ file: 'c', rank: 4, piece: null },
{ file: 'd', rank: 4, piece: null },
{ file: 'e', rank: 4, piece: null },
{ file: 'f',
rank: 4,
piece:
{ moveCount: 4,
side: { name: 'white' },
type: 'rook',
notation: 'R' } },
{ file: 'g', rank: 4, piece: null },
{ file: 'h', rank: 4, piece: null },
{ file: 'a', rank: 5, piece: null },
{ file: 'b', rank: 5, piece: null },
{ file: 'c', rank: 5, piece: null },
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
{ file: 'e', rank: 5, piece: null },
{ file: 'f',
rank: 5,
piece:
{ moveCount: 3,
side: { name: 'white' },
type: 'pawn',
notation: '' } },
{ file: 'g', rank: 5, piece: null },
{ file: 'h', rank: 5, piece: null },
{ file: 'a', rank: 6, piece: null },
{ file: 'b', rank: 6, piece: null },
{ file: 'c',
rank: 6,
piece:
{ moveCount: 1,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
{ file: 'd', rank: 6, piece: null },
{ file: 'e', rank: 6, piece: null },
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
{ file: 'g', rank: 6, piece: null },
{ file: 'h',
rank: 6,
piece:
{ moveCount: 1,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
{ file: 'a',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
{ file: 'b',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
{ file: 'c', rank: 7, piece: null },
{ file: 'd', rank: 7, piece: null },
{ file: 'e', rank: 7, piece: null },
{ file: 'f',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
{ file: 'g', rank: 7, piece: null },
{ file: 'h',
rank: 7,
piece:
{ moveCount: 2,
side: { name: 'black' },
type: 'king',
notation: 'K' } },
{ file: 'a', rank: 8, piece: null },
{ file: 'b', rank: 8, piece: null },
{ file: 'c', rank: 8, piece: null },
{ file: 'd', rank: 8, piece: null },
{ file: 'e', rank: 8, piece: null },
{ file: 'f', rank: 8, piece: null },
{ file: 'g', rank: 8, piece: null },
{ file: 'h', rank: 8, piece: null } ],
_events: { move: [Function] } },
isCheck: false,
isCheckmate: false,
isRepetition: true,
isStalemate: false,
notatedMoves:
{ Rd4:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 4, piece: null } },
Rd3:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 3, piece: null } },
Rd2:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 2, piece: null } },
Rd1:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 1, piece: null } },
Rd6:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 6, piece: null } },
Rd7:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 7, piece: null } },
Rd8:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'd', rank: 8, piece: null } },
Rc5:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'c', rank: 5, piece: null } },
Rb5:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'b', rank: 5, piece: null } },
Ra5:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'a', rank: 5, piece: null } },
Re5:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest: { file: 'e', rank: 5, piece: null } },
Rxf5:
{ src:
{ file: 'd',
rank: 5,
piece:
{ moveCount: 4,
side: { name: 'black' },
type: 'rook',
notation: 'R' } },
dest:
{ file: 'f',
rank: 5,
piece:
{ moveCount: 3,
side: { name: 'white' },
type: 'pawn',
notation: '' } } },
c5:
{ src:
{ file: 'c',
rank: 6,
piece:
{ moveCount: 1,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'c', rank: 5, piece: null } },
Qxf5:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest:
{ file: 'f',
rank: 5,
piece:
{ moveCount: 3,
side: { name: 'white' },
type: 'pawn',
notation: '' } } },
Qe6:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'e', rank: 6, piece: null } },
Qd6:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'd', rank: 6, piece: null } },
Qg6:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'g', rank: 6, piece: null } },
Qe7:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'e', rank: 7, piece: null } },
Qd8:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'd', rank: 8, piece: null } },
Qg5:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'g', rank: 5, piece: null } },
Qh4:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'h', rank: 4, piece: null } },
Qe5:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'e', rank: 5, piece: null } },
Qd4:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'd', rank: 4, piece: null } },
Qc3:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'c', rank: 3, piece: null } },
Qb2:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'b', rank: 2, piece: null } },
Qa1:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'a', rank: 1, piece: null } },
Qg7:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'g', rank: 7, piece: null } },
Qh8:
{ src:
{ file: 'f',
rank: 6,
piece:
{ moveCount: 3,
side: { name: 'black' },
type: 'queen',
notation: 'Q' } },
dest: { file: 'h', rank: 8, piece: null } },
h5:
{ src:
{ file: 'h',
rank: 6,
piece:
{ moveCount: 1,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'h', rank: 5, piece: null } },
a6:
{ src:
{ file: 'a',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'a', rank: 6, piece: null } },
a5:
{ src:
{ file: 'a',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'a', rank: 5, piece: null } },
b6:
{ src:
{ file: 'b',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'b', rank: 6, piece: null } },
b5:
{ src:
{ file: 'b',
rank: 7,
piece:
{ moveCount: 0,
side: { name: 'black' },
type: 'pawn',
notation: '' } },
dest: { file: 'b', rank: 5, piece: null } },
Kh8:
{ src:
{ file: 'h',
rank: 7,
piece:
{ moveCount: 2,
side: { name: 'black' },
type: 'king',
notation: 'K' } },
dest: { file: 'h', rank: 8, piece: null } },
Kg7:
{ src:
{ file: 'h',
rank: 7,
piece:
{ moveCount: 2,
side: { name: 'black' },
type: 'king',
notation: 'K' } },
dest: { file: 'g', rank: 7, piece: null } },
Kg8:
{ src:
{ file: 'h',
rank: 7,
piece:
{ moveCount: 2,
side: { name: 'black' },
type: 'king',
notation: 'K' } },
dest: { file: 'g', rank: 8, piece: null } } } }