kokopu
Version:
A JavaScript/TypeScript library implementing the chess game rules and providing tools to read/write the standard chess file formats.
449 lines (448 loc) • 23.4 kB
TypeScript
/*!
* -------------------------------------------------------------------------- *
* *
* 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/>. *
* *
* -------------------------------------------------------------------------- */
import { Color, Piece, ColoredPiece, File, Square, Castle, Castle960, GameVariant } from './base_types';
import { MoveDescriptor } from './move_descriptor';
import { PositionAsciiOptions } from './private_position/fen';
/**
* 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.
*/
export declare class Position {
private _impl;
/**
* Instantiate a new {@link Position} configured for the usual chess rules, and initialized with the usual starting position.
*/
constructor(state?: 'start');
/**
* Instantiate a new {@link Position} configured for the usual chess rules, and initialized with an empty board.
*/
constructor(state: 'empty');
/**
* Instantiate a new {@link Position} configured for the given chess game variant, and initialized with
* the usual starting position of this variant.
*
* Warning: only chess game variants with a canonical start position can be used here (see {@link variantWithCanonicalStartPosition}).
*/
constructor(variant: 'regular' | 'antichess' | 'horde', state?: 'start');
/**
* Instantiate a new {@link Position} configured for the given chess game variant, and initialized with an empty board.
*/
constructor(variant: GameVariant, state: 'empty');
/**
* Instantiate a new {@link Position} configured for the Chess960 game variant, and initialized with
* the 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).
*/
constructor(variant: 'chess960', scharnaglCode: number);
/**
* Instantiate a new {@link Position} and initialize it by parsing the given [FEN](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation) string.
*
* If the given FEN string is prefixed by the name of the chess game variant + `:`, the position is configured
* for the corresponding chess game variant. Otherwise, the usual chess rules are used.
*
* If the chess game variant is Chess960, [X-FEN](https://en.wikipedia.org/wiki/X-FEN) can be used instead of regular FEN.
*
* @throws {@link exception.InvalidFEN} if the given string cannot be parsed as a valid FEN string.
*/
constructor(fen: string);
/**
* Instantiate a new {@link Position} configured for the given chess game variant, and initialize it by parsing the given
* [FEN](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation) string.
*
* If the chess game variant is Chess960, [X-FEN](https://en.wikipedia.org/wiki/X-FEN) can be used instead of regular FEN.
*
* @throws {@link exception.InvalidFEN} if the given string cannot be parsed as a valid FEN string.
*/
constructor(variant: GameVariant, fen: string);
/**
* Instantiate a copy of the given {@link Position}.
*/
constructor(other: Position);
/**
* Set the position to the empty state, for the given chess game variant.
*/
clear(variant?: GameVariant): void;
/**
* Set the position to the starting state (in the regular chess variant).
*/
reset(): void;
/**
* 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: number): void;
/**
* Set the position to the starting state of the antichess variant.
*/
resetAntichess(): void;
/**
* Set the position to the starting state of the horde chess variant.
*/
resetHorde(): void;
/**
* 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: unknown, pos2: unknown): boolean;
/**
* 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?: PositionAsciiOptions): string;
/**
* Get the [FEN](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation) representation of the current {@link Position}.
*
* @param options.fiftyMoveClock - Value of the fifty move clock counter (5th field) in the generated FEN string. `0` by default.
* @param options.fullMoveNumber - Value of the full move number counter (6th field) in the generated FEN string. `1` by default.
* @param options.withVariant - If `true`, a prefix containing the name of the chess game variant + `:` is prepend to the generated FEN string.
* `false` by default.
* @param options.regularFENIfPossible - For Chess960 only: if `true`, the castling flags are rendered using the regular-FEN style (i.e. `KQkq`)
* if no ambiguity is possible; if `false` or if there is an ambiguity, X-FEN style (i.e. `AHah`) is used instead.
* For non-Chess960 variants, this flag has no effect (regular FEN-style is always used in these cases).
* `false` by default.
*/
fen(options?: {
fiftyMoveClock?: number;
fullMoveNumber?: number;
withVariant?: boolean;
regularFENIfPossible?: boolean;
}): string;
/**
* Parse the given [FEN](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation) string and set the position accordingly.
*
* @param strict - If `true`, only perfectly formatted FEN strings are accepted. `false` by default.
* @throws {@link exception.InvalidFEN} if the given string cannot be parsed as a valid FEN string.
*/
fen(fen: string, strict?: boolean): {
fiftyMoveClock: number;
fullMoveNumber: number;
};
/**
* Get the chess game variant in use.
*/
variant(): GameVariant;
/**
* Get the content of a square.
*/
square(square: Square): ColoredPiece | '-';
/**
* Set the content of a square.
*/
square(square: Square, value: ColoredPiece | '-'): void;
/**
* Get the turn flag (i.e. who is about to play).
*/
turn(): Color;
/**
* Set the turn flag (i.e. who is about to play).
*/
turn(value: Color): void;
/**
* Get a castle flag (i.e. whether or not the corresponding castle is allowed or not).
*
* @param castle - Must be {@link Castle960} if the {@link Position} is configured for Chess960, or {@link Castle} otherwise.
*/
castling(castle: Castle | Castle960): boolean;
/**
* Set a castle flag (i.e. whether or not the corresponding castle is allowed or not).
*
* @param castle - Must be {@link Castle960} if the {@link Position} is configured for Chess960, or {@link Castle} otherwise.
*/
castling(castle: Castle | Castle960, value: boolean): void;
/**
* 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: Castle | Castle960): boolean;
/**
* Get the *en-passant* flag (i.e. the file on which a 2-square pawn move has just happen, if any).
*
* WARNING: even if this method returns something different from `'-'`, *en-passant* capture may not be possible on the corresponding file.
* Use {@link Position.effectiveEnPassant} to determine whether *en-passant* capture is actually possible or not.
*/
enPassant(): File | '-';
/**
* Set the *en-passant* flag (i.e. the file on which a 2-square pawn move has just happen, if any).
*/
enPassant(value: File | '-'): void;
/**
* 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(): File | '-';
/**
* Check if any piece of the given color attacks the given square.
*/
isAttacked(square: Square, byWho: Color): boolean;
/**
* Return the squares from which a piece of the given color attacks the given square.
*/
getAttacks(square: Square, byWho: Color): Square[];
/**
* 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(): boolean;
/**
* 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: Color): Square | false;
/**
* 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(): boolean;
/**
* 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(): boolean;
/**
* 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(): boolean;
/**
* 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?: boolean): boolean;
/**
* 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(): boolean;
/**
* 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(): MoveDescriptor[];
/**
* 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: Square, to: Square): RegularMoveFactory | PromotionMoveFactory | false;
/**
* Try to parse the given string as a [SAN](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)) notation, and to play
* the corresponding move on the current position.
*
* @returns `true` if the move has been played, `false` if the move is not legal or if the string passed to the method
* cannot be interpreted as a valid SAN move notation.
*/
play(move: string): boolean;
/**
* Play the given move.
*
* @param moveDescriptor - WARNING: this {@link MoveDescriptor} MUST have been generated by one of the methods of the current {@link Position}.
* Trying to invoke {@link Position.play} with a {@link MoveDescriptor} generated by another {@link Position} instance
* would result in an undefined behavior.
*/
play(moveDescriptor: MoveDescriptor): true;
/**
* 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(): boolean;
/**
* Play a null-move on the current position if it is legal.
*
* @returns `true` if the move has actually been played, `false` otherwise.
*/
playNullMove(): boolean;
/**
* Return the [standard algebraic notation](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)) corresponding to the given move descriptor.
*/
notation(moveDescriptor: MoveDescriptor): string;
/**
* Parse the given string as [standard algebraic notation](https://en.wikipedia.org/wiki/Algebraic_notation_(chess))
* and return the corresponding move descriptor.
*
* @param strict - If `true`, only perfectly formatted SAN moves are accepted. If `false`, "small errors" in the input
* such as a missing capture character, an unnecessary disambiguation symbol... do not interrupt the parsing.
* `false` by default.
* @throws {@link exception.InvalidNotation} if the move parsing fails or if the parsed move would correspond to an illegal move.
*/
notation(move: string, strict?: boolean): MoveDescriptor;
/**
* Return the figurine algebraic notation corresponding to the given move descriptor (figurine algebraic notation is the same as
* [standard algebraic notation](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)), except that chess pieces are represented
* with their respective unicode character, instead of the first letter of their English name).
*/
figurineNotation(moveDescriptor: MoveDescriptor): string;
/**
* Parse the given string as figurine algebraic notation and return the corresponding move descriptor (figurine algebraic notation is the same as
* [standard algebraic notation](https://en.wikipedia.org/wiki/Algebraic_notation_(chess)), except that chess pieces are represented
* with their respective unicode character, instead of the first letter of their English name).
*
* @param strict - If `true`, only perfectly formatted FAN moves are accepted. If `false`, "small errors" in the input
* such as a missing capture character, an unnecessary disambiguation symbol... do not interrupt the parsing.
* `false` by default.
* @throws {@link exception.InvalidNotation} if the move parsing fails or if the parsed move would correspond to an illegal move.
*/
figurineNotation(move: string, strict?: boolean): MoveDescriptor;
/**
* Return the UCI notation corresponding to the given move descriptor.
*
* Examples of UCI notation: `'e2e4'`, `'b8c6'`, `'e7e8q'` (promotion)... For more details, please refer to:
* - https://en.wikipedia.org/wiki/Universal_Chess_Interface
* - https://www.chessprogramming.org/UCI
* - https://www.shredderchess.com/download/div/uci.zip
*
* @param forceKxR - If `true`, castling moves are encoded as "king-take-rook", i.e. for instance white king-side castling will be `'e1h1'`
* (instead of `'e1g1'` in UCI standard). If `false`, castling move encoding follows the UCI standard for normal chess games
* (e.g. `'e1g1'`). For Chess960 games, the "king-take-rook" style is always used, whatever the value of this flag.
* `false` by default.
*/
uci(moveDescriptor: MoveDescriptor, forceKxR?: boolean): string;
/**
* Parse the given string as UCI notation and return the corresponding move descriptor.
*
* @param strict - If `true`, "king-take-rook"-encoded castling moves (i.e. for instance `'e1h1'` for white king-side castling)
* are rejected in case of normal chess games. If `false`, both "king-take-rook"-encoded and UCI-standard-encoded castling moves
* (e.g. `'e1g1'`) are accepted. For Chess960 games, only the "king-take-rook" style is accepted, whatever the value of this flag.
* `false` by default.
* @throws {@link exception.InvalidNotation} if the move parsing fails or if the parsed move would correspond to an illegal move.
*/
uci(move: string, strict?: boolean): MoveDescriptor;
}
/**
* @see {@link Position.isMoveLegal}
*/
export type RegularMoveFactory = {
status: 'regular';
(): MoveDescriptor;
};
/**
* @see {@link Position.isMoveLegal}
*/
export type PromotionMoveFactory = {
status: 'promotion';
(promotion: Piece): MoveDescriptor;
};