kokopu
Version:
A JavaScript/TypeScript library implementing the chess game rules and providing tools to read/write the standard chess file formats.
661 lines • 31.4 kB
JavaScript
"use strict";
/*!
* -------------------------------------------------------------------------- *
* *
* Kokopu - A JavaScript/TypeScript chess library. *
* <https://www.npmjs.com/package/kokopu> *
* Copyright (C) 2018-2025 Yoann Le Montagner <yo35 -at- melix.net> *
* *
* Kokopu is free software: you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public License *
* as published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version. *
* *
* Kokopu is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General *
* Public License along with this program. If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* -------------------------------------------------------------------------- */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Position = void 0;
const exception_1 = require("./exception");
const helper_1 = require("./helper");
const i18n_1 = require("./i18n");
const attacks_1 = require("./private_position/attacks");
const base_types_impl_1 = require("./private_position/base_types_impl");
const fen_1 = require("./private_position/fen");
const impl_1 = require("./private_position/impl");
const legality_1 = require("./private_position/legality");
const move_descriptor_impl_1 = require("./private_position/move_descriptor_impl");
const move_generation_1 = require("./private_position/move_generation");
const notation_1 = require("./private_position/notation");
const uci_1 = require("./private_position/uci");
/**
* Represent a chess position, i.e.:
* - the state of a 64-square chessboard,
* - who is about to play,
* - the castling rights,
* - and the file on which *en-passant* is possible, if any.
*/
class Position {
constructor(arg0, arg1) {
switch (arguments.length) {
// Default constructor
case 0:
this._impl = (0, impl_1.makeInitial)(0 /* GameVariantImpl.REGULAR_CHESS */);
break;
// Possible overloads with 1 argument:
// - 'start'
// - 'empty'
// - Position
// - GameVariant (valid only for variants with a canonical starting position)
// - FEN
// - GameVariant:FEN
case 1: {
if (arg0 === 'start') {
this._impl = (0, impl_1.makeInitial)(0 /* GameVariantImpl.REGULAR_CHESS */);
}
else if (arg0 === 'empty') {
this._impl = (0, impl_1.makeEmpty)(0 /* GameVariantImpl.REGULAR_CHESS */);
}
else if (arg0 instanceof Position) {
this._impl = (0, impl_1.makeCopy)(arg0._impl);
}
else {
const variantCode = (0, base_types_impl_1.variantFromString)(arg0);
if (variantCode >= 0) {
if (!(0, impl_1.hasCanonicalStartPosition)(variantCode)) {
throw new exception_1.IllegalArgument('Position()');
}
this._impl = (0, impl_1.makeInitial)(variantCode);
}
else if (typeof arg0 === 'string') {
const separatorIndex = arg0.indexOf(':');
if (separatorIndex < 0) {
this._impl = (0, fen_1.parseFEN)(0 /* GameVariantImpl.REGULAR_CHESS */, arg0, false).position;
}
else {
const variantPrefix = arg0.substring(0, separatorIndex);
const variantPrefixCode = (0, base_types_impl_1.variantFromString)(variantPrefix);
if (variantPrefixCode < 0) {
throw new exception_1.InvalidFEN(arg0, i18n_1.i18n.INVALID_VARIANT_PREFIX, variantPrefix);
}
this._impl = (0, fen_1.parseFEN)(variantPrefixCode, arg0.substring(separatorIndex + 1), false).position;
}
}
else {
throw new exception_1.IllegalArgument('Position()');
}
}
break;
}
// Possible overloads with 2 arguments:
// - (GameVariant, 'start') (valid only for variants with a canonical starting position)
// - (GameVariant, 'empty')
// - (GameVariant, scharnaglCode) (valid only for Chess960)
// - (GameVariant, FEN)
default: {
const variantCode = (0, base_types_impl_1.variantFromString)(arg0);
if (variantCode < 0) {
throw new exception_1.IllegalArgument('Position()');
}
if (arg1 === 'start') {
if (!(0, impl_1.hasCanonicalStartPosition)(variantCode)) {
throw new exception_1.IllegalArgument('Position()');
}
this._impl = (0, impl_1.makeInitial)(variantCode);
}
else if (arg1 === 'empty') {
this._impl = (0, impl_1.makeEmpty)(variantCode);
}
else if (typeof arg1 === 'number') {
if (variantCode !== 1 /* GameVariantImpl.CHESS960 */ || !isValidScharnaglCode(arg1)) {
throw new exception_1.IllegalArgument('Position()');
}
this._impl = (0, impl_1.make960FromScharnagl)(arg1);
}
else if (typeof arg1 === 'string') {
this._impl = (0, fen_1.parseFEN)(variantCode, arg1, false).position;
}
else {
throw new exception_1.IllegalArgument('Position()');
}
break;
}
}
}
/**
* Set the position to the empty state, for the given chess game variant.
*/
clear(variant = 'regular') {
const variantCode = (0, base_types_impl_1.variantFromString)(variant);
if (variantCode < 0) {
throw new exception_1.IllegalArgument('Position.clear()');
}
this._impl = (0, impl_1.makeEmpty)(variantCode);
}
/**
* Set the position to the starting state (in the regular chess variant).
*/
reset() {
this._impl = (0, impl_1.makeInitial)(0 /* GameVariantImpl.REGULAR_CHESS */);
}
/**
* Set the position to Chess960 starting position corresponding to the given Scharnagl code.
*
* @param scharnaglCode - Must be between 0 and 959 inclusive (see https://chess960.net/start-positions/
* or https://www.chessprogramming.org/Reinhard_Scharnagl for more details).
*/
reset960(scharnaglCode) {
if (!isValidScharnaglCode(scharnaglCode)) {
throw new exception_1.IllegalArgument('Position.reset960()');
}
this._impl = (0, impl_1.make960FromScharnagl)(scharnaglCode);
}
/**
* Set the position to the starting state of the antichess variant.
*/
resetAntichess() {
this._impl = (0, impl_1.makeInitial)(5 /* GameVariantImpl.ANTICHESS */);
}
/**
* Set the position to the starting state of the horde chess variant.
*/
resetHorde() {
this._impl = (0, impl_1.makeInitial)(6 /* GameVariantImpl.HORDE */);
}
/**
* Check whether both given objects represent the same chess position (i.e. the same chess variant, same board,
* and same turn/castling/en-passant flags).
*/
static isEqual(pos1, pos2) {
return pos1 instanceof Position && pos2 instanceof Position && (0, legality_1.isEqual)(pos1._impl, pos2._impl);
}
// -------------------------------------------------------------------------
// FEN & ASCII conversion
// -------------------------------------------------------------------------
/**
* Return a human-readable string representing the position. This string is multi-line,
* and is intended to be displayed in a fixed-width font (similarly to an ASCII-art picture).
* For instance:
*
* ```
* const position = new Position();
* console.log(position.ascii({ coordinateVisible: true }));
*
* // +---+---+---+---+---+---+---+---+
* // 8 | r | n | b | q | k | b | n | r |
* // +---+---+---+---+---+---+---+---+
* // 7 | p | p | p | p | p | p | p | p |
* // +---+---+---+---+---+---+---+---+
* // 6 | | | | | | | | |
* // +---+---+---+---+---+---+---+---+
* // 5 | | | | | | | | |
* // +---+---+---+---+---+---+---+---+
* // 4 | | | | | | | | |
* // +---+---+---+---+---+---+---+---+
* // 3 | | | | | | | | |
* // +---+---+---+---+---+---+---+---+
* // 2 | P | P | P | P | P | P | P | P |
* // +---+---+---+---+---+---+---+---+
* // 1 | R | N | B | Q | K | B | N | R |
* // +---+---+---+---+---+---+---+---+
* // a b c d e f g h
* // w KQkq -
* ```
*/
ascii(options) {
return (0, fen_1.ascii)(this._impl, options ?? {});
}
fen(fenOrOptions, strict) {
// Getter, without options.
if (arguments.length === 0) {
return (0, fen_1.getFEN)(this._impl);
}
// Getter, with options.
else if (arguments.length === 1 && typeof fenOrOptions === 'object') {
const validate = buildValidator(fenOrOptions, 'Position.fen()');
const fiftyMoveClock = validate('fiftyMoveClock', 0, val => Number.isInteger(val));
const fullMoveNumber = validate('fullMoveNumber', 1, val => Number.isInteger(val));
const withVariant = validate('withVariant', false, val => typeof val === 'boolean');
const regularFENIfPossible = validate('regularFENIfPossible', false, val => typeof val === 'boolean');
return (withVariant ? (0, base_types_impl_1.variantToString)(this._impl.variant) + ':' : '') + (0, fen_1.getFEN)(this._impl, fiftyMoveClock, fullMoveNumber, regularFENIfPossible);
}
// Setter, without strict option.
else if (arguments.length === 1 && typeof fenOrOptions === 'string') {
const result = (0, fen_1.parseFEN)(this._impl.variant, fenOrOptions, false);
this._impl = result.position;
return { fiftyMoveClock: result.fiftyMoveClock, fullMoveNumber: result.fullMoveNumber };
}
// Setter, with strict option.
else if (arguments.length >= 2 && typeof fenOrOptions === 'string' && typeof strict === 'boolean') {
const result = (0, fen_1.parseFEN)(this._impl.variant, fenOrOptions, strict);
this._impl = result.position;
return { fiftyMoveClock: result.fiftyMoveClock, fullMoveNumber: result.fullMoveNumber };
}
// Unsupported overload.
else {
throw new exception_1.IllegalArgument('Position.fen()');
}
}
// -------------------------------------------------------------------------
// Accessors
// -------------------------------------------------------------------------
/**
* Get the chess game variant in use.
*/
variant() {
return (0, base_types_impl_1.variantToString)(this._impl.variant);
}
square(square, value) {
const squareCode = (0, base_types_impl_1.squareFromString)(square);
if (squareCode < 0) {
throw new exception_1.IllegalArgument('Position.square()');
}
if (arguments.length === 1) {
const cp = this._impl.board[squareCode];
return cp === -1 /* SpI.EMPTY */ ? '-' : (0, base_types_impl_1.coloredPieceToString)(cp);
}
else if (value === '-') {
this._impl.board[squareCode] = -1 /* SpI.EMPTY */;
this._impl.legal = null;
this._impl.effectiveCastling = null;
this._impl.effectiveEnPassant = null;
}
else {
const cp = (0, base_types_impl_1.coloredPieceFromString)(value);
if (cp < 0) {
throw new exception_1.IllegalArgument('Position.square()');
}
this._impl.board[squareCode] = cp;
this._impl.legal = null;
this._impl.effectiveCastling = null;
this._impl.effectiveEnPassant = null;
}
}
turn(value) {
if (arguments.length === 0) {
return (0, base_types_impl_1.colorToString)(this._impl.turn);
}
else {
const colorCode = (0, base_types_impl_1.colorFromString)(value);
if (colorCode < 0) {
throw new exception_1.IllegalArgument('Position.turn()');
}
this._impl.turn = colorCode;
this._impl.legal = null;
this._impl.effectiveEnPassant = null;
}
}
castling(castle, value) {
if (!(this._impl.variant === 1 /* GameVariantImpl.CHESS960 */ ? (0, helper_1.isCastle960)(castle) : (0, helper_1.isCastle)(castle))) {
throw new exception_1.IllegalArgument('Position.castling()');
}
const color = (0, base_types_impl_1.colorFromString)(castle[0]);
const file = this._impl.variant === 1 /* GameVariantImpl.CHESS960 */ ? (0, base_types_impl_1.fileFromString)(castle[1]) : castle[1] === 'k' ? 7 : 0;
if (arguments.length === 1) {
return (this._impl.castling[color] & 1 << file) !== 0;
}
else if (typeof value === 'boolean') {
if (value) {
this._impl.castling[color] |= 1 << file;
}
else {
this._impl.castling[color] &= ~(1 << file);
}
this._impl.effectiveCastling = null;
}
else {
throw new exception_1.IllegalArgument('Position.castling()');
}
}
/**
* Get a validated (aka. effective) castle flag (i.e. whether or not the corresponding castle is allowed or not).
*
* Compared to {@link Position.castling}, if this method returns `true`, then it is guaranteed that there are a king and a rook on the squares
* corresponding to the given castle.
*
* @param castle - Must be {@link Castle960} if the {@link Position} is configured for Chess960, or {@link Castle} otherwise.
*/
effectiveCastling(castle) {
if (!(this._impl.variant === 1 /* GameVariantImpl.CHESS960 */ ? (0, helper_1.isCastle960)(castle) : (0, helper_1.isCastle)(castle))) {
throw new exception_1.IllegalArgument('Position.effectiveCastling()');
}
const color = (0, base_types_impl_1.colorFromString)(castle[0]);
const file = this._impl.variant === 1 /* GameVariantImpl.CHESS960 */ ? (0, base_types_impl_1.fileFromString)(castle[1]) : castle[1] === 'k' ? 7 : 0;
(0, legality_1.refreshEffectiveCastling)(this._impl);
return (this._impl.effectiveCastling[color] & 1 << file) !== 0;
}
enPassant(value) {
if (arguments.length === 0) {
return this._impl.enPassant < 0 ? '-' : (0, base_types_impl_1.fileToString)(this._impl.enPassant);
}
else if (value === '-') {
this._impl.enPassant = -1;
this._impl.effectiveEnPassant = -1;
}
else {
const enPassantCode = (0, base_types_impl_1.fileFromString)(value);
if (enPassantCode < 0) {
throw new exception_1.IllegalArgument('Position.enPassant()');
}
this._impl.enPassant = enPassantCode;
this._impl.effectiveEnPassant = null;
}
}
/**
* Get the effective *en-passant* flag (i.e. the column on which a *en-passant* capture is possible, if any).
*
* If {@link Position.enPassant} returns `'-'`, this method returns `'-'`. Otherwise, it returns:
* - either the same file that is returned by {@link Position.enPassant} if a *en-passant* capture is allowed on this file,
* - or `'-'` otherwise.
*/
effectiveEnPassant() {
(0, legality_1.refreshEffectiveEnPassant)(this._impl);
return this._impl.effectiveEnPassant < 0 ? '-' : (0, base_types_impl_1.fileToString)(this._impl.effectiveEnPassant);
}
// -------------------------------------------------------------------------
// Attacks
// -------------------------------------------------------------------------
/**
* Check if any piece of the given color attacks the given square.
*/
isAttacked(square, byWho) {
const squareCode = (0, base_types_impl_1.squareFromString)(square);
const byWhoCode = (0, base_types_impl_1.colorFromString)(byWho);
if (squareCode < 0 || byWhoCode < 0) {
throw new exception_1.IllegalArgument('Position.isAttacked()');
}
return (0, attacks_1.isAttacked)(this._impl, squareCode, byWhoCode);
}
/**
* Return the squares from which a piece of the given color attacks the given square.
*/
getAttacks(square, byWho) {
const squareCode = (0, base_types_impl_1.squareFromString)(square);
const byWhoCode = (0, base_types_impl_1.colorFromString)(byWho);
if (squareCode < 0 || byWhoCode < 0) {
throw new exception_1.IllegalArgument('Position.getAttacks()');
}
return (0, attacks_1.getAttacks)(this._impl, squareCode, byWhoCode).map(base_types_impl_1.squareToString);
}
// -------------------------------------------------------------------------
// Legality
// -------------------------------------------------------------------------
/**
* Check whether the current position is legal or not.
*
* A position is considered to be legal if all the following conditions are met:
*
* 1. There is exactly one white king and one black king on the board (or more generally,
* the number of kings on the board matches what is expected in the game variant of the position).
* 2. The player that is not about to play is not in check (this condition is omitted for variants
* in which kings have no royal power).
* 3. There are no pawn on ranks 1 and 8 (except if the game variant of the position allows it).
*/
isLegal() {
return (0, legality_1.isLegal)(this._impl);
}
/**
* Return the square on which is located the king of the given color. If there is no such king on the board
* (or on the contrary, if there are two or more such kings on the board), `false` is returned.
*
* For non-standard variants, the behavior of this method depends on whether king has royal power in the current variant or not
* (i.e. whether it can be put in check or not). For instance:
* - in antichess, the king has no royal power, thus `false` is always returned,
* - in Chess960, the king has royal power (as in the usual chess rules), thus the method returns the square on which the king is located.
*/
kingSquare(color) {
const colorCode = (0, base_types_impl_1.colorFromString)(color);
if (colorCode < 0) {
throw new exception_1.IllegalArgument('Position.kingSquare()');
}
(0, legality_1.refreshLegalFlagAndKingSquares)(this._impl);
const squareCode = this._impl.king[colorCode];
return squareCode < 0 ? false : (0, base_types_impl_1.squareToString)(squareCode);
}
// -------------------------------------------------------------------------
// Move generation
// -------------------------------------------------------------------------
/**
* Whether the player that is about to play is in check or not. If the position is not legal (see {@link Position.isLegal}),
* the returned value is always `false`.
*
* For antichess, this method always returns `false`.
*/
isCheck() {
return (0, move_generation_1.isCheck)(this._impl);
}
/**
* Whether the player that is about to play is checkmated or not. If the position is not legal (see {@link Position.isLegal}),
* the returned value is always `false`.
*
* For antichess, this method returns `true` if the player about to play has no remaining piece or pawn,
* or if non of his/her remaining pieces can move (i.e. same behavior as {@link Position.isStalemate} for this variant).
*
* For horde chess, this method returns `true` if black has been checkmated or if white has no remaining piece.
*/
isCheckmate() {
return (0, move_generation_1.isCheckmate)(this._impl);
}
/**
* Whether the player that is about to play is stalemated or not. If the position is not legal (see {@link Position.isLegal}),
* the returned value is always `false`.
*
* For antichess, this method returns `true` if the player about to play has no remaining piece or pawn,
* or if non of his/her remaining pieces can move (i.e. same behavior as {@link Position.isCheckmate} for this variant).
*
* For horde chess, this method returns `true` if black has been stalemated or if white cannot move but has still at least one piece.
*/
isStalemate() {
return (0, move_generation_1.isStalemate)(this._impl);
}
/**
* Whether both players have insufficient material so the game cannot end in checkmate ([dead position rule](https://en.wikipedia.org/wiki/Rules_of_chess#Dead_position)).
* If the position is not legal (see {@link Position.isLegal}), the returned value is always `false`.
*
* By default, the method uses the FIDE rules, i.e. the position is considered as dead if there is no possible checkmate, even if both players
* cooperate for that. This is different from the USCF rules, for which the position is considered as dead if no player can force the other into
* a checkmate. For instance, king + two knights vs king is NOT considered as dead according to the FIDE rules, whereas it is according to the
* USCF rules.
*
* For antichess and horde chess, this method always returns `false` since it is always possible to end in a checkmate-like situation
* (by capturing all the pieces of one player).
*
* @param uscfRules - `true` to use the USCF rules (forced checkmate), `false` to use the FIDE rules (possible checkmate).
*/
isDead(uscfRules = false) {
return (0, move_generation_1.isDead)(this._impl, uscfRules);
}
/**
* Whether at least one legal move exists in the current position or not. If the position is not legal (see {@link Position.isLegal}),
* the returned value is always `false`.
*/
hasMove() {
return (0, move_generation_1.hasMove)(this._impl);
}
/**
* Return the list of all legal moves in the current position. An empty list is returned if the position itself is not legal
* (see {@link Position.isLegal}).
*/
moves() {
return (0, move_generation_1.moves)(this._impl);
}
/**
* Check whether a move is legal or not, and return a factory capable the corresponding {@link MoveDescriptor}(-s) if it is legal.
*
* For castling moves, `to` is supposed to represent:
* - for regular chess, the destination square of the king (i.e. c1, g1, c8 or g8),
* - for Chess960, the origin square of the rook ("king-take-rook" pattern).
*
* A code interpreting the result returned by {@link Position.isMoveLegal} would typically look like this:
*
* ```
* const result = position.isMoveLegal(from, to);
* if (!result) {
* // The move "from -> to" is not legal.
* }
* else {
* switch (result.status) {
*
* case 'regular':
* // The move "from -> to" is legal, and the corresponding move descriptor
* // is `result()`.
* break;
*
* case 'promotion':
* // The move "from -> to" is legal, but it corresponds to a promotion,
* // so the promoted piece must be specified. The corresponding move descriptors
* // are `result('q')`, `result('r')`, `result('b')` and `result('n')`.
* break;
*
* default:
* // This case is not supposed to happen.
* break;
* }
* }
* ```
*/
isMoveLegal(from, to) {
const fromCode = (0, base_types_impl_1.squareFromString)(from);
const toCode = (0, base_types_impl_1.squareFromString)(to);
if (fromCode < 0 || toCode < 0) {
throw new exception_1.IllegalArgument('Position.isMoveLegal()');
}
const moveInfo = (0, move_generation_1.isMoveLegal)(this._impl, fromCode, toCode);
// No legal move.
if (!moveInfo) {
return false;
}
switch (moveInfo.type) {
case 'promotion': {
const result = (promotion) => {
const promotionCode = (0, base_types_impl_1.pieceFromString)(promotion);
if (promotionCode >= 0) {
const moveDescriptor = moveInfo.moveDescriptorFactory(promotionCode);
if (moveDescriptor) {
return moveDescriptor;
}
}
throw new exception_1.IllegalArgument('Position.isMoveLegal()');
};
result.status = 'promotion';
return result;
}
case 'regular': {
const result = () => moveInfo.moveDescriptor;
result.status = 'regular';
return result;
}
}
}
play(move) {
if (typeof move === 'string') {
try {
(0, move_generation_1.play)(this._impl, (0, notation_1.parseNotation)(this._impl, move, false, 'standard'));
return true;
}
catch (err) {
// istanbul ignore else
if (err instanceof exception_1.InvalidNotation) {
return false;
}
else {
throw err;
}
}
}
else if (move instanceof move_descriptor_impl_1.MoveDescriptorImpl) {
(0, move_generation_1.play)(this._impl, move);
return true;
}
else {
throw new exception_1.IllegalArgument('Position.play()');
}
}
/**
* Whether a null-move (i.e. switching the player about to play) can be played in the current position or not.
*
* A null-move is possible if the position is legal and if the current player about to play is not in check.
*/
isNullMoveLegal() {
return (0, move_generation_1.isNullMoveLegal)(this._impl);
}
/**
* Play a null-move on the current position if it is legal.
*
* @returns `true` if the move has actually been played, `false` otherwise.
*/
playNullMove() {
return (0, move_generation_1.playNullMove)(this._impl);
}
notation(moveOrDescriptor, strict) {
if (arguments.length === 1 && moveOrDescriptor instanceof move_descriptor_impl_1.MoveDescriptorImpl) {
return (0, notation_1.getNotation)(this._impl, moveOrDescriptor, 'standard');
}
else if (arguments.length === 1 && typeof moveOrDescriptor === 'string') {
return (0, notation_1.parseNotation)(this._impl, moveOrDescriptor, false, 'standard');
}
else if (arguments.length >= 2 && typeof moveOrDescriptor === 'string' && typeof strict === 'boolean') {
return (0, notation_1.parseNotation)(this._impl, moveOrDescriptor, strict, 'standard');
}
else {
throw new exception_1.IllegalArgument('Position.notation()');
}
}
figurineNotation(moveOrDescriptor, strict) {
if (arguments.length === 1 && moveOrDescriptor instanceof move_descriptor_impl_1.MoveDescriptorImpl) {
return (0, notation_1.getNotation)(this._impl, moveOrDescriptor, 'figurine');
}
else if (arguments.length === 1 && typeof moveOrDescriptor === 'string') {
return (0, notation_1.parseNotation)(this._impl, moveOrDescriptor, false, 'figurine');
}
else if (arguments.length >= 2 && typeof moveOrDescriptor === 'string' && typeof strict === 'boolean') {
return (0, notation_1.parseNotation)(this._impl, moveOrDescriptor, strict, 'figurine');
}
else {
throw new exception_1.IllegalArgument('Position.figurineNotation()');
}
}
uci(moveOrDescriptor, strictOrForceKxR) {
if (arguments.length === 1 && moveOrDescriptor instanceof move_descriptor_impl_1.MoveDescriptorImpl) {
return (0, uci_1.getUCINotation)(this._impl, moveOrDescriptor, false);
}
else if (arguments.length >= 2 && moveOrDescriptor instanceof move_descriptor_impl_1.MoveDescriptorImpl && typeof strictOrForceKxR === 'boolean') {
return (0, uci_1.getUCINotation)(this._impl, moveOrDescriptor, strictOrForceKxR);
}
else if (arguments.length === 1 && typeof moveOrDescriptor === 'string') {
return (0, uci_1.parseUCINotation)(this._impl, moveOrDescriptor, false);
}
else if (arguments.length >= 2 && typeof moveOrDescriptor === 'string' && typeof strictOrForceKxR === 'boolean') {
return (0, uci_1.parseUCINotation)(this._impl, moveOrDescriptor, strictOrForceKxR);
}
else {
throw new exception_1.IllegalArgument('Position.uci()');
}
}
}
exports.Position = Position;
function isValidScharnaglCode(scharnaglCode) {
return Number.isInteger(scharnaglCode) && scharnaglCode >= 0 && scharnaglCode < 960;
}
function buildValidator(options, functionName) {
return function (key, defaultValue, validator) {
if (options[key] === undefined) {
return defaultValue;
}
else {
const value = options[key];
if (!validator(value)) {
throw new exception_1.IllegalArgument(functionName);
}
return value;
}
};
}
//# sourceMappingURL=position.js.map