kokopu
Version:
A JavaScript/TypeScript library implementing the chess game rules and providing tools to read/write the standard chess file formats.
244 lines • 17.6 kB
JavaScript
;
/*!
* -------------------------------------------------------------------------- *
* *
* 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.hasCanonicalStartPosition = hasCanonicalStartPosition;
exports.makeEmpty = makeEmpty;
exports.makeInitial = makeInitial;
exports.make960FromScharnagl = make960FromScharnagl;
exports.makeCopy = makeCopy;
const EMPTY_BOARD = [
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */,
];
const REGULAR_START_BOARD = [
4 /* CpI.WR */, 8 /* CpI.WN */, 6 /* CpI.WB */, 2 /* CpI.WQ */, 0 /* CpI.WK */, 6 /* CpI.WB */, 8 /* CpI.WN */, 4 /* CpI.WR */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
5 /* CpI.BR */, 9 /* CpI.BN */, 7 /* CpI.BB */, 3 /* CpI.BQ */, 1 /* CpI.BK */, 7 /* CpI.BB */, 9 /* CpI.BN */, 5 /* CpI.BR */,
/* eslint-enable */
];
const HORDE_START_BOARD = [
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, 10 /* CpI.WP */, 10 /* CpI.WP */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, 10 /* CpI.WP */, 10 /* CpI.WP */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
5 /* CpI.BR */, 9 /* CpI.BN */, 7 /* CpI.BB */, 3 /* CpI.BQ */, 1 /* CpI.BK */, 7 /* CpI.BB */, 9 /* CpI.BN */, 5 /* CpI.BR */,
/* eslint-enable */
];
const START_POSITION_INFO = [
{
board: REGULAR_START_BOARD,
castling: [129 /* (1 << A-file) | (1 << H-file) */, 129 /* (1 << A-file) | (1 << H-file) */],
king: [4 /* SquareImpl.E1 */, 116 /* SquareImpl.E8 */],
},
null, // Chess960
null, // no king
null, // white king only
null, // black king only
{
board: REGULAR_START_BOARD,
castling: [0, 0],
king: [-1, -1],
},
{
board: HORDE_START_BOARD,
castling: [0, 129 /* (1 << A-file) | (1 << H-file) */],
king: [-1, 116 /* SquareImpl.E8 */],
},
];
function hasCanonicalStartPosition(variant) {
return START_POSITION_INFO[variant] !== null;
}
function makeEmpty(variant) {
return {
board: EMPTY_BOARD.slice(),
turn: 0 /* ColorImpl.WHITE */,
castling: [0, 0],
enPassant: -1,
variant: variant,
legal: variant === 2 /* GameVariantImpl.NO_KING */,
king: [-1, -1],
effectiveCastling: [0, 0],
effectiveEnPassant: -1,
};
}
/**
* @param variant - Must be a variant with a canonical start position.
*/
function makeInitial(variant) {
const info = START_POSITION_INFO[variant]; // WARNING: applicable only to variants with a canonical start position.
return {
board: info.board.slice(),
turn: 0 /* ColorImpl.WHITE */,
castling: info.castling.slice(),
enPassant: -1,
variant: variant,
legal: true,
king: info.king.slice(),
effectiveCastling: info.castling.slice(),
effectiveEnPassant: -1,
};
}
/**
* Chess960 initial position, following the numbering scheme proposed by Reinhard Scharnagl (see for instance https://chess960.net/start-positions/).
*
* @param scharnaglCode - Integer between 0 and 959 inclusive.
*/
function make960FromScharnagl(scharnaglCode) {
const info = decodeScharnagl(scharnaglCode);
const rank1 = info.pieceScheme.map(piece => piece * 2 + 0 /* ColorImpl.WHITE */);
const rank8 = info.pieceScheme.map(piece => piece * 2 + 1 /* ColorImpl.BLACK */);
return {
board: [
/* eslint-disable @stylistic/comma-spacing, @stylistic/no-multi-spaces */
rank1[0], rank1[1], rank1[2], rank1[3], rank1[4], rank1[5], rank1[6], rank1[7], -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, 10 /* CpI.WP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
-1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -1 /* SpI.EMPTY */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, 11 /* CpI.BP */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */, -2 /* SpI.INVALID */,
rank8[0], rank8[1], rank8[2], rank8[3], rank8[4], rank8[5], rank8[6], rank8[7],
/* eslint-enable */
],
turn: 0 /* ColorImpl.WHITE */,
castling: [info.castling, info.castling],
enPassant: -1,
variant: 1 /* GameVariantImpl.CHESS960 */,
legal: true,
king: [0 /* SquareImpl.A1 */ + info.kingFile, 112 /* SquareImpl.A8 */ + info.kingFile],
effectiveCastling: [info.castling, info.castling],
effectiveEnPassant: -1,
};
}
/**
* @param scharnaglCode - Integer between 0 and 959 inclusive.
*/
function decodeScharnagl(scharnaglCode) {
const scheme = [-1, -1, -1, -1, -1, -1, -1, -1];
let castling = 0;
let kingFile = -1;
function forEachEmpty(fun) {
let emptyIndex = 0;
for (let file = 0; file < 8; ++file) {
if (scheme[file] >= 0) {
continue;
}
fun(file, emptyIndex);
++emptyIndex;
}
}
function setAt(piece, emptyIndexTarget1, emptyIndexTarget2) {
forEachEmpty((file, emptyIndex) => {
if (emptyIndex === emptyIndexTarget1 || emptyIndex === emptyIndexTarget2) {
scheme[file] = piece;
}
});
}
// Light-square bishop
scheme[(scharnaglCode % 4) * 2 + 1] = 3 /* PieceImpl.BISHOP */;
scharnaglCode = Math.trunc(scharnaglCode / 4);
// Dark-square bishop
scheme[(scharnaglCode % 4) * 2] = 3 /* PieceImpl.BISHOP */;
scharnaglCode = Math.trunc(scharnaglCode / 4);
// Queen
setAt(1 /* PieceImpl.QUEEN */, scharnaglCode % 6, -1);
scharnaglCode = Math.trunc(scharnaglCode / 6);
// Knights
switch (scharnaglCode) { // `scharnaglCode` is guaranteed here to be between 0 and 9 inclusive
case 0:
setAt(4 /* PieceImpl.KNIGHT */, 0, 1);
break;
case 1:
setAt(4 /* PieceImpl.KNIGHT */, 0, 2);
break;
case 2:
setAt(4 /* PieceImpl.KNIGHT */, 0, 3);
break;
case 3:
setAt(4 /* PieceImpl.KNIGHT */, 0, 4);
break;
case 4:
setAt(4 /* PieceImpl.KNIGHT */, 1, 2);
break;
case 5:
setAt(4 /* PieceImpl.KNIGHT */, 1, 3);
break;
case 6:
setAt(4 /* PieceImpl.KNIGHT */, 1, 4);
break;
case 7:
setAt(4 /* PieceImpl.KNIGHT */, 2, 3);
break;
case 8:
setAt(4 /* PieceImpl.KNIGHT */, 2, 4);
break;
case 9:
setAt(4 /* PieceImpl.KNIGHT */, 3, 4);
break;
}
// Rooks and king
forEachEmpty((file, emptyIndex) => {
if (emptyIndex === 1) {
scheme[file] = 0 /* PieceImpl.KING */;
kingFile = file;
}
else {
scheme[file] = 2 /* PieceImpl.ROOK */;
castling |= 1 << file;
}
});
return {
pieceScheme: scheme,
castling: castling,
kingFile: kingFile,
};
}
function makeCopy(position) {
return {
board: position.board.slice(),
turn: position.turn,
castling: position.castling.slice(),
enPassant: position.enPassant,
variant: position.variant,
legal: position.legal,
king: position.king.slice(),
effectiveCastling: position.effectiveCastling === null ? null : position.effectiveCastling.slice(),
effectiveEnPassant: position.effectiveEnPassant,
};
}
//# sourceMappingURL=impl.js.map