UNPKG

@chess-fu/chess-game

Version:
820 lines 37.3 kB
"use strict"; 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