@chess-fu/chess-game
Version:
Chess game logic
820 lines • 37.3 kB
JavaScript
;
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var fen_parser_1 = require("@chess-fu/fen-parser");
var pgn_parser_1 = require("@chess-fu/pgn-parser");
var chessMoves_1 = require("./chessMoves");
var constants_1 = require("./constants");
var chessUtils_1 = require("./chessUtils");
var lowerA = 'a';
var lowerACode = 'a'.charCodeAt(0);
var letter0Code = '0'.charCodeAt(0);
var INDEXES = {};
var STRING = 'string';
var EMPTY_STRING = '';
var EMPTY_ARRAY = Object.freeze([]);
var EMPTY_BOARD = function () { var r = new Array(constants_1.BOARD_SIZE); for (var i = 0; i < constants_1.BOARD_SIZE; i++)
r[i] = constants_1.NONE; return r; };
var PATTERN_FEN_DASH = /\-+/g;
var PATTERN_PROMOTE_RNB = /=[RNB]/;
var PATTERN_PROMOTE_TYPE = /=([QRNB])/;
var PATTERN_ANNOTATION = /(([\!\?]+)|$\d{1,3})+$/;
var PATTERN_LOWER_CASE = /[a-z]/g;
var PATTERN_UPPER_CASE = /[A-Z]/g;
var PATTERN_PGN_QUOTED = /"|\\\\/g;
var PIECE_TYPES = 'kqbnrp'.split(EMPTY_STRING);
var PROMOTIONS = 'qrnb'.split(EMPTY_STRING);
var PIECES = {};
var COLORPIECES = {};
(function initializer() {
for (var rank = 0; rank < constants_1.BOARD_WIDTH; rank++) {
for (var file = 0; file < constants_1.BOARD_WIDTH; file++) {
INDEXES["" + String.fromCharCode(lowerACode + file) + (rank + 1)] = file + (((constants_1.BOARD_HEIGHT - 1) - rank) * constants_1.BOARD_WIDTH);
}
}
for (var _i = 0, PIECE_TYPES_1 = PIECE_TYPES; _i < PIECE_TYPES_1.length; _i++) {
var piece = PIECE_TYPES_1[_i];
PIECES[constants_1.WHITE + piece] = piece.toUpperCase();
PIECES[constants_1.BLACK + piece] = piece.toLowerCase();
PIECES[piece.toUpperCase()] = piece.toUpperCase();
PIECES[piece.toLowerCase()] = piece.toLowerCase();
COLORPIECES[piece.toUpperCase()] = constants_1.WHITE + piece;
COLORPIECES[piece.toLowerCase()] = constants_1.BLACK + piece;
}
})();
var ChessGame = (function () {
function ChessGame(fen) {
if (fen) {
this.load(fen);
}
else {
this.init();
}
}
ChessGame.prototype.init = function () {
this._headers = {};
this._history = [];
this._board = EMPTY_BOARD();
this._turn = constants_1.WHITE;
this._enpass = -1;
this._halfmoveClock = 0;
this._moveNumber = 1;
this._whiteKing = this._blackKing = -1;
this._castles = EMPTY_STRING;
this._cacheValid = false;
this._gameResult = constants_1.ONGOING;
};
ChessGame.prototype.load = function (fen) {
this.init();
fen = fen || constants_1.START_FEN;
this.restoreFen(fen);
if (fen !== constants_1.START_FEN) {
this._headers.SetUp = '1';
this._headers.FEN = fen;
}
};
ChessGame.prototype.loadPgn = function (pgnGame) {
var game = new pgn_parser_1.default().parse(pgnGame)[0];
if (!game) {
throw new Error('No PGN game data found.');
}
var startFEN = constants_1.START_FEN;
var headers = game.headersMap();
if (headers.FEN && fen_parser_1.default.isFen(headers.FEN)) {
startFEN = headers.FEN;
}
var moveNumber = this._moveNumber;
this.load(startFEN);
var moves = game.moves();
for (var ix = 0; ix < moves.length; ix++) {
var move = moves[ix];
moveNumber = move.number || moveNumber;
if (!move.san)
continue;
try {
this.move(move.san);
}
catch (ex) {
console.error("Unable to restore game at move " + moveNumber + " SAN = " + move.san + ".\nFEN = " + this.fen());
throw new Error("Invalid PGN move " + this._moveNumber + ". " + (this._turn === constants_1.WHITE ? '' : '... ') + move.san);
}
}
};
ChessGame.prototype.restoreFen = function (fen) {
var data = new fen_parser_1.default(fen || constants_1.START_FEN);
var board = data.ranks.join(EMPTY_STRING).split(EMPTY_STRING);
if (board.length !== constants_1.BOARD_SIZE) {
throw new Error("Invalid FEN, size expected " + constants_1.BOARD_SIZE + " found " + board.length);
}
this._board = board.slice();
this._whiteKing = board.indexOf(constants_1.WKING);
this._blackKing = board.indexOf(constants_1.BKING);
this._castles = data.castles;
this._halfmoveClock = data.halfmoveClock;
this._moveNumber = data.moveNumber;
this._enpass = (data.enpass in INDEXES) ? INDEXES[data.enpass] : -1;
this._turn = data.turn === constants_1.BLACK ? constants_1.BLACK : constants_1.WHITE;
this.updated();
};
ChessGame.prototype.toString = function () { return this.fen(); };
ChessGame.prototype.fen = function () {
var ranks = [];
var bstring = this._board.map(function (p) { return p || constants_1.NONE; }).join(EMPTY_STRING);
for (var file = constants_1.BOARD_HEIGHT; file > 0; file--) {
var index = INDEXES[lowerA + file.toString()];
ranks.push(bstring.substr(index, constants_1.BOARD_WIDTH));
}
return ranks.join(constants_1.SLASH).replace(PATTERN_FEN_DASH, function (m) { return m.length.toString(); }) +
(" " + this._turn + " " + (this._castles || constants_1.NONE) + " " + chessUtils_1.indexToSquare(this._enpass) + " " + this._halfmoveClock + " " + this._moveNumber);
};
ChessGame.prototype.pgn = function (options) {
var newline = (options || {}).newline_char || '\n';
var width = (options || {}).max_width || 80;
if (!this._headers.Result) {
this._headers.Result = this._gameResult;
}
var pgn = [];
var headers = __assign({}, this._headers);
pgn.push.apply(pgn, this.pgnHeaders(constants_1.STANDARD_PGN_HEADERS, headers));
pgn.push.apply(pgn, this.pgnHeaders(Object.keys(headers), headers));
pgn.push();
var _a = new fen_parser_1.default(this._headers.FEN || constants_1.START_FEN), turn = _a.turn, moveNumber = _a.moveNumber;
var history = this._history;
var moveCounter = Math.max(1, moveNumber);
var line = '';
for (var ix = 0; ix < history.length; ix++) {
var moveNum = (moveCounter++) + ".";
var white = history[ix].san;
if (ix === 0 && turn === 'b') {
white = '...';
}
else {
ix++;
}
var black = ix < history.length ? history[ix].san : null;
var reqLen = 1 + moveNum.length + 1 + white.length + 1 + (black ? (black.length + 1) : 0);
if (line.length && (line.length + reqLen) >= width) {
pgn.push(line);
line = '';
}
line += "" + (line.length ? ' ' : '') + moveNum + " " + white + (black ? " " + black : '');
}
if (line.length) {
pgn.push(line);
}
pgn.push(this._headers.Result);
return pgn.join(newline);
};
ChessGame.prototype.pgnHeaders = function (keys, headers) {
return keys
.filter(function (key) { return headers.hasOwnProperty(key) && headers[key] !== null && headers[key] !== undefined; })
.map(function (key) {
var value = ("" + headers[key]).replace(PATTERN_PGN_QUOTED, function (m) { return '\\' + m[0]; });
headers[key] = undefined;
return "[" + key + " \"" + value + "\"]";
});
};
ChessGame.prototype.squares = function () {
return Object.keys(INDEXES);
};
ChessGame.prototype.turn = function () {
return this._turn;
};
ChessGame.prototype.header = function () {
var tagPairs = [];
for (var _i = 0; _i < arguments.length; _i++) {
tagPairs[_i] = arguments[_i];
}
for (var i = 0; i < (tagPairs.length - 1); i += 2) {
if (typeof tagPairs[i] === STRING &&
typeof tagPairs[i + 1] === STRING) {
this._headers[tagPairs[i]] = tagPairs[i + 1];
}
}
return __assign({}, this._headers, { Result: this._gameResult });
};
ChessGame.prototype.history = function (options) {
return this._history.map(function (moveData) {
var restoreFEN = moveData.restoreFEN, x = moveData.x, y = moveData.y, move = __rest(moveData, ["restoreFEN", "x", "y"]);
return move;
});
};
ChessGame.prototype.get = function (square) {
if (!(square in INDEXES)) {
throw new Error("Invalid square identifier: \"" + square + "\"");
}
var piece = this._board[INDEXES[square]];
if (!piece || !(piece in COLORPIECES)) {
return null;
}
return COLORPIECES[piece];
};
ChessGame.prototype.set = function (square, piece) {
if (!piece) {
return this.remove(square);
}
if (!(square in INDEXES)) {
throw new Error("Invalid square identifier: \"" + square + "\"");
}
if (!(piece in PIECES)) {
throw new Error("Invalid piece identifier: \"" + piece + "\"");
}
var offset = INDEXES[square];
piece = PIECES[piece];
this._board[offset] = PIECES[piece];
if (piece === constants_1.WKING) {
this._whiteKing = offset;
}
if (piece === constants_1.BKING) {
this._blackKing = offset;
}
this.updated();
};
ChessGame.prototype.remove = function (square) {
if (!(square in INDEXES)) {
throw new Error("Invalid square identifier: \"" + square + "\"");
}
var offset = INDEXES[square];
this._board[offset] = constants_1.NONE;
this._whiteKing = this._whiteKing !== offset ? this._whiteKing : -1;
this._blackKing = this._blackKing !== offset ? this._blackKing : -1;
this.updated();
};
ChessGame.prototype.updated = function () {
this._gameResult = constants_1.ONGOING;
this._cacheValid = false;
};
ChessGame.prototype.updateCachedState = function () {
if (this._cacheValid !== true) {
this._cacheValid = true;
this._checked = this.testForCheck(this._turn);
this._noValidMoves = !this.hasValidMoves(this._turn);
if (this.isAutomaticDraw()) {
this._gameResult = constants_1.DRAW;
}
else {
if (this._noValidMoves) {
if (this._checked) {
this._gameResult = this._turn === constants_1.WHITE ? constants_1.BLACK_WINS : constants_1.WHITE_WINS;
}
else {
this._gameResult = constants_1.DRAW;
}
}
}
}
};
ChessGame.prototype.undo = function () {
var move = this._history.pop();
if (!move)
return;
if (!move.restoreFEN)
throw new Error('History entry is missing restore point.');
this.restoreFen(move.restoreFEN);
return move;
};
ChessGame.prototype.move = function (move) {
this.assertOngoing();
if (typeof move === STRING) {
return this.moveToSAN(move);
}
move = move;
if (move.from && move.to) {
return this.performMove(move, true);
}
else if (move.san) {
return this.moveToSAN(move.san);
}
else {
throw new Error('Move is not valid.');
}
};
ChessGame.prototype.moveToSAN = function (san) {
var _this = this;
var movesValid = this.moves({ color: this._turn });
var sanMove = new pgn_parser_1.default().parseMove(san) || {};
var found = movesValid
.filter(function (m) { return (sanMove.san === m.san || (sanMove.to === m.to &&
(sanMove.piece || '').toLowerCase() === m.piece.toLowerCase() &&
(!sanMove.from || m.from.indexOf(sanMove.from.toLowerCase()) >= 0) &&
m.color === _this._turn)); });
if (found.length !== 1) {
console.warn("Can not find move " + san + " in " + movesValid.map(function (m) { return m.san; }).join(','));
throw new Error("The SAN " + san + " is not valid.");
}
var move = found[0];
if (move.promotion && sanMove.promotion) {
move.promotion = sanMove.promotion.toLowerCase();
}
return this.performMove(move, false);
};
ChessGame.prototype.performMove = function (move, validate) {
var _this = this;
if (validate) {
var movesValid = this.moves({ square: move.from, color: this._turn })
.filter(function (m) { return m.from === move.from && m.to === move.to && m.color === _this._turn; });
if (movesValid.length !== 1) {
throw new Error("Move from " + move.from + " to " + move.to + " is not valid.");
}
move = __assign({}, movesValid[0], { promotion: move.promotion || movesValid[0].promotion });
}
if (!(move.from in INDEXES) || !(move.to in INDEXES)) {
throw new Error("Invalid move " + move.from + " " + move.to + " (off table).");
}
var promotion = move.promotion;
if (move.piece === constants_1.PAWN && promotion) {
promotion = typeof promotion === STRING ? promotion.toLowerCase() : constants_1.NONE;
if (PROMOTIONS.indexOf(promotion) < 0) {
throw new Error("Invalid promotion piece \"" + move.promotion + "\".");
}
promotion = move.promotion = this._turn === constants_1.WHITE ? promotion.toUpperCase() : promotion;
}
var moveData = __assign({}, move, { restoreFEN: this.fen() });
var sourceIndex = INDEXES[move.from];
var sourceOffset = chessUtils_1.indexToOffset(sourceIndex);
var targetIndex = INDEXES[move.to];
var targetOffset = chessUtils_1.indexToOffset(targetIndex);
var movingPiece = this._board[sourceIndex];
if (move.piece === constants_1.PAWN && move.captured === constants_1.PAWN && move.enpass) {
var killSquare = {
y: targetOffset.y > sourceOffset.y ? targetOffset.y - 1 : targetOffset.y + 1,
x: targetOffset.x
};
var killIndex = chessUtils_1.offsetToIndex(killSquare);
var pieceRemoved = this._board[killIndex];
if (COLORPIECES[pieceRemoved] !== "" + (this._turn === constants_1.WHITE ? constants_1.BLACK : constants_1.WHITE) + constants_1.PAWN) {
throw new Error("Invalid enpass capture " + COLORPIECES[pieceRemoved] + " at " + chessUtils_1.indexToSquare(chessUtils_1.offsetToIndex(killSquare)) + ".");
}
this._board[killIndex] = constants_1.NONE;
}
this._board[sourceIndex] = constants_1.NONE;
this._board[targetIndex] = movingPiece;
if (move.piece === constants_1.PAWN && promotion) {
this._board[targetIndex] = promotion;
}
if (move.piece === constants_1.KING && move.castle && move.castle in INDEXES) {
var castleIx = INDEXES[move.castle];
var rookPiece = (this._turn === constants_1.WHITE ? constants_1.ROOK.toUpperCase() : constants_1.ROOK);
var queenSide = move.castle < move.from;
var rookToIx = targetIndex + (queenSide ? 1 : -1);
this._board[castleIx] = (this._board[castleIx] === rookPiece ? constants_1.NONE : rookPiece);
this._board[rookToIx] = rookPiece;
}
if (movingPiece === constants_1.WKING) {
this._whiteKing = targetIndex;
this._castles = (this._castles || EMPTY_STRING).replace(PATTERN_UPPER_CASE, EMPTY_STRING);
}
else if (movingPiece === constants_1.BKING) {
this._blackKing = targetIndex;
this._castles = (this._castles || EMPTY_STRING).replace(PATTERN_LOWER_CASE, EMPTY_STRING);
}
if (movingPiece.toLowerCase() === constants_1.ROOK && this._castles && this._castles !== constants_1.NONE) {
var _a = move.from.split(EMPTY_STRING), fromRank = _a[0], fromFile = _a[1];
var startFile = this._turn === constants_1.WHITE ? '1' : String.fromCharCode(letter0Code + constants_1.BOARD_HEIGHT);
var valid = this._castles;
if (startFile === fromFile) {
for (var _i = 0, _b = this._castles; _i < _b.length; _i++) {
var ch = _b[_i];
if (chessUtils_1.isPieceColor(ch, this._turn)) {
var test_1 = ch.toLowerCase();
if (constants_1.BOARD_WIDTH > 10) { }
else if (test_1 === constants_1.KING)
test_1 = String.fromCharCode(lowerACode + constants_1.BOARD_WIDTH);
else if (test_1 === constants_1.QUEEN)
test_1 = lowerA;
if (test_1 === fromRank) {
this._castles = this._castles.replace(ch, EMPTY_STRING);
break;
}
}
}
}
}
this._enpass = -1;
if (move.piece === constants_1.PAWN) {
var delta = chessUtils_1.deltaOffsets(sourceOffset, targetOffset);
if (delta.y === 2) {
this._enpass = chessUtils_1.offsetToIndex({
x: sourceOffset.x,
y: sourceOffset.y + (targetOffset.y > sourceOffset.y ? 1 : -1)
});
}
}
this._halfmoveClock = (move.captured || move.piece === constants_1.PAWN) ? 0 : this._halfmoveClock + 1;
if (this._turn === constants_1.WHITE) {
this._turn = constants_1.BLACK;
}
else {
this._turn = constants_1.WHITE;
this._moveNumber++;
}
this._history.push(moveData);
this.updated();
return __assign({}, moveData);
};
ChessGame.prototype.canMoveTo = function (targetOffset, color) {
if (!chessUtils_1.offsetValid(targetOffset))
return;
var result = __assign({}, targetOffset);
var occupied = this._board[chessUtils_1.offsetToIndex(targetOffset)];
if (occupied in COLORPIECES) {
var occupiedColor = COLORPIECES[occupied][0];
if (occupiedColor === color)
return;
result.captured = occupied.toLowerCase();
}
return result;
};
ChessGame.prototype.getMovesBySquare = function (options, color, piece) {
var squareIx = options.squareIx, simple = options.simple, captures = options.captures;
var startOffset = chessUtils_1.indexToOffset(squareIx);
var targets = [];
var moveTypes = chessMoves_1.MoveTable[piece];
if (!moveTypes) {
moveTypes = chessMoves_1.MoveTable["" + color + piece];
}
for (var mix = 0; mix < moveTypes.length; mix++) {
var mv = moveTypes[mix];
for (var mul = 1; mul < constants_1.BOARD_HEIGHT; mul++) {
var targetOffset = chessUtils_1.addOffsets(startOffset, { x: mv.x * mul, y: mv.y * mul });
var move = this.canMoveTo(targetOffset, color);
if (!move)
break;
var condition = false;
if (mv.when === chessMoves_1.MoveCriteria.EmptyAndStart && !move.captured) {
if (color === constants_1.WHITE) {
var before_1 = this.canMoveTo({ x: targetOffset.x, y: startOffset.y - 1 }, color);
condition = startOffset.y === 6 && !!before_1 && !before_1.captured;
}
else {
var before_2 = this.canMoveTo({ x: targetOffset.x, y: startOffset.y + 1 }, color);
condition = startOffset.y === 1 && !!before_2 && !before_2.captured;
}
}
else if (mv.when === chessMoves_1.MoveCriteria.Empty && !move.captured) {
condition = true;
}
else if (mv.when === chessMoves_1.MoveCriteria.Attacking) {
if (move.captured)
condition = true;
else if (piece === constants_1.PAWN && chessUtils_1.offsetToIndex(targetOffset) === this._enpass) {
condition = true;
move.captured = constants_1.PAWN;
move.enpass = true;
}
}
else if (!mv.when) {
condition = true;
}
if (condition) {
move.color = color;
move.piece = piece;
targets.push(move);
}
if (move.captured || !mv.repeat)
break;
}
}
if (captures) {
return targets.filter(function (m) { return m.captured; });
}
if (simple) {
return targets;
}
if (piece === constants_1.KING && (this._castles || constants_1.NONE) !== constants_1.NONE) {
var moves = this.getCastleMoves(color);
for (var _i = 0, moves_1 = moves; _i < moves_1.length; _i++) {
var move = moves_1[_i];
targets.push(move);
}
}
return targets.map(function (t) { return (__assign({ from: chessUtils_1.indexToSquare(squareIx), to: chessUtils_1.indexToSquare(chessUtils_1.offsetToIndex(t)) }, t)); });
};
ChessGame.prototype.getCastleMoves = function (color) {
var results = [];
if (constants_1.NONE === (this._castles || constants_1.NONE)) {
return results;
}
for (var _i = 0, _a = this._castles; _i < _a.length; _i++) {
var sqChar = _a[_i];
if (!chessUtils_1.isPieceColor(sqChar, color))
continue;
var castleFile = sqChar.toLowerCase();
castleFile = castleFile === constants_1.QUEEN ? lowerA :
castleFile === constants_1.KING ? String.fromCharCode(lowerACode + constants_1.BOARD_WIDTH - 1) : castleFile;
var kingIndex = color === constants_1.WHITE ? this._whiteKing : this._blackKing;
var kingOffset = chessUtils_1.indexToOffset(kingIndex);
var castleOffset = { x: castleFile.charCodeAt(0) - lowerACode, y: kingOffset.y };
var move = this.getCastle(color, castleOffset);
if (move) {
results.push(move);
}
}
return results;
};
ChessGame.prototype.getCastle = function (color, castleOffset) {
var kingIndex = color === constants_1.WHITE ? this._whiteKing : this._blackKing;
var kingOffset = chessUtils_1.indexToOffset(kingIndex);
var isQueenSide = castleOffset.x < kingOffset.x;
var fromX = Math.min(kingOffset.x, castleOffset.x, isQueenSide ? constants_1.CASTLE_QUEEN_OFFSETX : constants_1.CASTLE_KING_OFFSETX);
var toX = Math.max(kingOffset.x, castleOffset.x, isQueenSide ? constants_1.CASTLE_QUEEN_OFFSETX : constants_1.CASTLE_KING_OFFSETX);
var testIndex = chessUtils_1.offsetToIndex({ y: kingOffset.y, x: fromX });
for (var ix = fromX; ix <= toX; ix++, testIndex++) {
var occupied = this._board[testIndex];
if (occupied === constants_1.NONE)
continue;
if (occupied === constants_1.WKING || occupied === constants_1.BKING)
continue;
if (occupied.toLowerCase() === constants_1.ROOK && testIndex === chessUtils_1.offsetToIndex(castleOffset))
continue;
return null;
}
fromX = Math.min(kingOffset.x, isQueenSide ? constants_1.CASTLE_QUEEN_OFFSETX : constants_1.CASTLE_KING_OFFSETX);
toX = Math.max(kingOffset.x, isQueenSide ? constants_1.CASTLE_QUEEN_OFFSETX : constants_1.CASTLE_KING_OFFSETX);
testIndex = chessUtils_1.offsetToIndex({ y: kingOffset.y, x: fromX });
for (var ix = fromX; ix <= toX; ix++, testIndex++) {
if (this.isSquareAttacked(testIndex, color)) {
return null;
}
}
return {
color: color,
piece: constants_1.KING,
x: isQueenSide ? constants_1.CASTLE_QUEEN_OFFSETX : constants_1.CASTLE_KING_OFFSETX,
y: kingOffset.y,
castle: chessUtils_1.indexToSquare(chessUtils_1.offsetToIndex(castleOffset)),
};
};
ChessGame.prototype.getMovesFrom = function (options) {
var squareIx = options.squareIx;
var boardPiece = this._board[squareIx];
if (typeof boardPiece !== STRING || boardPiece === constants_1.NONE) {
return EMPTY_ARRAY;
}
var colorPiece = COLORPIECES[boardPiece];
var color = colorPiece[0];
var piece = colorPiece[1];
return this.getMovesBySquare(options, color, piece);
};
ChessGame.prototype.isValidAttack = function (move, offset, color) {
if (move.captured === constants_1.KING) {
var dist = chessUtils_1.deltaOffsets(offset, move);
return dist.x <= 1 && dist.y <= 1;
}
if (move.captured === constants_1.PAWN) {
if (color === constants_1.WHITE) {
return offset.y === move.y + 1 && 1 === Math.abs(offset.x - move.x);
}
else {
return offset.y === move.y - 1 && 1 === Math.abs(offset.x - move.x);
}
}
if (move.captured === constants_1.BISHOP) {
var dist = chessUtils_1.deltaOffsets(offset, move);
return dist.x === dist.y;
}
if (move.captured === constants_1.ROOK) {
var dist = chessUtils_1.deltaOffsets(offset, move);
return dist.x === 0 || dist.y === 0;
}
return true;
};
ChessGame.prototype.isSquareAttacked = function (squareIx, color) {
var startOffset = chessUtils_1.indexToOffset(squareIx);
var lineAttacks = this.getMovesBySquare({ squareIx: squareIx, simple: true, captures: true }, color, constants_1.QUEEN);
for (var _i = 0, lineAttacks_1 = lineAttacks; _i < lineAttacks_1.length; _i++) {
var move = lineAttacks_1[_i];
if (move.captured && move.captured !== constants_1.KNIGHT && this.isValidAttack(move, startOffset, color)) {
return true;
}
}
var knightAttacks = this.getMovesBySquare({ squareIx: squareIx, simple: true, captures: true }, color, constants_1.KNIGHT);
for (var _a = 0, knightAttacks_1 = knightAttacks; _a < knightAttacks_1.length; _a++) {
var move = knightAttacks_1[_a];
if (move.captured === constants_1.KNIGHT && this.isValidAttack(move, startOffset, color)) {
return true;
}
}
return false;
};
ChessGame.prototype.moves = function (options) {
options = options || {};
var _a = options, square = _a.square, exists = _a.exists;
if (square && !(square in INDEXES)) {
throw new Error("Invalid square identifier: \"" + square + "\"");
}
if (square) {
var result = [];
this.getMoves(result, __assign({ squareIx: INDEXES[square] }, options));
return result;
}
else {
var result = [];
var color = options.color || this._turn;
for (var squareIx = 0; squareIx < constants_1.BOARD_SIZE; squareIx++) {
var boardPiece = this._board[squareIx];
if (chessUtils_1.isPieceColor(boardPiece, color)) {
this.getMoves(result, __assign({ squareIx: squareIx }, options));
if (exists && result.length) {
return result;
}
}
}
return result;
}
};
ChessGame.prototype.getMoves = function (result, options) {
var squareIx = options.squareIx;
var possible = this.getMovesFrom(options);
if (possible.length === 0)
return;
var movingPiece = this._board[squareIx];
var colorPiece = COLORPIECES[movingPiece];
var color = colorPiece[0];
var opponentColor = color === constants_1.WHITE ? constants_1.BLACK : constants_1.WHITE;
var piece = colorPiece[1];
var _a = this, _whiteKing = _a._whiteKing, _blackKing = _a._blackKing;
for (var _i = 0, possible_1 = possible; _i < possible_1.length; _i++) {
var move = possible_1[_i];
var isLegal = false;
var targetIx = chessUtils_1.offsetToIndex(move);
var restore = [targetIx, this._board[targetIx], squareIx, this._board[squareIx]];
this._board[squareIx] = constants_1.NONE;
if (this._enpass >= 0 && move.enpass) {
var killSquare = chessUtils_1.offsetToIndex({ y: move.y === 2 ? move.y + 1 : move.y - 1, x: move.x });
restore.push(killSquare, this._board[killSquare]);
this._board[killSquare] = constants_1.NONE;
}
else if (move.castle && move.castle in INDEXES) {
var castleIx = INDEXES[move.castle];
var rookPiece = (this._turn === constants_1.WHITE ? constants_1.ROOK.toUpperCase() : constants_1.ROOK);
var queenSide = move.castle < move.from;
var rookToIx = targetIx + (queenSide ? 1 : -1);
restore.push(castleIx, this._board[castleIx]);
restore.push(rookToIx, this._board[rookToIx]);
this._board[castleIx] = constants_1.NONE;
this._board[rookToIx] = rookPiece;
}
try {
this._board[targetIx] = movingPiece;
if (movingPiece === constants_1.WKING) {
this._whiteKing = targetIx;
}
else if (movingPiece === constants_1.BKING) {
this._blackKing = targetIx;
}
if (!this.testForCheck(color)) {
if (this.testForCheck(opponentColor)) {
move.check = constants_1.PLUS;
if (!this.hasValidMoves(opponentColor)) {
move.check = constants_1.HASH;
}
}
isLegal = true;
}
}
finally {
for (var ix = 0; ix < restore.length; ix += 2) {
this._board[restore[ix]] = restore[ix + 1];
}
this._whiteKing = _whiteKing;
this._blackKing = _blackKing;
}
if (isLegal) {
if (options.exists) {
result.push({});
return;
}
if (move.piece === constants_1.PAWN && (move.y === 0 || move.y === constants_1.BOARD_HEIGHT - 1)) {
move.promotion = constants_1.QUEEN;
}
if (!options.simple) {
var pieceType = movingPiece.toLowerCase();
if (pieceType === constants_1.KING || (pieceType === constants_1.PAWN && !move.captured)) {
move.san = chessUtils_1.buildSAN(move, EMPTY_ARRAY);
}
else {
var disambiguation = this.disambiguateFrom(targetIx, pieceType, opponentColor);
move.san = chessUtils_1.buildSAN(move, disambiguation);
}
}
var x = move.x, y = move.y, resultMove = __rest(move, ["x", "y"]);
result.push(resultMove);
}
}
};
ChessGame.prototype.disambiguateFrom = function (squareIx, piece, color) {
var results = [];
var lineAttacks = this.getMovesBySquare({ squareIx: squareIx, simple: true, captures: true }, color, piece);
for (var _i = 0, lineAttacks_2 = lineAttacks; _i < lineAttacks_2.length; _i++) {
var move = lineAttacks_2[_i];
if (move.captured === piece) {
results.push(chessUtils_1.indexToSquare(chessUtils_1.offsetToIndex(move)));
}
}
return results;
};
ChessGame.prototype.testForCheck = function (color) {
var kingSquareIx = color === constants_1.WHITE ? this._whiteKing : this._blackKing;
return kingSquareIx < 0 || this.isSquareAttacked(kingSquareIx, color);
};
ChessGame.prototype.hasValidMoves = function (color) {
return this.moves({ color: color, simple: true, exists: true }).length > 0;
};
ChessGame.prototype.getResult = function () {
return this._gameResult;
};
ChessGame.prototype.assertOngoing = function () {
if (!this.isOngoing()) {
throw new Error("Invalid operation, game is over: " + this.getResult() + ".");
}
};
ChessGame.prototype.isInCheck = function () {
this.updateCachedState();
return this._checked;
};
ChessGame.prototype.isCheckmate = function () {
this.updateCachedState();
return this._checked && this._noValidMoves;
};
ChessGame.prototype.isStalemate = function () {
this.updateCachedState();
return !this._checked && this._noValidMoves;
};
ChessGame.prototype.isAutomaticDraw = function () {
if (this._whiteKing < 0 || this._blackKing < 0) {
return true;
}
var boardString = this._board.join(EMPTY_STRING);
var blackPieces = (boardString.match(PATTERN_LOWER_CASE) || []).sort().join(EMPTY_STRING);
var whitePieces = (boardString.match(PATTERN_UPPER_CASE) || []).sort().join(EMPTY_STRING).toLowerCase();
if (blackPieces === constants_1.KING || whitePieces === constants_1.KING) {
var morePieces = whitePieces.length > blackPieces.length ? whitePieces : blackPieces;
if (morePieces === constants_1.KING || morePieces === constants_1.BISHOP + constants_1.KING || morePieces === constants_1.KING + constants_1.KNIGHT) {
return true;
}
}
if (blackPieces === whitePieces && whitePieces === constants_1.BISHOP + constants_1.KING) {
var colorBishop1 = chessUtils_1.squareColor(boardString.indexOf(constants_1.BISHOP.toUpperCase()));
var colorBishop2 = chessUtils_1.squareColor(boardString.indexOf(constants_1.BISHOP));
if (colorBishop1 === colorBishop2) {
return true;
}
}
return false;
};
ChessGame.prototype.hasThreefoldRepetition = function () {
var found = {};
for (var _i = 0, _a = this._history; _i < _a.length; _i++) {
var move = _a[_i];
if (move.restoreFEN) {
var positions = move.restoreFEN.split(' ')[0];
var count = found[positions] = (found[positions] || 0) + 1;
if (count >= 3) {
return true;
}
}
}
return false;
};
ChessGame.prototype.hasExceededFiftyMoveRule = function () {
return this._halfmoveClock >= 100;
};
ChessGame.prototype.canDraw = function () {
return this.isOngoing() && (this.hasThreefoldRepetition() ||
this.hasExceededFiftyMoveRule());
};
ChessGame.prototype.setDraw = function () {
this.assertOngoing();
this._headers.Result = constants_1.DRAW;
this._gameResult = constants_1.DRAW;
};
ChessGame.prototype.isDraw = function () {
this.updateCachedState();
return this.getResult() === constants_1.DRAW;
};
ChessGame.prototype.isOngoing = function () {
this.updateCachedState();
return this.getResult() === constants_1.ONGOING;
};
return ChessGame;
}());
exports.ChessGame = ChessGame;
exports.default = ChessGame;
//# sourceMappingURL=chessGame.js.map