Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

chess.js

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chess.js - npm Package Compare versions

Comparing version 0.1.0 to 0.9.0

.jshintrc

436

chess.js

@@ -0,3 +1,4 @@

'use strict';
/*
* Copyright (c) 2011, Jeff Hlywa (jhlywa@gmail.com)
* Copyright (c) 2015, Jeff Hlywa (jhlywa@gmail.com)
* All rights reserved.

@@ -28,4 +29,13 @@ *

/* minified license below */
/*! Copyright (c) 2015, Jeff Hlywa (jhlywa@gmail.com)
* Released under the BSD license
* https://github.com/jhlywa/chess.js/blob/master/LICENSE
*/
var Chess = function(fen) {
/* jshint indent: false */
var BLACK = 'b';

@@ -160,3 +170,3 @@ var WHITE = 'w';

*/
if (typeof fen == 'undefined') {
if (typeof fen === 'undefined') {
load(DEFAULT_POSITION);

@@ -171,3 +181,3 @@ } else {

turn = WHITE;
castling = {w: '', b: ''};
castling = {w: 0, b: 0};
ep_square = EMPTY;

@@ -200,3 +210,3 @@ half_moves = 0;

if (piece == '/') {
if (piece === '/') {
square += 8;

@@ -227,3 +237,3 @@ } else if (is_digit(piece)) {

ep_square = (tokens[3] == '-') ? EMPTY : SQUARES[tokens[3]];
ep_square = (tokens[3] === '-') ? EMPTY : SQUARES[tokens[3]];
half_moves = parseInt(tokens[4], 10);

@@ -254,3 +264,3 @@ move_number = parseInt(tokens[5], 10);

var tokens = fen.split(/\s+/);
if (tokens.length != 6) {
if (tokens.length !== 6) {
return {valid: false, error_number: 1, error: errors[1]};

@@ -285,4 +295,4 @@ }

/* 7th criterion: 1st field contains 8 rows? */
var rows = tokens[0].split("/");
if (rows.length != 8) {
var rows = tokens[0].split('/');
if (rows.length !== 8) {
return {valid: false, error_number: 7, error: errors[7]};

@@ -302,3 +312,3 @@ }

}
sum_fields += parseInt(rows[i][k]);
sum_fields += parseInt(rows[i][k], 10);
previous_was_number = true;

@@ -313,7 +323,7 @@ } else {

}
if (sum_fields != 8) {
if (sum_fields !== 8) {
return {valid: false, error_number: 10, error: errors[10]};
}
}
/* everything's okay! */

@@ -338,3 +348,3 @@ return {valid: true, error_number: 0, error: errors[0]};

fen += (color == WHITE) ?
fen += (color === WHITE) ?
piece.toUpperCase() : piece.toLowerCase();

@@ -348,3 +358,3 @@ }

if (i != SQUARES.h1) {
if (i !== SQUARES.h1) {
fen += '/';

@@ -366,5 +376,5 @@ }

cflags = cflags || '-';
var epflags = (ep_square == EMPTY) ? '-' : algebraic(ep_square);
var epflags = (ep_square === EMPTY) ? '-' : algebraic(ep_square);
return [fen, turn, cflags, epflags, half_moves, move_number].join(' ')
return [fen, turn, cflags, epflags, half_moves, move_number].join(' ');
}

@@ -374,4 +384,4 @@

for (var i = 0; i < args.length; i += 2) {
if (typeof args[i] == "string" &&
typeof args[i + 1] == "string") {
if (typeof args[i] === 'string' &&
typeof args[i + 1] === 'string') {
header[args[i]] = args[i + 1];

@@ -392,8 +402,8 @@ }

if (fen != DEFAULT_POSITION) {
header["SetUp"] = fen;
header["FEN"] = '1';
if (fen !== DEFAULT_POSITION) {
header['SetUp'] = '1';
header['FEN'] = fen;
} else {
delete header["SetUp"];
delete header["FEN"];
delete header['SetUp'];
delete header['FEN'];
}

@@ -414,3 +424,3 @@ }

/* check for piece */
if (SYMBOLS.indexOf(piece.type.toLowerCase()) == -1) {
if (SYMBOLS.indexOf(piece.type.toLowerCase()) === -1) {
return false;

@@ -425,4 +435,11 @@ }

var sq = SQUARES[square];
/* don't let the user place more than one king */
if (piece.type == KING &&
!(kings[piece.color] == EMPTY || kings[piece.color] == sq)) {
return false;
}
board[sq] = {type: piece.type, color: piece.color};
if (piece.type == KING) {
if (piece.type === KING) {
kings[piece.color] = sq;

@@ -439,3 +456,3 @@ }

board[SQUARES[square]] = null;
if (piece && piece.type == KING) {
if (piece && piece.type === KING) {
kings[piece.color] = EMPTY;

@@ -449,39 +466,35 @@ }

function generate_moves(settings) {
function build_move(board, from, to, flags, promotion) {
var move = {
color: turn,
from: from,
to: to,
flags: flags,
piece: board[from].type
};
if (promotion) {
move.flags |= BITS.PROMOTION;
move.promotion = promotion;
}
if (board[to]) {
move.captured = board[to].type;
} else if (flags & BITS.EP_CAPTURE) {
move.captured = PAWN;
}
return move;
}
function generate_moves(options) {
function add_move(board, moves, from, to, flags) {
/* if pawn promotion */
if (board[from].type == PAWN &&
(rank(to) == RANK_8 || rank(to) == RANK_1)) {
if (board[from].type === PAWN &&
(rank(to) === RANK_8 || rank(to) === RANK_1)) {
var pieces = [QUEEN, ROOK, BISHOP, KNIGHT];
for (var i = 0, len = pieces.length; i < len; i++) {
var promotion = {
color: turn,
from: from,
to: to,
flags: flags | BITS.PROMOTION,
promotion: pieces[i],
piece: board[from].type
};
/* add the captured piece */
if (board[to]) {
promotion.captured = board[to].type;
}
moves.push(promotion);
moves.push(build_move(board, from, to, flags, pieces[i]));
}
} else {
var move = {
color: turn,
from: from,
to: to,
flags: flags,
piece: board[from].type
};
if (board[to]) {
move.captured = board[to].type;
}
moves.push(move);
moves.push(build_move(board, from, to, flags));
}

@@ -495,3 +508,22 @@ }

for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
var first_sq = SQUARES.a8;
var last_sq = SQUARES.h1;
var single_square = false;
/* do we want legal moves? */
var legal = (typeof options !== 'undefined' && 'legal' in options) ?
options.legal : true;
/* are we generating moves for a single square? */
if (typeof options !== 'undefined' && 'square' in options) {
if (options.square in SQUARES) {
first_sq = last_sq = SQUARES[options.square];
single_square = true;
} else {
/* invalid square */
return [];
}
}
for (var i = first_sq; i <= last_sq; i++) {
/* did we run off the end of the board */

@@ -501,7 +533,7 @@ if (i & 0x88) { i += 7; continue; }

var piece = board[i];
if (piece == null || piece.color != us) {
if (piece == null || piece.color !== us) {
continue;
}
if (piece.type == PAWN) {
if (piece.type === PAWN) {
/* single square, non-capturing */

@@ -514,3 +546,3 @@ var square = i + PAWN_OFFSETS[us][0];

var square = i + PAWN_OFFSETS[us][1];
if (second_rank[us] == rank(i) && board[square] == null) {
if (second_rank[us] === rank(i) && board[square] == null) {
add_move(board, moves, i, square, BITS.BIG_PAWN);

@@ -526,5 +558,5 @@ }

if (board[square] != null &&
board[square].color == them) {
board[square].color === them) {
add_move(board, moves, i, square, BITS.CAPTURE);
} else if (square == ep_square) {
} else if (square === ep_square) {
add_move(board, moves, i, ep_square, BITS.EP_CAPTURE);

@@ -545,3 +577,3 @@ }

} else {
if (board[square].color == us) break;
if (board[square].color === us) break;
add_move(board, moves, i, square, BITS.CAPTURE);

@@ -552,3 +584,3 @@ break;

/* break, if knight or king */
if (piece.type == 'n' || piece.type == 'k') break;
if (piece.type === 'n' || piece.type === 'k') break;
}

@@ -559,42 +591,42 @@ }

/* king-side castling */
if (castling[us] & BITS.KSIDE_CASTLE) {
var castling_from = kings[us];
var castling_to = castling_from + 2;
/* check for castling if: a) we're generating all moves, or b) we're doing
* single square move generation on the king's square
*/
if ((!single_square) || last_sq === kings[us]) {
/* king-side castling */
if (castling[us] & BITS.KSIDE_CASTLE) {
var castling_from = kings[us];
var castling_to = castling_from + 2;
if (board[castling_from + 1] == null &&
board[castling_to] == null &&
!attacked(them, kings[us]) &&
!attacked(them, castling_from + 1) &&
!attacked(them, castling_to)) {
add_move(board, moves, kings[us] , castling_to,
BITS.KSIDE_CASTLE);
if (board[castling_from + 1] == null &&
board[castling_to] == null &&
!attacked(them, kings[us]) &&
!attacked(them, castling_from + 1) &&
!attacked(them, castling_to)) {
add_move(board, moves, kings[us] , castling_to,
BITS.KSIDE_CASTLE);
}
}
}
/* queen-side castling */
if (castling[us] & BITS.QSIDE_CASTLE) {
var castling_from = kings[us];
var castling_to = castling_from - 2;
/* queen-side castling */
if (castling[us] & BITS.QSIDE_CASTLE) {
var castling_from = kings[us];
var castling_to = castling_from - 2;
if (board[castling_from - 1] == null &&
board[castling_from - 2] == null &&
board[castling_from - 3] == null &&
!attacked(them, kings[us]) &&
!attacked(them, castling_from - 1) &&
!attacked(them, castling_to)) {
add_move(board, moves, kings[us], castling_to,
BITS.QSIDE_CASTLE);
if (board[castling_from - 1] == null &&
board[castling_from - 2] == null &&
board[castling_from - 3] == null &&
!attacked(them, kings[us]) &&
!attacked(them, castling_from - 1) &&
!attacked(them, castling_to)) {
add_move(board, moves, kings[us], castling_to,
BITS.QSIDE_CASTLE);
}
}
}
/* if no parameters passed in, assume legal w/ algebraic moves */
if (typeof settings == 'undefined') {
settings = {legal: true};
}
/* return all pseudo-legal moves (this includes moves that allow the king
* to be captured
* to be captured)
*/
if (settings.legal != null && settings.legal == false) {
if (!legal) {
return moves;

@@ -616,3 +648,3 @@ }

/* convert a move from 0x88 coordinates to Standard Algebraic Notation
/* convert a move from 0x88 coordinates to Standard Algebraic Notation
* (SAN)

@@ -630,3 +662,3 @@ */

if (move.piece != PAWN) {
if (move.piece !== PAWN) {
output += move.piece.toUpperCase() + disambiguator;

@@ -636,3 +668,3 @@ }

if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {
if (move.piece == PAWN) {
if (move.piece === PAWN) {
output += algebraic(move.from)[0];

@@ -669,3 +701,3 @@ }

/* if empty square or wrong color */
if (board[i] == null || board[i].color != color) continue;
if (board[i] == null || board[i].color !== color) continue;

@@ -677,7 +709,7 @@ var piece = board[i];

if (ATTACKS[index] & (1 << SHIFTS[piece.type])) {
if (piece.type == PAWN) {
if (piece.type === PAWN) {
if (difference > 0) {
if (piece.color == WHITE) return true;
if (piece.color === WHITE) return true;
} else {
if (piece.color == BLACK) return true;
if (piece.color === BLACK) return true;
}

@@ -688,3 +720,3 @@ continue;

/* if the piece is a knight or a king */
if (piece.type == 'n' || piece.type == 'k') return true;
if (piece.type === 'n' || piece.type === 'k') return true;

@@ -695,3 +727,3 @@ var offset = RAYS[index];

var blocked = false;
while (j != square) {
while (j !== square) {
if (board[j] != null) { blocked = true; break; }

@@ -717,7 +749,7 @@ j += offset;

function in_checkmate() {
return in_check() && generate_moves().length == 0
return in_check() && generate_moves().length === 0;
}
function in_stalemate() {
return !in_check() && generate_moves().length == 0
return !in_check() && generate_moves().length === 0;
}

@@ -727,5 +759,8 @@

var pieces = {};
var bishops = [];
var num_pieces = 0;
var sq_color = 0;
for (var i = SQUARES.a8; i<= SQUARES.h1; i++) {
sq_color = (sq_color + 1) % 2;
if (i & 0x88) { i += 7; continue; }

@@ -735,4 +770,7 @@

if (piece) {
pieces[piece.type] = (piece.type in pieces) ?
pieces[piece.type] = (piece.type in pieces) ?
pieces[piece.type] + 1 : 1;
if (piece.type === BISHOP) {
bishops.push(sq_color);
}
num_pieces++;

@@ -743,9 +781,17 @@ }

/* k vs. k */
if (num_pieces == 2) { return true; }
if (num_pieces === 2) { return true; }
/* k vs. kn .... or .... k vs. kb */
else if (num_pieces == 3 && (pieces[BISHOP] == 1 ||
pieces[KNIGHT] == 1)) { return true; }
else if (num_pieces === 3 && (pieces[BISHOP] === 1 ||
pieces[KNIGHT] === 1)) { return true; }
/* TODO: kb vs. kb where both bishops are on the same color */
/* kb vs. kb where any number of bishops are all on the same color */
else if (num_pieces === pieces[BISHOP] + 2) {
var sum = 0;
var len = bishops.length;
for (var i = 0; i < len; i++) {
sum += bishops[i];
}
if (sum === 0 || sum === len) { return true; }
}

@@ -813,3 +859,3 @@ return false;

if (move.flags & BITS.EP_CAPTURE) {
if (turn == BLACK) {
if (turn === BLACK) {
board[move.to - 16] = null;

@@ -827,3 +873,3 @@ } else {

/* if we moved the king */
if (board[move.to].type == KING) {
if (board[move.to].type === KING) {
kings[board[move.to].color] = move.to;

@@ -845,12 +891,11 @@

/* turn off castling */
castling[turn] = '';
castling[us] = '';
}
/* turn off castling if we move a rook */
if (castling[turn] != '') {
for (var i = 0, len = ROOKS[turn].length; i < len; i++) {
if (move.from == ROOKS[turn][i].square) {
castling[turn] =
castling[turn] ^= ROOKS[turn][i].flag;
if (castling[us]) {
for (var i = 0, len = ROOKS[us].length; i < len; i++) {
if (move.from === ROOKS[us][i].square &&
castling[us] & ROOKS[us][i].flag) {
castling[us] ^= ROOKS[us][i].flag;
break;

@@ -862,7 +907,7 @@ }

/* turn off castling if we capture a rook */
if (castling[them] != '') {
if (castling[them]) {
for (var i = 0, len = ROOKS[them].length; i < len; i++) {
if (move.to == ROOKS[them][i].square) {
castling[them] =
castling[them] ^= ROOKS[them][i].flag;
if (move.to === ROOKS[them][i].square &&
castling[them] & ROOKS[them][i].flag) {
castling[them] ^= ROOKS[them][i].flag;
break;

@@ -875,3 +920,3 @@ }

if (move.flags & BITS.BIG_PAWN) {
if (turn == 'b') {
if (turn === 'b') {
ep_square = move.to - 16;

@@ -886,3 +931,3 @@ } else {

/* reset the 50 move counter if a pawn is moved or a piece is captured */
if (move.piece == PAWN) {
if (move.piece === PAWN) {
half_moves = 0;

@@ -895,3 +940,3 @@ } else if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {

if (turn == BLACK) {
if (turn === BLACK) {
move_number++;

@@ -918,3 +963,3 @@ }

board[move.from] = board[move.to];
board[move.from].type = move.piece // to undo any promotions
board[move.from].type = move.piece; // to undo any promotions
board[move.to] = null;

@@ -926,3 +971,3 @@

var index;
if (us == BLACK) {
if (us === BLACK) {
index = move.to - 16;

@@ -973,10 +1018,10 @@ } else {

*/
if (piece == ambig_piece && from != ambig_from && to == ambig_to) {
if (piece === ambig_piece && from !== ambig_from && to === ambig_to) {
ambiguities++;
if (rank(from) == rank(ambig_from)) {
if (rank(from) === rank(ambig_from)) {
same_rank++;
}
if (file(from) == file(ambig_from)) {
if (file(from) === file(ambig_from)) {
same_file++;

@@ -1013,3 +1058,3 @@ }

/* display the rank */
if (file(i) == 0) {
if (file(i) === 0) {
s += ' ' + '87654321'[rank(i)] + ' |';

@@ -1024,3 +1069,3 @@ }

var color = board[i].color;
var symbol = (color == WHITE) ?
var symbol = (color === WHITE) ?
piece.toUpperCase() : piece.toLowerCase();

@@ -1058,7 +1103,7 @@ s += ' ' + symbol + ' ';

function swap_color(c) {
return c == WHITE ? BLACK : WHITE;
return c === WHITE ? BLACK : WHITE;
}
function is_digit(c) {
return '0123456789'.indexOf(c) != -1
return '0123456789'.indexOf(c) !== -1;
}

@@ -1089,3 +1134,3 @@

for (var property in obj) {
if (typeof property == 'object') {
if (typeof property === 'object') {
dupe[property] = clone(obj[property]);

@@ -1108,3 +1153,3 @@ } else {

function perft(depth) {
var moves = generate_moves({legal: false})
var moves = generate_moves({legal: false});
var nodes = 0;

@@ -1175,3 +1220,3 @@ var color = turn;

var ugly_moves = generate_moves();
var ugly_moves = generate_moves(options);
var moves = [];

@@ -1181,6 +1226,6 @@

/* does the user want a full move object (most likely not), or just
/* does the user want a full move object (most likely not), or just
* SAN
*/
if (typeof options != 'undefined' && 'verbose' in options &&
if (typeof options !== 'undefined' && 'verbose' in options &&
options.verbose) {

@@ -1243,7 +1288,7 @@ moves.push(make_pretty(ugly_moves[i]));

*/
var newline = (typeof options == "object" &&
typeof options.newline_char == "string") ?
options.newline_char : "\n";
var max_width = (typeof options == "object" &&
typeof options.max_width == "number") ?
var newline = (typeof options === 'object' &&
typeof options.newline_char === 'string') ?
options.newline_char : '\n';
var max_width = (typeof options === 'object' &&
typeof options.max_width === 'number') ?
options.max_width : 0;

@@ -1256,5 +1301,5 @@ var result = [];

/* TODO: order of enumerated properties in header object is not
* guaranteed, see ECMA-262 spec (section 12.6.4)
* guaranteed, see ECMA-262 spec (section 12.6.4)
*/
result.push("[" + i + " \"" + header[i] + "\"]" + newline);
result.push('[' + i + ' \"' + header[i] + '\"]' + newline);
header_exists = true;

@@ -1274,3 +1319,3 @@ }

var moves = [];
var move_string = "";
var move_string = '';
var pgn_move_number = 1;

@@ -1283,6 +1328,6 @@

/* if the position started with black to move, start PGN with 1. ... */
if (pgn_move_number == 1 && move.color == 'b') {
if (pgn_move_number === 1 && move.color === 'b') {
move_string = '1. ...';
pgn_move_number++;
} else if (move.color == 'w') {
} else if (move.color === 'w') {
/* store the previous generated move_string if we have one */

@@ -1294,5 +1339,5 @@ if (move_string.length) {

pgn_move_number++;
}
}
move_string = move_string + " " + move_to_san(move);
move_string = move_string + ' ' + move_to_san(move);
make_move(move);

@@ -1307,3 +1352,3 @@ }

/* is there a result? */
if (typeof header.Result != 'undefined') {
if (typeof header.Result !== 'undefined') {
moves.push(header.Result);

@@ -1315,4 +1360,4 @@ }

*/
if (max_width == 0) {
return result.join("") + moves.join(" ");
if (max_width === 0) {
return result.join('') + moves.join(' ');
}

@@ -1324,6 +1369,6 @@

/* if the current move will push past max_width */
if (current_width + moves[i].length > max_width && i != 0) {
if (current_width + moves[i].length > max_width && i !== 0) {
/* don't end the line with whitespace */
if (result[result.length - 1] == " ") {
if (result[result.length - 1] === ' ') {
result.pop();

@@ -1334,4 +1379,4 @@ }

current_width = 0;
} else if (i != 0) {
result.push(" ");
} else if (i !== 0) {
result.push(' ');
current_width++;

@@ -1343,3 +1388,3 @@ }

return result.join("");
return result.join('');
},

@@ -1349,3 +1394,3 @@

function mask(str) {
return str.replace(/\n/g, '\\n');
return str.replace(/\\/g, '\\');
}

@@ -1357,5 +1402,8 @@

function move_from_san(move) {
/* strip off any move decorations: e.g Nf3+?! */
var moveReplaced = move.replace(/[+#?!=]/,'');
var moves = generate_moves();
for (var i = 0, len = moves.length; i < len; i++) {
if (move == move_to_san(moves[i])) {
if (moveReplaced ==
move_to_san(moves[i]).replace(/[+#?!=]/,'')) {
return moves[i];

@@ -1378,9 +1426,9 @@ }

}
function parse_pgn_header(header, options) {
var newline_char = (typeof options == 'object' &&
typeof options.newline_char == 'string') ?
options.newline_char : '\n';
var newline_char = (typeof options === 'object' &&
typeof options.newline_char === 'string') ?
options.newline_char : '\r?\n';
var header_obj = {};
var headers = header.split(newline_char);
var headers = header.split(new RegExp(mask(newline_char)));
var key = '';

@@ -1396,12 +1444,12 @@ var value = '';

}
return header_obj;
}
var newline_char = (typeof options == 'object' &&
typeof options.newline_char == 'string') ?
options.newline_char : '\n';
var newline_char = (typeof options === 'object' &&
typeof options.newline_char === 'string') ?
options.newline_char : '\r?\n';
var regex = new RegExp('^(\\[(.|' + mask(newline_char) + ')*\\])' +
'(' + mask(newline_char) + ')*' +
'1\.(' + mask(newline_char) + '|.)*$', 'g');
'1.(' + mask(newline_char) + '|.)*$', 'g');

@@ -1412,8 +1460,8 @@ /* get header part of the PGN file */

/* no info part given, begins with moves */
if (header_string[0] != '[') {
if (header_string[0] !== '[') {
header_string = '';
}
reset();
/* parse PGN header */

@@ -1424,3 +1472,3 @@ var headers = parse_pgn_header(header_string, options);

}
/* delete header to get the moves */

@@ -1431,3 +1479,3 @@ var ms = pgn.replace(header_string, '').replace(new RegExp(mask(newline_char), 'g'), ' ');

ms = ms.replace(/(\{[^}]+\})+?/g, '');
/* delete move numbers */

@@ -1441,3 +1489,3 @@ ms = ms.replace(/\d+\./g, '');

/* delete empty entries */
moves = moves.join(",").replace(/,,+/g, ',').split(",");
moves = moves.join(',').replace(/,,+/g, ',').split(',');
var move = '';

@@ -1447,3 +1495,3 @@

move = get_move_obj(moves[half_move]);
/* move not possible! (don't clear the board to examine to show the

@@ -1458,7 +1506,7 @@ * latest valid position)

}
/* examine last move */
move = moves[moves.length - 1];
if (POSSIBLE_RESULTS.indexOf(move) > -1) {
if (has_keys(header) && typeof header.Result == 'undefined') {
if (has_keys(header) && typeof header.Result === 'undefined') {
set_header(['Result', move]);

@@ -1503,6 +1551,8 @@ }

if (typeof move == 'string') {
if (typeof move === 'string') {
/* convert the move string to a move object */
/* strip off any move decorations: e.g Nf3+?! */
var moveReplaced = move.replace(/[+#?!=]/,'');
for (var i = 0, len = moves.length; i < len; i++) {
if (move == move_to_san(moves[i])) {
if (moveReplaced === move_to_san(moves[i]).replace(/[+#?!=]/,'')) {
move_obj = moves[i];

@@ -1512,9 +1562,9 @@ break;

}
} else if (typeof move == 'object') {
} else if (typeof move === 'object') {
/* convert the pretty move object to an ugly move object */
for (var i = 0, len = moves.length; i < len; i++) {
if (move.from == algebraic(moves[i].from) &&
move.to == algebraic(moves[i].to) &&
if (move.from === algebraic(moves[i].from) &&
move.to === algebraic(moves[i].to) &&
(!('promotion' in moves[i]) ||
move.promotion == moves[i].promotion)) {
move.promotion === moves[i].promotion)) {
move_obj = moves[i];

@@ -1569,3 +1619,3 @@ break;

var sq_0x88 = SQUARES[square];
return ((rank(sq_0x88) + file(sq_0x88)) % 2 == 0) ? 'light' : 'dark';
return ((rank(sq_0x88) + file(sq_0x88)) % 2 === 0) ? 'light' : 'dark';
}

@@ -1579,3 +1629,3 @@

var move_history = [];
var verbose = (typeof options != 'undefined' && 'verbose' in options &&
var verbose = (typeof options !== 'undefined' && 'verbose' in options &&
options.verbose);

@@ -1600,7 +1650,9 @@

}
}
};
};
/* export Chess object if using node or any other CommonJS compatible
* environment */
if (typeof exports != 'undefined') exports.Chess = Chess;
if (typeof exports !== 'undefined') exports.Chess = Chess;
/* export Chess object for any RequireJS compatible environment */
if (typeof define !== 'undefined') define( function () { return Chess; });
{
"name": "chess.js",
"version": "0.1.0",
"version": "0.9.0",
"description": "A Javascript chess library for chess move generation/validation, piece placement/movement, and check/checkmate/draw detection",
"author": "Jeff Hlywa <jhlywa@gmail.com> (https://github.com/jhlywa)",
"contributors": [
"Steve Bragg (https://github.com/2sb18)",
"E. Azer Koçulu (https://github.com/azer)",
"Falco Nogatz (https://https://github.com/fnogatz)"
],
"keywords": ["chess"],
"main": "chess.js",
"repository" : {
"type" : "git",
"url" : "http://github.com/jhlywa/chess.js.git"
},
"devDependencies": {
"mocha": "*",
"chai": "*"
}
}
# chess.js
chess.js is a Javascript chess library that is used for chess move generation/validation,
piece placement/movement, and check/checkmate/stalemate detection - basically everything
but the AI.
chess.js is a Javascript chess library that is used for chess move
generation/validation, piece placement/movement, and check/checkmate/stalemate
detection - basically everything but the AI.
Using chess.js in a browser is straight-forward:
<script type="text/javascript" src="chess.js"></script>
<script type="text/javascript">
chess.js has been extensively tested in node.js and most modern browsers.
var chess = new Chess();
...
</script>
Using chess.js in node.js is equally easy:
var ch = require('/chess.js')
var chess = new ch.Chess();
...
## Example Code
The code below plays a complete game of chess ... randomly.
var sys = require('sys'),
ch = require('./chess');
```js
var Chess = require('./chess').Chess;
var chess = new Chess();
var chess = new ch.Chess();
while (!chess.game_over()) {
var moves = chess.moves();
var move = moves[Math.floor(Math.random() * moves.length)];
chess.move(move);
}
console.log(chess.pgn());
```
while (!chess.game_over()) {
sys.puts('position: ' + chess.fen());
var moves = chess.moves();
var move = moves[Math.floor(Math.random() * moves.length)];
chess.move(move);
sys.puts('move: ' + move);
}
## Sites Using chess.js
- [The Internet Chess Club (ICC)](http://www.chessclub.com/)
- [lichess](http://lichess.org/tv)
- [Redbull - Battle for the Queen](http://battleforthequeen.redbull.com/)
- [3D Hartwig Chess](http://creativejs.com/2012/12/3d-hartwig-chess/)
- [Scene VR](http://client.scenevr.com/?connect=chess.scenevr.hosting/chess.xml)
- [Multiplayer Chess](http://chessapp.com/)
- [Reti Chess](http://retichess.nodejitsu.com/)
- [Chess Fork](http://www.chessfork.com/)
- [Lozza](http://op12no2.me/posts/1641)
- [angular-chess](http://theborakompanioni.github.io/angular-chess)
Need a user interface? Try Chris Oakman's excellent
[chessboard.js](http://chessboardjs.com) library. See
[chessboard.js - Random vs Random](http://chessboardjs.com/examples#5002) for
an example integration of chess.js with chessboard.js.
## API

@@ -46,7 +48,9 @@

// board defaults to the starting position when called with no parameters
var chess = new Chess();
```js
// board defaults to the starting position when called with no parameters
var chess = new Chess();
// pass in a FEN string to load a particular position
var chess = new Chess('r1k4r/p2nb1p1/2b4p/1p1n1p2/2PP4/3Q1NB1/1P3PPP/R5K1 b - c3 0 19');
// pass in a FEN string to load a particular position
var chess = new Chess('r1k4r/p2nb1p1/2b4p/1p1n1p2/2PP4/3Q1NB1/1P3PPP/R5K1 b - c3 0 19');
```

@@ -56,21 +60,23 @@ ### .ascii()

var chess = new Chess();
```js
var chess = new Chess();
// make some moves
chess.move('e4');
chess.move('e5');
chess.move('f4');
// make some moves
chess.move('e4');
chess.move('e5');
chess.move('f4');
chess.ascii();
// -> ' +------------------------+
// 8 | r n b q k b n r |
// 7 | p p p p . p p p |
// 6 | . . . . . . . . |
// 5 | . . . . p . . . |
// 4 | . . . . P P . . |
// 3 | . . . . . . . . |
// 2 | P P P P . . P P |
// 1 | R N B Q K B N R |
// +------------------------+
// a b c d e f g h'
chess.ascii();
// -> ' +------------------------+
// 8 | r n b q k b n r |
// 7 | p p p p . p p p |
// 6 | . . . . . . . . |
// 5 | . . . . p . . . |
// 4 | . . . . P P . . |
// 3 | . . . . . . . . |
// 2 | P P P P . . P P |
// 1 | R N B Q K B N R |
// +------------------------+
// a b c d e f g h'
```

@@ -80,5 +86,7 @@ ### .clear()

chess.clear();
chess.fen();
// -> '8/8/8/8/8/8/8/8 w - - 0 1' <- empty board
```js
chess.clear();
chess.fen();
// -> '8/8/8/8/8/8/8/8 w - - 0 1' <- empty board
```

@@ -88,26 +96,30 @@ ### .fen()

var chess = new Chess();
```js
var chess = new Chess();
// make some moves
chess.move('e4');
chess.move('e5');
chess.move('f4');
// make some moves
chess.move('e4');
chess.move('e5');
chess.move('f4');
chess.fen();
// -> 'rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPPP2PP/RNBQKBNR b KQkq f3 0 2'
chess.fen();
// -> 'rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPPP2PP/RNBQKBNR b KQkq f3 0 2'
```
### .game_over()
Returns true or false if the game has ended via checkmate, stalemate, or draw.
### .game_over()
Returns true if the game has ended via checkmate, stalemate, draw, threefold repetition, or insufficient material. Otherwise, returns false.
var chess = new Chess();
chess.game_over();
// -> false
```js
var chess = new Chess();
chess.game_over();
// -> false
chess.load('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.game_over();
// -> true (stalemate)
chess.load('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.game_over();
// -> true (stalemate)
chess.load('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.game_over();
// -> true (checkmate)
chess.load('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.game_over();
// -> true (checkmate)
```

@@ -117,9 +129,11 @@ ### .get(square)

chess.clear();
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
```js
chess.clear();
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
chess.get('a5');
// -> { type: 'p', color: 'b' },
chess.get('a6');
// -> null
chess.get('a5');
// -> { type: 'p', color: 'b' },
chess.get('a6');
// -> null
```

@@ -131,64 +145,76 @@ ### .history([ options ])

var chess = new Chess();
chess.move('e4');
chess.move('e5');
chess.move('f4');
chess.move('exf4');
chess.history();
// -> ['e4', 'e5', 'f4', 'exf4']
```js
var chess = new Chess();
chess.move('e4');
chess.move('e5');
chess.move('f4');
chess.move('exf4');
chess.history({ verbose: true });
// -> [{ color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e4' },
// { color: 'b', from: 'e7', to: 'e5', flags: 'b', piece: 'p', san: 'e5' },
// { color: 'w', from: 'f2', to: 'f4', flags: 'b', piece: 'p', san: 'f4' },
// { color: 'b', from: 'e5', to: 'f4', flags: 'c', piece: 'p', captured: 'p', san: 'exf4' }]
chess.history();
// -> ['e4', 'e5', 'f4', 'exf4']
### .in_check()
chess.history({ verbose: true });
// -> [{ color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e4' },
// { color: 'b', from: 'e7', to: 'e5', flags: 'b', piece: 'p', san: 'e5' },
// { color: 'w', from: 'f2', to: 'f4', flags: 'b', piece: 'p', san: 'f4' },
// { color: 'b', from: 'e5', to: 'f4', flags: 'c', piece: 'p', captured: 'p', san: 'exf4' }]
```
### .in_check()
Returns true or false if the side to move is in check.
var chess = new Chess('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.in_check();
// -> true
```js
var chess = new Chess('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.in_check();
// -> true
```
### .in_checkmate()
### .in_checkmate()
Returns true or false if the side to move has been checkmated.
var chess = new Chess('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.in_checkmate();
// -> true
```js
var chess = new Chess('rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3');
chess.in_checkmate();
// -> true
```
### .in_draw()
### .in_draw()
Returns true or false if the game is drawn (50-move rule or insufficient material).
var chess = new Chess('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.in_draw();
// -> true
```js
var chess = new Chess('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.in_draw();
// -> true
```
### .in_stalemate()
### .in_stalemate()
Returns true or false if the side to move has been stalemated.
var chess = new Chess('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.in_stalemate();
// -> true
```js
var chess = new Chess('4k3/4P3/4K3/8/8/8/8/8 b - - 0 78');
chess.in_stalemate();
// -> true
```
### .in_threefold_repetition()
### .in_threefold_repetition()
Returns true or false if the current board position has occurred three or more
times.
var chess = new Chess('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
// -> true
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 1st time
chess.in_threefold_repetition();
// -> false
```js
var chess = new Chess('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
// -> true
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 1st time
chess.in_threefold_repetition();
// -> false
chess.move('Nf3'); chess.move('Nf6') chess.move('Ng1'); chess.move('Ng8');
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 2nd time
chess.in_threefold_repetition();
// -> false
chess.move('Nf3'); chess.move('Nf6'); chess.move('Ng1'); chess.move('Ng8');
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 2nd time
chess.in_threefold_repetition();
// -> false
chess.move('Nf3'); chess.move('Nf6') chess.move('Ng1'); chess.move('Ng8');
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 3rd time
chess.in_threefold_repetition();
// -> true
chess.move('Nf3'); chess.move('Nf6'); chess.move('Ng1'); chess.move('Ng8');
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq occurs 3rd time
chess.in_threefold_repetition();
// -> true
```

@@ -199,16 +225,20 @@ ### .header()

chess.header('White', 'Robert James Fischer');
chess.header('Black', 'Mikhail Tal');
```js
chess.header('White', 'Robert James Fischer');
chess.header('Black', 'Mikhail Tal');
// or
// or
chess.header('White', 'Morphy', 'Black', 'Anderssen', 'Date', '1858-??-??');
chess.header('White', 'Morphy', 'Black', 'Anderssen', 'Date', '1858-??-??');
```
### .insufficient_material()
### .insufficient_material()
Returns true if the game is drawn due to insufficient material (K vs. K,
K vs. KB, or K vs. KN); otherwise false.
var chess = new Chess('k7/8/n7/8/8/8/8/7K b - - 0 1');
chess.insufficient_material()
// -> true
```js
var chess = new Chess('k7/8/n7/8/8/8/8/7K b - - 0 1');
chess.insufficient_material()
// -> true
```

@@ -219,54 +249,58 @@ ### .load(fen)

var chess = new Chess();
chess.load('4r3/8/2p2PPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> true
chess.load('4r3/8/X12XPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> false, bad piece X
```js
var chess = new Chess();
chess.load('4r3/8/2p2PPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> true
chess.load('4r3/8/X12XPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> false, bad piece X
```
### .load_pgn(pgn, [ options ])
Load the moves of a game stored in
[Portal Game Notation](http://en.wikipedia.org/wiki/Portable_Game_Notation).
Options is a optional parameter that contains a 'newline_char' denoting the line
delimiter (the default delimiter is '\n'). Returns true if the PGN was parsed
successfully, otherwise false.
Load the moves of a game stored in
[Portable Game Notation](http://en.wikipedia.org/wiki/Portable_Game_Notation).
Options is a optional parameter that contains a 'newline_char' which is a
string representation of a RegExp (and should not be pre-escaped) and defaults
to '\r?\n'). Returns true if the PGN was parsed successfully, otherwise false.
var chess = new Chess();
pgn = ['[Event "Casual Game"]',
'[Site "Berlin GER"]',
'[Date "1852.??.??"]',
'[EventDate "?"]',
'[Round "?"]',
'[Result "1-0"]',
'[White "Adolf Anderssen"]',
'[Black "Jean Dufresne"]',
'[ECO "C52"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "47"]',
'',
'1.e4 e5 2.Nf3 Nc6 3.Bc4 Bc5 4.b4 Bxb4 5.c3 Ba5 6.d4 exd4 7.O-O',
'd3 8.Qb3 Qf6 9.e5 Qg6 10.Re1 Nge7 11.Ba3 b5 12.Qxb5 Rb8 13.Qa4',
'Bb6 14.Nbd2 Bb7 15.Ne4 Qf5 16.Bxd3 Qh5 17.Nf6+ gxf6 18.exf6',
'Rg8 19.Rad1 Qxf3 20.Rxe7+ Nxe7 21.Qxd7+ Kxd7 22.Bf5+ Ke8',
'23.Bd7+ Kf8 24.Bxe7# 1-0'];
```js
var chess = new Chess();
pgn = ['[Event "Casual Game"]',
'[Site "Berlin GER"]',
'[Date "1852.??.??"]',
'[EventDate "?"]',
'[Round "?"]',
'[Result "1-0"]',
'[White "Adolf Anderssen"]',
'[Black "Jean Dufresne"]',
'[ECO "C52"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "47"]',
'',
'1.e4 e5 2.Nf3 Nc6 3.Bc4 Bc5 4.b4 Bxb4 5.c3 Ba5 6.d4 exd4 7.O-O',
'd3 8.Qb3 Qf6 9.e5 Qg6 10.Re1 Nge7 11.Ba3 b5 12.Qxb5 Rb8 13.Qa4',
'Bb6 14.Nbd2 Bb7 15.Ne4 Qf5 16.Bxd3 Qh5 17.Nf6+ gxf6 18.exf6',
'Rg8 19.Rad1 Qxf3 20.Rxe7+ Nxe7 21.Qxd7+ Kxd7 22.Bf5+ Ke8',
'23.Bd7+ Kf8 24.Bxe7# 1-0'];
chess.load_pgn(pgn.join('\n'));
// -> true
chess.load_pgn(pgn.join('\n'));
// -> true
chess.fen()
// -> 1r3kr1/pbpBBp1p/1b3P2/8/8/2P2q2/P4PPP/3R2K1 b - - 0 24
chess.fen()
// -> 1r3kr1/pbpBBp1p/1b3P2/8/8/2P2q2/P4PPP/3R2K1 b - - 0 24
chess.ascii()
// -> ' +------------------------+
// 8 | . r . . . k r . |
// 7 | p b p B B p . p |
// 6 | . b . . . P . . |
// 5 | . . . . . . . . |
// 4 | . . . . . . . . |
// 3 | . . P . . q . . |
// 2 | P . . . . P P P |
// 1 | . . . R . . K . |
// +------------------------+
// a b c d e f g h'
chess.ascii()
// -> ' +------------------------+
// 8 | . r . . . k r . |
// 7 | p b p B B p . p |
// 6 | . b . . . P . . |
// 5 | . . . . . . . . |
// 4 | . . . . . . . . |
// 3 | . . P . . q . . |
// 2 | P . . . . P P P |
// 1 | . . . R . . K . |
// +------------------------+
// a b c d e f g h'
```

@@ -278,12 +312,14 @@ ### .move(move)

var chess = new Chess();
```js
var chess = new Chess();
chess.move('e4')
// -> { color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e2' }
chess.move('e4')
// -> { color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e4' }
chess.move('nf6') // SAN is case sensitive!!
// -> null
chess.move('nf6') // SAN is case sensitive!!
// -> null
chess.move('Nf6')
// -> { color: 'b', from: 'g8', to: 'f6', flags: 'n', piece: 'n', san: 'Nf6' }
chess.move('Nf6')
// -> { color: 'b', from: 'g8', to: 'f6', flags: 'n', piece: 'n', san: 'Nf6' }
```

@@ -293,24 +329,34 @@ Or by passing .move() a move object (only the 'to', 'from', and when necessary

var chess = new Chess();
```js
var chess = new Chess();
chess.move({ from: 'g2', to: 'g3' });
// -> { color: 'w', from: 'g2', to: 'g3', flags: 'n', piece: 'p', san: 'g3' }
chess.move({ from: 'g2', to: 'g3' });
// -> { color: 'w', from: 'g2', to: 'g3', flags: 'n', piece: 'p', san: 'g3' }
```
### .moves([ options ])
Returns a list of all legal moves from the current position. The function be passed a options hash which controls the verbosity of the return values (this may change in the future).
Returns a list of legals moves from the current position. The function takes an optional parameter which controls the single-square move generation and verbosity.
var chess = new Chess();
chess.moves();
// -> ['a3', 'a4', 'b3', 'b4', 'c3', 'c4', 'd3', 'd4', 'e3', 'e4',
'f3', 'f4', 'g3', 'g4', 'h3', 'h4', 'Na3', 'Nc3', 'Nf3', 'Nh3']
```js
var chess = new Chess();
chess.moves();
// -> ['a3', 'a4', 'b3', 'b4', 'c3', 'c4', 'd3', 'd4', 'e3', 'e4',
// 'f3', 'f4', 'g3', 'g4', 'h3', 'h4', 'Na3', 'Nc3', 'Nf3', 'Nh3']
chess.moves({ verbose: true });
// -> [{ color: 'w', from: 'a2', to: 'a3',
flags: 'n', piece: 'p', san 'a3'
# a captured: key is included when the move is a capture
# a promotion: key is included when the move is a promotion
},
...
]
chess.moves({square: 'e2'});
// -> ['e3', 'e4']
chess.moves({square: 'e9'}); // invalid square
// -> []
chess.moves({ verbose: true });
// -> [{ color: 'w', from: 'a2', to: 'a3',
// flags: 'n', piece: 'p', san 'a3'
// # a captured: key is included when the move is a capture
// # a promotion: key is included when the move is a promotion
// },
// ...
// ]
```
The _piece_, _captured_, and _promotion_ fields contain the lowercase

@@ -325,3 +371,3 @@ representation of the applicable piece.

- 'c' - a standard capture
- 'p' - a promotion
- 'p' - a promotion
- 'k' - kingside castling

@@ -336,11 +382,13 @@ - 'q' - queenside castling

var chess = new Chess();
chess.info('White', 'Plunky', 'Black', 'Plinkie');
chess.move('e4');
chess.move('e5');
chess.move('Nc3');
chess.move('Nc6');
```js
var chess = new Chess();
chess.header('White', 'Plunky', 'Black', 'Plinkie');
chess.move('e4');
chess.move('e5');
chess.move('Nc3');
chess.move('Nc6');
chess.pgn({ max_width: 5, newline_char: '<br />' });
// -> '[White "Plunky"]<br />[Black "Plinkie"]<br /><br />1. e4 e5<br />2. Nc3 Nc6'
chess.pgn({ max_width: 5, newline_char: '<br />' });
// -> '[White "Plunky"]<br />[Black "Plinkie"]<br /><br />1. e4 e5<br />2. Nc3 Nc6'
```

@@ -350,27 +398,45 @@ ### .put(piece, square)

{ type: ..., color: ... }. Returns true if piece was successfully placed,
otherwise false.
otherwise the board remains unchanged and false is returned. `put()` will fail
when passed an invalid piece or square, or when two or more kings of the
same color are placed.
chess.clear();
```js
chess.clear();
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
// -> true
chess.put({ type: 'k', color: 'w' }, 'h1') // shorthand
// -> true
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
// -> true
chess.put({ type: 'k', color: 'w' }, 'h1') // shorthand
// -> true
chess.fen();
// -> '8/8/8/p7/8/8/8/7K w - - 0 0'
chess.fen();
// -> '8/8/8/p7/8/8/8/7K w - - 0 0'
chess.put({ type: 'z', color: 'w' }, 'a1') // invalid piece
// -> false
chess.clear();
chess.put({ type: 'k', color: 'w' }, 'a1')
// -> true
chess.put({ type: 'k', color: 'w' }, 'h1') // fail - two kings
// -> false
```
### .remove(square)
Remove and return the piece on _square_.
chess.clear();
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
chess.put({ type: chess.KING, color: chess.WHITE }, 'h1') // put a white king on h1
```js
chess.clear();
chess.put({ type: chess.PAWN, color: chess.BLACK }, 'a5') // put a black pawn on a5
chess.put({ type: chess.KING, color: chess.WHITE }, 'h1') // put a white king on h1
chess.remove('a5');
// -> { type: 'p', color: 'b' },
chess.remove('h1');
// -> { type: 'k', color: 'w' },
chess.remove('e1');
// -> null
chess.remove('a5');
// -> { type: 'p', color: 'b' },
chess.remove('h1');
// -> { type: 'k', color: 'w' },
chess.remove('e1');
// -> null
```

@@ -383,9 +449,11 @@ ### .reset()

var chess = Chess();
chess.square_color('h1')
// -> 'light'
chess.square_color('a7')
// -> 'dark'
chess.square_color('bogus square')
// -> null
```js
var chess = Chess();
chess.square_color('h1')
// -> 'light'
chess.square_color('a7')
// -> 'dark'
chess.square_color('bogus square')
// -> null
```

@@ -395,24 +463,28 @@ ### .turn()

chess.load('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1')
chess.turn()
// -> 'b'
```js
chess.load('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1')
chess.turn()
// -> 'b'
```
### .undo()
Takeback the last half-move, returning a move object if successful, otherwise null.
var chess = new Chess();
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
chess.move('e4');
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
```js
var chess = new Chess();
chess.undo();
// -> { color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e4' }
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
chess.undo();
// -> null
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
chess.move('e4');
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
chess.undo();
// -> { color: 'w', from: 'e2', to: 'e4', flags: 'b', piece: 'p', san: 'e4' }
chess.fen();
// -> 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'
chess.undo();
// -> null
```
### .validate_fen(fen):

@@ -422,36 +494,27 @@ Returns a validation object specifying validity or the errors found within the

chess.validate_fen('2n1r3/p1k2pp1/B1p3b1/P7/5bP1/2N1B3/1P2KP2/2R5 b - - 4 25');
// -> { valid: true, error_number: 0, error: 'No errors.' }
```js
chess.validate_fen('2n1r3/p1k2pp1/B1p3b1/P7/5bP1/2N1B3/1P2KP2/2R5 b - - 4 25');
// -> { valid: true, error_number: 0, error: 'No errors.' }
chess.validate_fen('4r3/8/X12XPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> { valid: false, error_number: 9,
// error: '1st field (piece positions) is invalid [invalid piece].' }
chess.validate_fen('4r3/8/X12XPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45');
// -> { valid: false, error_number: 9,
// error: '1st field (piece positions) is invalid [invalid piece].' }
```
## MUSIC
## CONTRIBUTORS
Special thanks to the following developers for their patches and contributions:
- [Steve Bragg](https://github.com/2sb18)
- [E. Azer Koçulu](https://github.com/azer)
- [Falco Nogatz](https://github.com/fnogatz)
Musical support provided by:
- [The Grateful Dead](http://www.youtube.com/watch?v=YLzUme1gN8c)
- [The Grateful Dead](https://www.youtube.com/watch?feature=player_detailpage&v=ANF6qanEB7s#t=2999)
- [Umphrey's McGee](http://www.youtube.com/watch?v=jh-1fFWkSdw)
## BUGS
- the en passant square and castling flags aren't adjusted when using the put/remove functions (workaround: use .load() instead)
- The en passant square and castling flags aren't adjusted when using the put/remove functions (workaround: use .load() instead)
## TODO
- add AI (basic alpha-beta search w/ primitive position evaluation)
- add jQuery chessboard widget
- investigate the use of piece lists (this may shave a few cycles off generate_moves() and attacked())
- Investigate the use of piece lists (this may shave a few cycles off
generate_moves() and attacked()).
- Refactor API to use camelCase - yuck.
- Add more robust FEN validation.

@@ -1,22 +0,12 @@

var is_node = true;
try {
is_node = typeof process == 'object'
} catch (e) {
is_node = false;
if (typeof require != "undefined") {
var chai = require('chai');
var Chess = require('../chess').Chess;
}
var assert = chai.assert;
if (is_node) {
var sys = require('sys'),
ch = require('../chess');
var log = sys.puts;
var Chess = ch.Chess;
} else {
var log = function(text, newline) {
var console = document.getElementById('console');
console.innerHTML += text;
if (typeof newline == 'undefined' || newline == true) {
console.innerHTML += '<br />';
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}

@@ -26,7 +16,7 @@ }

function perft_unit_tests() {
var chess = new Chess();
var start = new Date;
suite("Perft", function() {
var perfts = [
{fen: 'r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1',
{fen: 'r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1',
depth: 3, nodes: 97862},

@@ -41,21 +31,77 @@ {fen: '8/PPP4k/8/8/8/8/4Kppp/8 w - - 0 1',

var total_nodes = 0;
for (var i = 0; i < perfts.length; i++) {
chess.load(perfts[i].fen);
var nodes = chess.perft(perfts[i].depth);
var s = 'Perft Test #' + i + ': ' + perfts[i].fen + ' - ' + nodes + ' : ';
s += (nodes != perfts[i].nodes) ? 'FAILED!' : 'PASSED!';
total_nodes += nodes;
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
perfts.forEach(function(perft) {
var chess = new Chess();
chess.load(perft.fen);
log('--> Perft Time: ' + diff + ' secs ' + '(' + Math.floor(total_nodes / diff) + ' NPS)');
log('');
}
test(perft.fen, function() {
var nodes = chess.perft(perft.depth);
assert(nodes == perft.nodes);
});
function checkmate_unit_tests() {
});
});
suite("Single Square Move Generation", function() {
var positions = [
{fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
square: 'e2', verbose: false, moves: ['e3', 'e4']},
{fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
square: 'e9', verbose: false, moves: []}, // invalid square
{fen: 'rnbqk1nr/pppp1ppp/4p3/8/1b1P4/2N5/PPP1PPPP/R1BQKBNR w KQkq - 2 3',
square: 'c3', verbose: false, moves: []}, // pinned piece
{fen: '8/k7/8/8/8/8/7p/K7 b - - 0 1',
square: 'h2', verbose: false, moves: ['h1=Q+', 'h1=R+', 'h1=B', 'h1=N']}, // promotion
{fen: 'r1bq1rk1/1pp2ppp/p1np1n2/2b1p3/2B1P3/2NP1N2/PPPBQPPP/R3K2R w KQ - 0 8',
square: 'e1', verbose: false, moves: ['Kf1', 'Kd1', 'O-O', 'O-O-O']}, // castling
{fen: 'r1bq1rk1/1pp2ppp/p1np1n2/2b1p3/2B1P3/2NP1N2/PPPBQPPP/R3K2R w - - 0 8',
square: 'e1', verbose: false, moves: ['Kf1', 'Kd1']}, // no castling
{fen: '8/7K/8/8/1R6/k7/1R1p4/8 b - - 0 1',
square: 'a3', verbose: false, moves: []}, // trapped king
{fen: '8/7K/8/8/1R6/k7/1R1p4/8 b - - 0 1',
square: 'd2', verbose: true,
moves:
[{color:'b', from:'d2', to:'d1', flags:'np', piece:'p', promotion:'q', san:'d1=Q'},
{color:'b', from:'d2', to:'d1', flags:'np', piece:'p', promotion:'r', san:'d1=R'},
{color:'b', from:'d2', to:'d1', flags:'np', piece:'p', promotion:'b', san:'d1=B'},
{color:'b', from:'d2', to:'d1', flags:'np', piece:'p', promotion:'n', san:'d1=N'}]
}, // verbose
{fen: 'rnbqk2r/ppp1pp1p/5n1b/3p2pQ/1P2P3/B1N5/P1PP1PPP/R3KBNR b KQkq - 3 5',
square: 'f1', verbose: true, moves: []}, // issue #30
];
positions.forEach(function(position) {
var chess = new Chess();
chess.load(position.fen);
test(position.fen + ' ' + position.square, function() {
var moves = chess.moves({square: position.square, verbose: position.verbose});
var passed = position.moves.length == moves.length;
for (var j = 0; j < moves.length; j++) {
if (!position.verbose) {
passed = passed && moves[j] == position.moves[j];
} else {
for (var k in moves[j]) {
passed = passed && moves[j][k] == position.moves[j][k];
}
}
}
assert(passed);
});
});
});
suite("Checkmate", function() {
var chess = new Chess();
var start = new Date;
var checkmates = [

@@ -65,21 +111,19 @@ '8/5r2/4K1q1/4p3/3k4/8/8/8 w - - 0 7',

'r3k2r/ppp2p1p/2n1p1p1/8/2B2P1q/2NPb1n1/PP4PP/R2Q3K w kq - 0 8',
'8/6R1/pp1r3p/6p1/P3R1Pk/1P4P1/7K/8 b - - 0 4',
]
'8/6R1/pp1r3p/6p1/P3R1Pk/1P4P1/7K/8 b - - 0 4'
];
for (var i = 0; i < checkmates.length; i++) {
chess.load(checkmates[i]);
var s = 'Checkmate Test #' + i + ': ' + checkmates[i] + ' : ';
s += (chess.in_checkmate()) ? 'PASSED!' : 'FAILED!';
log(s);
}
checkmates.forEach(function(checkmate) {
chess.load(checkmate);
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Checkmate Time: ' + diff + ' secs');
log('');
}
test(checkmate, function() {
assert(chess.in_checkmate());
});
});
function stalemate_unit_tests() {
var chess = new Chess();
var start = new Date;
});
suite("Stalemate", function() {
var stalemates = [

@@ -90,17 +134,17 @@ '1R6/8/8/8/8/8/7R/k6K b - - 0 1',

for (var i = 0; i < stalemates.length; i++) {
chess.load(stalemates[i]);
var s = 'Stalemate Test #' + i + ': ' + stalemates[i] + ' : ';
s += (chess.in_stalemate()) ? 'PASSED!' : 'FAILED!';
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Stalemate Time: ' + diff + ' secs');
log('');
}
stalemates.forEach(function(stalemate) {
var chess = new Chess();
chess.load(stalemate);
function insufficient_material_unit_test() {
var chess = new Chess();
var start = new Date;
test(stalemate, function() {
assert(chess.in_stalemate())
});
});
});
suite("Insufficient Material", function() {
var positions = [

@@ -112,26 +156,29 @@ {fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', draw: false},

{fen: '8/2b5/8/8/8/8/8/k6K w - - 0 1', draw: true},
{fen: '8/b7/3B4/8/8/8/8/k6K w - - 0 1', draw: true},
{fen: '8/b7/B7/8/8/8/8/k6K w - - 0 1', draw: false},
{fen: '8/b1B1b1B1/1b1B1b1B/8/8/8/8/1k5K w - - 0 1', draw: true},
{fen: '8/bB2b1B1/1b1B1b1B/8/8/8/8/1k5K w - - 0 1', draw: false}
];
for (var i = 0; i < positions.length; i++) {
chess.load(positions[i].fen);
var s = 'Insufficient Material Test #' + i + ': ' + positions[i].fen + ' : ';
if (positions[i].draw) {
s += (chess.insufficient_material() && chess.in_draw()) ? 'PASSED!' : 'FAILED!';
} else {
s += (!chess.insufficient_material() && !chess.in_draw()) ? 'PASSED!' : 'FAILED!';
}
positions.forEach(function(position) {
var chess = new Chess();
chess.load(position.fen);
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Insufficient Material Time: ' + diff + ' secs');
log('');
}
test(position.fen, function() {
if (position.draw) {
assert(chess.insufficient_material() && chess.in_draw());
} else {
assert(!chess.insufficient_material() && !chess.in_draw());
}
});
function threefold_repetition_unit_test() {
var chess = new Chess();
var start = new Date;
});
});
suite("Threefold Repetition", function() {
var positions = [
{fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
{fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
moves: ['Nf3', 'Nf6', 'Ng1', 'Ng8', 'Nf3', 'Nf6', 'Ng1', 'Ng8']},

@@ -144,34 +191,30 @@

for (var i = 0; i < positions.length; i++) {
chess.load(positions[i].fen);
var s = 'Threefold Repetition Test #' + i + ': ' + positions[i].fen + ' : ';
var passed = true;
for (var j = 0; j < positions[i].moves.length; j++) {
if (chess.in_threefold_repetition()) {
passed = false;
break;
positions.forEach(function(position) {
var chess = new Chess();
chess.load(position.fen);
test(position.fen, function() {
var passed = true;
for (var j = 0; j < position.moves.length; j++) {
if (chess.in_threefold_repetition()) {
passed = false;
break;
}
chess.move(position.moves[j]);
}
chess.move(positions[i].moves[j]);
}
if (!passed) {
s += 'FAILED!';
} else {
s += (chess.in_threefold_repetition() && chess.in_draw()) ? 'PASSED!' : 'FAILED!';
}
assert(passed && chess.in_threefold_repetition() && chess.in_draw());
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Threefold Repetition Time: ' + diff + ' secs');
log('');
}
});
function algebraic_notation_tests() {
var chess = new Chess();
var start = new Date;
var passed = true;
});
});
suite("Algebraic Notation", function() {
var positions = [
{fen: '7k/3R4/3p2Q1/6Q1/2N1N3/8/8/3R3K w - - 0 1',
{fen: '7k/3R4/3p2Q1/6Q1/2N1N3/8/8/3R3K w - - 0 1',
moves: ['Rd8#', 'Re7', 'Rf7', 'Rg7', 'Rh7#', 'R7xd6', 'Rc7', 'Rb7', 'Ra7',

@@ -188,3 +231,3 @@ 'Qf7', 'Qe8#', 'Qg7#', 'Qg8#', 'Qh7#', 'Q6h6#', 'Q6h5#', 'Q6f5',

'axb8=N', 'c8=Q+', 'c8=R+', 'c8=B', 'c8=N', 'cxb8=Q+', 'cxb8=R+',
'cxb8=B', 'cxb8=N', 'Ra2', 'Ra3', 'Ra4', 'Ra5', 'Ra6', 'Rb1',
'cxb8=B', 'cxb8=N', 'Ra2', 'Ra3', 'Ra4', 'Ra5', 'Ra6', 'Rb1',
'Rc1', 'Rd1', 'Kd2', 'Ke2', 'Kf2', 'Kf1', 'Kd1', 'Rh2', 'Rh3',

@@ -212,31 +255,32 @@ 'Rh4', 'Rh5', 'Rh6', 'Rh7', 'Rh8+', 'Rg1', 'Rf1+', 'O-O+',

'N4b3', 'Kb8']},
];
];
for (var i = 0; i < positions.length; i++) {
var s = 'Algebraic Notation Test #' + i + ': ' + positions[i].fen + ' : ';
chess.load(positions[i].fen);
var moves = chess.moves();
if (moves.length != positions[i].moves.length) {
passed = false;
} else {
for (var j = 0; j < moves.length; j++) {
if (positions[i].moves.indexOf(moves[j]) == -1) {
passed = false;
break;
}
}
}
s += passed ? 'PASSED!' : 'FAILED!';
log(s);
}
positions.forEach(function(position) {
var chess = new Chess();
var passed = true;
chess.load(position.fen);
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Algebraic Notation Time: ' + diff + ' secs');
log('');
}
test(position.fen, function() {
var moves = chess.moves();
if (moves.length != position.moves.length) {
passed = false;
} else {
for (var j = 0; j < moves.length; j++) {
if (position.moves.indexOf(moves[j]) == -1) {
passed = false;
break;
}
}
}
assert(passed);
});
function get_put_remove_tests() {
});
});
suite("Get/Put/Remove", function() {
var chess = new Chess();
var start = new Date;
var passed = true;

@@ -263,76 +307,92 @@ var positions = [

should_pass: false},
/* disallow two kings (black) */
{pieces: {a7: {type: chess.KING, color: chess.BLACK},
h2: {type: chess.KING, color: chess.WHITE},
a8: {type: chess.KING, color: chess.BLACK}},
should_pass: false},
/* disallow two kings (white) */
{pieces: {a7: {type: chess.KING, color: chess.BLACK},
h2: {type: chess.KING, color: chess.WHITE},
h1: {type: chess.KING, color: chess.WHITE}},
should_pass: false},
/* allow two kings if overwriting the exact same square */
{pieces: {a7: {type: chess.KING, color: chess.BLACK},
h2: {type: chess.KING, color: chess.WHITE},
h2: {type: chess.KING, color: chess.WHITE}},
should_pass: true},
];
for (var i = 0; i < positions.length; i++) {
positions.forEach(function(position) {
passed = true;
chess.clear();
var s = 'Get/Put Test #' + i + ' (' + positions[i].should_pass + '): ';
/* places the pieces */
for (var square in positions[i].pieces) {
passed &= chess.put(positions[i].pieces[square], square);
}
test("position should pass - " + position.should_pass, function() {
/* iterate over every square to make sure get returns the proper
* piece values/color
*/
for (var j = 0; j < chess.SQUARES.length; j++) {
var square = chess.SQUARES[j];
if (!(square in positions[i].pieces)) {
if (chess.get(square)) {
passed = false;
break;
}
} else {
var piece = chess.get(square);
if (!(piece &&
piece.type == positions[i].pieces[square].type &&
piece.color == positions[i].pieces[square].color)) {
passed = false;
break;
}
/* places the pieces */
for (var square in position.pieces) {
passed &= chess.put(position.pieces[square], square);
}
}
if (passed) {
/* remove the pieces */
/* iterate over every square to make sure get returns the proper
* piece values/color
*/
for (var j = 0; j < chess.SQUARES.length; j++) {
var square = chess.SQUARES[j];
var piece = chess.remove(square);
if ((!(square in positions[i].pieces)) && piece) {
passed = false;
break;
if (!(square in position.pieces)) {
if (chess.get(square)) {
passed = false;
break;
}
} else {
var piece = chess.get(square);
if (!(piece &&
piece.type == position.pieces[square].type &&
piece.color == position.pieces[square].color)) {
passed = false;
break;
}
}
}
if (piece &&
(positions[i].pieces[square].type != piece.type ||
positions[i].pieces[square].color != piece.color)) {
passed = false;
break;
if (passed) {
/* remove the pieces */
for (var j = 0; j < chess.SQUARES.length; j++) {
var square = chess.SQUARES[j];
var piece = chess.remove(square);
if ((!(square in position.pieces)) && piece) {
passed = false;
break;
}
if (piece &&
(position.pieces[square].type != piece.type ||
position.pieces[square].color != piece.color)) {
passed = false;
break;
}
}
}
}
/* finally, check for an empty board */
passed = passed && (chess.fen() == '8/8/8/8/8/8/8/8 w - - 0 1');
/* finally, check for an empty board */
passed = passed && (chess.fen() == '8/8/8/8/8/8/8/8 w - - 0 1');
/* some tests should fail, so make sure we're supposed to pass/fail each
* test
*/
passed = (passed == positions[i].should_pass);
/* some tests should fail, so make sure we're supposed to pass/fail each
* test
*/
passed = (passed == position.should_pass);
s += passed ? 'PASSED!' : 'FAILED!';
assert(passed);
});
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> Get/Put Time: ' + diff + ' secs');
log('');
}
});
function fen_tests() {
var chess = new Chess();
var start = new Date;
var passed = true;
});
suite("FEN", function() {
var positions = [

@@ -354,18 +414,17 @@ {fen: '8/8/8/8/8/8/8/8 w - - 0 1', should_pass: true},

for (var i = 0; i < positions.length; i++) {
passed = true;
chess.load(positions[i].fen);
var s = 'FEN Test #' + i + ': ' + positions[i].fen + ' (' + positions[i].should_pass + '): ';
passed = (chess.fen() == positions[i].fen == positions[i].should_pass);
s += (passed) ? 'PASSED!' : 'FAILED!';
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> FEN Time: ' + diff + ' secs');
log('');
}
positions.forEach(function(position) {
var chess = new Chess();
function pgn_tests() {
var start = new Date;
test(position.fen + ' (' + position.should_pass + ')', function() {
chess.load(position.fen);
assert(chess.fen() == position.fen == position.should_pass);
});
});
});
suite("PGN", function() {
var passed = true;

@@ -375,8 +434,8 @@ var error_message;

{moves: ['d4', 'd5', 'Nf3', 'Nc6', 'e3', 'e6', 'Bb5', 'g5', 'O-O', 'Qf6', 'Nc3',
'Bd7', 'Bxc6', 'Bxc6', 'Re1', 'O-O-O', 'a4', 'Bb4', 'a5', 'b5', 'axb6',
'axb6', 'Ra8+', 'Kd7', 'Ne5+', 'Kd6', 'Rxd8+', 'Qxd8', 'Nxf7+', 'Ke7',
'Nxd5+', 'Qxd5', 'c3', 'Kxf7', 'Qf3+', 'Qxf3', 'gxf3', 'Bxf3', 'cxb4',
'e5', 'dxe5', 'Ke6', 'b3', 'Kxe5', 'Bb2+', 'Ke4', 'Bxh8', 'Nf6', 'Bxf6',
'h5', 'Bxg5', 'Bg2', 'Kxg2', 'Kf5', 'Bh4', 'Kg4', 'Bg3', 'Kf5', 'e4+',
'Kg4', 'e5', 'h4', 'Bxh4', 'Kxh4', 'e6', 'c5', 'bxc5', 'bxc5', 'e7', 'c4',
'Bd7', 'Bxc6', 'Bxc6', 'Re1', 'O-O-O', 'a4', 'Bb4', 'a5', 'b5', 'axb6',
'axb6', 'Ra8+', 'Kd7', 'Ne5+', 'Kd6', 'Rxd8+', 'Qxd8', 'Nxf7+', 'Ke7',
'Nxd5+', 'Qxd5', 'c3', 'Kxf7', 'Qf3+', 'Qxf3', 'gxf3', 'Bxf3', 'cxb4',
'e5', 'dxe5', 'Ke6', 'b3', 'Kxe5', 'Bb2+', 'Ke4', 'Bxh8', 'Nf6', 'Bxf6',
'h5', 'Bxg5', 'Bg2', 'Kxg2', 'Kf5', 'Bh4', 'Kg4', 'Bg3', 'Kf5', 'e4+',
'Kg4', 'e5', 'h4', 'Bxh4', 'Kxh4', 'e6', 'c5', 'bxc5', 'bxc5', 'e7', 'c4',
'bxc4', 'Kg4', 'e8=Q', 'Kf5', 'Qe5+', 'Kg4', 'Re4#'],

@@ -409,3 +468,3 @@ header: ['White', 'Jeff Hlywa', 'Black', 'Steve Bragg', 'GreatestGameEverPlayed?', 'True'],

max_width:20,
pgn: '[SetUp "r1bqk1nr/pppp1ppp/2n5/4p3/1bB1P3/2P2N2/P2P1PPP/RNBQK2R b KQkq - 0 1"]\n[FEN "1"]\n\n1. ... Ba5 2. O-O d6\n3. d4',
pgn: '[SetUp "1"]\n[FEN "r1bqk1nr/pppp1ppp/2n5/4p3/1bB1P3/2P2N2/P2P1PPP/RNBQK2R b KQkq - 0 1"]\n\n1. ... Ba5 2. O-O d6\n3. d4',
starting_position: 'r1bqk1nr/pppp1ppp/2n5/4p3/1bB1P3/2P2N2/P2P1PPP/RNBQK2R b KQkq - 0 1',

@@ -415,29 +474,179 @@ fen: 'r1bqk1nr/ppp2ppp/2np4/b3p3/2BPP3/2P2N2/P4PPP/RNBQ1RK1 b kq d3 0 3'}

for (var i = 0; i < positions.length; i++) {
var chess = ("starting_position" in positions[i]) ? new Chess(positions[i].starting_position) : new Chess();
passed = true;
error_message = "";
for (var j = 0; j < positions[i].moves.length; j++) {
if (chess.move(positions[i].moves[j]) === null) {
error_message = "move() did not accept " + positions[i].moves[j] + " : ";
break;
positions.forEach(function(position, i) {
test(i, function() {
var chess = ("starting_position" in position) ? new Chess(position.starting_position) : new Chess();
passed = true;
error_message = "";
for (var j = 0; j < position.moves.length; j++) {
if (chess.move(position.moves[j]) === null) {
error_message = "move() did not accept " + position.moves[j] + " : ";
break;
}
}
}
var s = 'PGN Test #' + i + ': ' + error_message;
chess.header.apply(null, positions[i].header);
var pgn = chess.pgn({max_width:positions[i].max_width, newline_char:positions[i].newline_char});
var fen = chess.fen();
passed = pgn === positions[i].pgn && fen === positions[i].fen;
s += (passed) ? 'PASSED!' : 'FAILED!';
log(s);
}
var finish = new Date;
var diff = (finish-start) / 1000;
log('--> PGN Time: ' + diff + ' secs');
log('');
}
function make_move_tests() {
chess.header.apply(null, position.header);
var pgn = chess.pgn({max_width:position.max_width, newline_char:position.newline_char});
var fen = chess.fen();
passed = pgn === position.pgn && fen === position.fen;
assert(passed && error_message.length == 0);
});
});
});
suite("Load PGN", function() {
var chess = new Chess();
var start = new Date;
var tests = [
{pgn: [
'[Event "Reykjavik WCh"]',
'[Site "Reykjavik WCh"]',
'[Date "1972.01.07"]',
'[EventDate "?"]',
'[Round "6"]',
'[Result "1-0"]',
'[White "Robert James Fischer"]',
'[Black "Boris Spassky"]',
'[ECO "D59"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "81"]',
'',
'1. c4 e6 2. Nf3 d5 3. d4 Nf6 4. Nc3 Be7 5. Bg5 O-O 6. e3 h6',
'7. Bh4 b6 8. cxd5 Nxd5 9. Bxe7 Qxe7 10. Nxd5 exd5 11. Rc1 Be6',
'12. Qa4 c5 13. Qa3 Rc8 14. Bb5 a6 15. dxc5 bxc5 16. O-O Ra7',
'17. Be2 Nd7 18. Nd4 Qf8 19. Nxe6 fxe6 20. e4 d4 21. f4 Qe7',
'22. e5 Rb8 23. Bc4 Kh8 24. Qh3 Nf8 25. b3 a5 26. f5 exf5',
'27. Rxf5 Nh7 28. Rcf1 Qd8 29. Qg3 Re7 30. h4 Rbb7 31. e6 Rbc7',
'32. Qe5 Qe8 33. a4 Qd8 34. R1f2 Qe8 35. R2f3 Qd8 36. Bd3 Qe8',
'37. Qe4 Nf6 38. Rxf6 gxf6 39. Rxf6 Kg8 40. Bc4 Kh8 41. Qf4 1-0'],
expect: true
},
{fen: '1n1Rkb1r/p4ppp/4q3/4p1B1/4P3/8/PPP2PPP/2K5 b k - 1 17',
pgn: [
'[Event "Paris"]',
'[Site "Paris"]',
'[Date "1858.??.??"]',
'[EventDate "?"]',
'[Round "?"]',
'[Result "1-0"]',
'[White "Paul Morphy"]',
'[Black "Duke Karl / Count Isouard"]',
'[ECO "C41"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "33"]',
'',
'1.e4 e5 2.Nf3 d6 3.d4 Bg4 {This is a weak move',
'already.--Fischer} 4.dxe5 Bxf3 5.Qxf3 dxe5 6.Bc4 Nf6 7.Qb3 Qe7',
'8.Nc3 c6 9.Bg5 {Black is in what\'s like a zugzwang position',
'here. He can\'t develop the [Queen\'s] knight because the pawn',
'is hanging, the bishop is blocked because of the',
'Queen.--Fischer} b5 10.Nxb5 cxb5 11.Bxb5+ Nbd7 12.O-O-O Rd8',
'13.Rxd7 Rxd7 14.Rd1 Qe6 15.Bxd7+ Nxd7 16.Qb8+ Nxb8 17.Rd8# 1-0'],
expect: true},
{pgn: [
'1. e4 e5 2. f4 exf4 3. Nf3 g5 4. h4 g4 5. Ne5 Nf6 6. Nxg4 Nxe4',
'7. d3 Ng3 8. Bxf4 Nxh1 9. Qe2+ Qe7 10. Nf6+ Kd8 11. Bxc7+ Kxc7',
'12. Nd5+ Kd8 13. Nxe7 Bxe7 14. Qg4 d6 15. Qf4 Rg8 16. Qxf7 Bxh4+',
'17. Kd2 Re8 18. Na3 Na6 19. Qh5 Bf6 20. Qxh1 Bxb2 21. Qh4+ Kd7',
'22. Rb1 Bxa3 23. Qa4+'],
expect: true},
/* regression test - broken PGN parser ended up here:
* fen = rnbqk2r/pp1p1ppp/4pn2/1N6/1bPN4/8/PP2PPPP/R1BQKB1R b KQkq - 2 6 */
{pgn: ['1. d4 Nf6 2. c4 e6 3. Nf3 c5 4. Nc3 cxd4 5. Nxd4 Bb4 6. Nb5'],
fen: 'rnbqk2r/pp1p1ppp/4pn2/1N6/1bP5/2N5/PP2PPPP/R1BQKB1R b KQkq - 2 6',
expect: true},
{pgn: ['1. e4 Qxd7 1/2-1/2'],
expect: false},
];
var newline_chars = ['\n', '<br />', '\r\n', 'BLAH'];
tests.forEach(function(t, i) {
newline_chars.forEach(function(newline, j) {
test(i + String.fromCharCode(97 + j), function() {
var result = chess.load_pgn(t.pgn.join(newline), { newline_char: newline });
var should_pass = t.expect;
/* some tests are expected to fail */
if (should_pass) {
/* some PGN's tests contain comments which are stripped during parsing,
* so we'll need compare the results of the load against a FEN string
* (instead of the reconstructed PGN [e.g. test.pgn.join(newline)])
*/
if ('fen' in t) {
assert(result && chess.fen() == t.fen);
} else {
assert(result && chess.pgn({ max_width: 65, newline_char: newline }) == t.pgn.join(newline));
}
} else {
/* this test should fail, so make sure it does */
assert(result == should_pass);
}
});
});
});
// special case dirty file containing a mix of \n and \r\n
test('dirty pgn', function() {
var pgn =
'[Event "Reykjavik WCh"]\n' +
'[Site "Reykjavik WCh"]\n' +
'[Date "1972.01.07"]\n' +
'[EventDate "?"]\n' +
'[Round "6"]\n' +
'[Result "1-0"]\n' +
'[White "Robert James Fischer"]\r\n' +
'[Black "Boris Spassky"]\n' +
'[ECO "D59"]\n' +
'[WhiteElo "?"]\n' +
'[BlackElo "?"]\n' +
'[PlyCount "81"]\n' +
'\r\n' +
'1. c4 e6 2. Nf3 d5 3. d4 Nf6 4. Nc3 Be7 5. Bg5 O-O 6. e3 h6\n' +
'7. Bh4 b6 8. cxd5 Nxd5 9. Bxe7 Qxe7 10. Nxd5 exd5 11. Rc1 Be6\n' +
'12. Qa4 c5 13. Qa3 Rc8 14. Bb5 a6 15. dxc5 bxc5 16. O-O Ra7\n' +
'17. Be2 Nd7 18. Nd4 Qf8 19. Nxe6 fxe6 20. e4 d4 21. f4 Qe7\r\n' +
'22. e5 Rb8 23. Bc4 Kh8 24. Qh3 Nf8 25. b3 a5 26. f5 exf5\n' +
'27. Rxf5 Nh7 28. Rcf1 Qd8 29. Qg3 Re7 30. h4 Rbb7 31. e6 Rbc7\n' +
'32. Qe5 Qe8 33. a4 Qd8 34. R1f2 Qe8 35. R2f3 Qd8 36. Bd3 Qe8\n' +
'37. Qe4 Nf6 38. Rxf6 gxf6 39. Rxf6 Kg8 40. Bc4 Kh8 41. Qf4 1-0\n';
var result = chess.load_pgn(pgn, { newline_char: '\r?\n' });
assert(result);
assert(chess.load_pgn(pgn));
assert(chess.pgn().match(/^\[\[/) === null);
});
});
suite('Pawn promotion without equal sign', function() {
test('load_pgn', function() {
var chess = new Chess();
assert(chess.load_pgn('1. d4 c5 2. d5 b6 3. Nc3 e6 4. dxe6 g6 5. exf7+ Ke7 6. fxg8Q'));
assert.deepEqual(chess.history(), ['d4', 'c5', 'd5', 'b6', 'Nc3', 'e6', 'dxe6', 'g6', 'exf7+', 'Ke7', 'fxg8=Q']);
});
test('move', function() {
var chess = new Chess();
assert(chess.load_pgn('1. d4 c5 2. d5 b6 3. Nc3 e6 4. dxe6 g6 5. exf7+ Ke7'));
assert.isNotNull(chess.move('fxg8Q'));
assert.deepEqual(chess.history(), ['d4', 'c5', 'd5', 'b6', 'Nc3', 'e6', 'dxe6', 'g6', 'exf7+', 'Ke7', 'fxg8=Q']);
});
});
suite("Make Move", function() {
var positions = [

@@ -458,27 +667,33 @@ {fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',

move: 'fxe6',
next: 'rnbqkbnr/pp3ppp/2ppP3/8/4P3/8/PPPP2PP/RNBQKBNR b KQkq - 0 1'}
next: 'rnbqkbnr/pp3ppp/2ppP3/8/4P3/8/PPPP2PP/RNBQKBNR b KQkq - 0 1',
captured: 'p'},
{fen: 'rnbqkbnr/pppp2pp/8/4p3/4Pp2/2PP4/PP3PPP/RNBQKBNR b KQkq e3 0 1',
legal: true,
move: 'fxe3',
next: 'rnbqkbnr/pppp2pp/8/4p3/8/2PPp3/PP3PPP/RNBQKBNR w KQkq - 0 2',
captured: 'p'}
];
for (var i = 0; i < positions.length; i++) {
chess.load(positions[i].fen);
var s = 'make move test #' + i + ': ' + positions[i].fen +
' (' + positions[i].move + ' ' + positions[i].legal + ') : ';
var result = chess.move(positions[i].move);
if (positions[i].legal) {
s += (result && chess.fen() == positions[i].next) ? 'PASSED!' : 'FAILED!';
} else {
s += (!result) ? 'PASSED!' : 'FAILED!';
}
positions.forEach(function(position) {
var chess = new Chess();
chess.load(position.fen);
test(position.fen + ' (' + position.move + ' ' + position.legal + ')', function() {
var result = chess.move(position.move);
if (position.legal) {
assert(result
&& chess.fen() == position.next
&& result.captured == position.captured);
} else {
assert(!result);
}
});
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> make move time: ' + diff + ' secs');
log('');
}
});
function validate_fen_tests() {
});
suite("Validate FEN", function() {
var chess = new Chess();
var start = new Date;
var positions = [

@@ -609,108 +824,15 @@ {fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNRw KQkq - 0 1', error_number: 1},

for (var i = 0; i < positions.length; i++) {
var s = 'validate fen test #' + i + ': ' + positions[i].fen +
' (valid: ' + (positions[i].error_number == 0) + ') : ';
var result = chess.validate_fen(positions[i].fen);
s += (result.error_number == positions[i].error_number) ?
'PASSED!' : 'FAILED! (' + result.error_number + ')';
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> validate fen time: ' + diff + ' secs');
log('');
positions.forEach(function(position) {
test(position.fen + ' (valid: ' + (position.error_number == 0) + ')', function() {
var result = chess.validate_fen(position.fen);
assert(result.error_number == position.error_number, result.error_number);
});
}
});
});
function load_pgn_tests() {
var chess = new Chess();
var start = new Date;
var tests = [
{pgn: [
'[Event "Reykjavik WCh"]',
'[Site "Reykjavik WCh"]',
'[Date "1972.01.07"]',
'[EventDate "?"]',
'[Round "6"]',
'[Result "1-0"]',
'[White "Robert James Fischer"]',
'[Black "Boris Spassky"]',
'[ECO "D59"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "81"]',
'',
'1. c4 e6 2. Nf3 d5 3. d4 Nf6 4. Nc3 Be7 5. Bg5 O-O 6. e3 h6',
'7. Bh4 b6 8. cxd5 Nxd5 9. Bxe7 Qxe7 10. Nxd5 exd5 11. Rc1 Be6',
'12. Qa4 c5 13. Qa3 Rc8 14. Bb5 a6 15. dxc5 bxc5 16. O-O Ra7',
'17. Be2 Nd7 18. Nd4 Qf8 19. Nxe6 fxe6 20. e4 d4 21. f4 Qe7',
'22. e5 Rb8 23. Bc4 Kh8 24. Qh3 Nf8 25. b3 a5 26. f5 exf5',
'27. Rxf5 Nh7 28. Rcf1 Qd8 29. Qg3 Re7 30. h4 Rbb7 31. e6 Rbc7',
'32. Qe5 Qe8 33. a4 Qd8 34. R1f2 Qe8 35. R2f3 Qd8 36. Bd3 Qe8',
'37. Qe4 Nf6 38. Rxf6 gxf6 39. Rxf6 Kg8 40. Bc4 Kh8 41. Qf4 1-0']
},
{fen: '1n1Rkb1r/p4ppp/4q3/4p1B1/4P3/8/PPP2PPP/2K5 b k - 1 17',
pgn: [
'[Event "Paris"]',
'[Site "Paris"]',
'[Date "1858.??.??"]',
'[EventDate "?"]',
'[Round "?"]',
'[Result "1-0"]',
'[White "Paul Morphy"]',
'[Black "Duke Karl / Count Isouard"]',
'[ECO "C41"]',
'[WhiteElo "?"]',
'[BlackElo "?"]',
'[PlyCount "33"]',
'',
'1.e4 e5 2.Nf3 d6 3.d4 Bg4 {This is a weak move',
'already.--Fischer} 4.dxe5 Bxf3 5.Qxf3 dxe5 6.Bc4 Nf6 7.Qb3 Qe7',
'8.Nc3 c6 9.Bg5 {Black is in what\'s like a zugzwang position',
'here. He can\'t develop the [Queen\'s] knight because the pawn',
'is hanging, the bishop is blocked because of the',
'Queen.--Fischer} b5 10.Nxb5 cxb5 11.Bxb5+ Nbd7 12.O-O-O Rd8',
'13.Rxd7 Rxd7 14.Rd1 Qe6 15.Bxd7+ Nxd7 16.Qb8+ Nxb8 17.Rd8# 1-0']},
{pgn: [
'1. e4 e5 2. f4 exf4 3. Nf3 g5 4. h4 g4 5. Ne5 Nf6 6. Nxg4 Nxe4',
'7. d3 Ng3 8. Bxf4 Nxh1 9. Qe2+ Qe7 10. Nf6+ Kd8 11. Bxc7+ Kxc7',
'12. Nd5+ Kd8 13. Nxe7 Bxe7 14. Qg4 d6 15. Qf4 Rg8 16. Qxf7 Bxh4+',
'17. Kd2 Re8 18. Na3 Na6 19. Qh5 Bf6 20. Qxh1 Bxb2 21. Qh4+ Kd7',
'22. Rb1 Bxa3 23. Qa4+']},
];
suite("History", function() {
var newline_chars = ['\n', '<br />', '\n\r', 'BLAH'];
for (var i = 0; i < tests.length; i++) {
for (var j = 0; j < newline_chars.length; j++) {
var newline = newline_chars[j];
var s = 'load pgn test #' + i + String.fromCharCode(97 + j) + ': ';
var result = chess.load_pgn(tests[i].pgn.join(newline), { newline_char: newline });
/* some PGN's tests contain comments which are stripped during parsing,
* so we'll need compare the results of the load against a FEN string
* (instead of the reconstructed PGN [e.g. tests[i].pgn.join(newline)])
*/
if ('fen' in tests[i]) {
s += (result && chess.fen() == tests[i].fen) ? 'PASSED!' : 'FAILED';
} else {
s += (result && chess.pgn({ max_width: 65, newline_char: newline }) ==
tests[i].pgn.join(newline)) ? 'PASSED!' : 'FAILED!';
}
log(s);
}
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> validate fen time: ' + diff + ' secs');
log('');
}
function history_tests() {
var chess = new Chess();
var start = new Date;
var tests = [

@@ -814,79 +936,55 @@ {verbose: false,

for (var i = 0; i < tests.length; i++) {
tests.forEach(function(t, i) {
var passed = true;
var s = 'history test #' + i + ': ';
chess.reset();
for (var j = 0; j < tests[i].moves.length; j++) {
chess.move(tests[i].moves[j])
}
var history = chess.history({verbose: tests[i].verbose});
if (tests[i].fen != chess.fen()) {
passed = false;
} else if (history.length != tests[i].moves.length) {
passed = false;
} else {
for (var j = 0; j < tests[i].moves.length; j++) {
if (!tests[i].verbose) {
if (history[j] != tests[i].moves[j]) {
passed = false;
break;
}
} else {
for (var key in history[j]) {
if (history[j][key] != tests[i].moves[j][key]) {
test(i, function() {
chess.reset();
for (var j = 0; j < t.moves.length; j++) {
chess.move(t.moves[j])
}
var history = chess.history({verbose: t.verbose});
if (t.fen != chess.fen()) {
passed = false;
} else if (history.length != t.moves.length) {
passed = false;
} else {
for (var j = 0; j < t.moves.length; j++) {
if (!t.verbose) {
if (history[j] != t.moves[j]) {
passed = false;
break;
}
} else {
for (var key in history[j]) {
if (history[j][key] != t.moves[j][key]) {
passed = false;
break;
}
}
}
}
}
}
assert(passed);
});
s += passed ? 'PASSED!' : 'FAILED!';
log(s);
}
var finish = new Date;
var diff = (finish - start) / 1000;
log('--> history test time: ' + diff + ' secs');
log('');
});
});
}
suite('Regression Tests', function() {
// Github Issue #32 reported by AlgoTrader
test('Issue #32 - castling flag reappearing', function() {
var chess = new Chess('b3k2r/5p2/4p3/1p5p/6p1/2PR2P1/BP3qNP/6QK b k - 2 28');
chess.move({from:'a8', to:'g2'});
assert(chess.fen() == '4k2r/5p2/4p3/1p5p/6p1/2PR2P1/BP3qbP/6QK w k - 0 29');
});
function run_unit_tests() {
var start = new Date;
if (!is_node) {
var console = document.getElementById('console');
if (console == null) {
alert('Can\'t locate console. Aborting.');
return
}
}
perft_unit_tests();
checkmate_unit_tests();
stalemate_unit_tests();
insufficient_material_unit_test();
threefold_repetition_unit_test();
algebraic_notation_tests();
get_put_remove_tests();
fen_tests();
pgn_tests();
load_pgn_tests();
make_move_tests();
validate_fen_tests();
history_tests();
var finish = new Date;
var diff = (finish - start) / 1000;
log('Total Time: ' + diff + ' secs');
}
if (is_node) {
run_unit_tests();
}
test('Issue #58 - placing more than one king', function() {
var chess = new Chess('N3k3/8/8/8/8/8/5b2/4K3 w - - 0 1');
assert(chess.put({type: 'k', color: 'w'}, 'a1') == false);
chess.put({type: 'q', color: 'w'}, 'a1');
chess.remove('a1');
assert(chess.moves().join(' ') == 'Kd2 Ke2 Kxf2 Kf1 Kd1');
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc