lichess-pgn-viewer
Version:
PGN viewer widget, designed to be embedded in content pages.
1,492 lines (1,481 loc) • 194 kB
JavaScript
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/types.js
var FILE_NAMES = ["a", "b", "c", "d", "e", "f", "g", "h"];
var RANK_NAMES = ["1", "2", "3", "4", "5", "6", "7", "8"];
var COLORS = ["white", "black"];
var ROLES = ["pawn", "knight", "bishop", "rook", "queen", "king"];
var CASTLING_SIDES = ["a", "h"];
var isDrop = (v) => "role" in v;
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/util.js
var defined = (v) => v !== void 0;
var opposite = (color) => color === "white" ? "black" : "white";
var squareRank = (square) => square >> 3;
var squareFile = (square) => square & 7;
var roleToChar = (role) => {
switch (role) {
case "pawn":
return "p";
case "knight":
return "n";
case "bishop":
return "b";
case "rook":
return "r";
case "queen":
return "q";
case "king":
return "k";
}
};
function charToRole(ch) {
switch (ch.toLowerCase()) {
case "p":
return "pawn";
case "n":
return "knight";
case "b":
return "bishop";
case "r":
return "rook";
case "q":
return "queen";
case "k":
return "king";
default:
return;
}
}
function parseSquare(str) {
if (str.length !== 2)
return;
const file = str.charCodeAt(0) - "a".charCodeAt(0);
const rank = str.charCodeAt(1) - "1".charCodeAt(0);
if (file < 0 || file >= 8 || rank < 0 || rank >= 8)
return;
return file + 8 * rank;
}
var makeSquare = (square) => FILE_NAMES[squareFile(square)] + RANK_NAMES[squareRank(square)];
var makeUci = (move3) => isDrop(move3) ? `${roleToChar(move3.role).toUpperCase()}@${makeSquare(move3.to)}` : makeSquare(move3.from) + makeSquare(move3.to) + (move3.promotion ? roleToChar(move3.promotion) : "");
var kingCastlesTo = (color, side) => color === "white" ? side === "a" ? 2 : 6 : side === "a" ? 58 : 62;
var rookCastlesTo = (color, side) => color === "white" ? side === "a" ? 3 : 5 : side === "a" ? 59 : 61;
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/squareSet.js
var popcnt32 = (n2) => {
n2 = n2 - (n2 >>> 1 & 1431655765);
n2 = (n2 & 858993459) + (n2 >>> 2 & 858993459);
return Math.imul(n2 + (n2 >>> 4) & 252645135, 16843009) >> 24;
};
var bswap32 = (n2) => {
n2 = n2 >>> 8 & 16711935 | (n2 & 16711935) << 8;
return n2 >>> 16 & 65535 | (n2 & 65535) << 16;
};
var rbit32 = (n2) => {
n2 = n2 >>> 1 & 1431655765 | (n2 & 1431655765) << 1;
n2 = n2 >>> 2 & 858993459 | (n2 & 858993459) << 2;
n2 = n2 >>> 4 & 252645135 | (n2 & 252645135) << 4;
return bswap32(n2);
};
var SquareSet = class _SquareSet {
constructor(lo, hi) {
this.lo = lo | 0;
this.hi = hi | 0;
}
static fromSquare(square) {
return square >= 32 ? new _SquareSet(0, 1 << square - 32) : new _SquareSet(1 << square, 0);
}
static fromRank(rank) {
return new _SquareSet(255, 0).shl64(8 * rank);
}
static fromFile(file) {
return new _SquareSet(16843009 << file, 16843009 << file);
}
static empty() {
return new _SquareSet(0, 0);
}
static full() {
return new _SquareSet(4294967295, 4294967295);
}
static corners() {
return new _SquareSet(129, 2164260864);
}
static center() {
return new _SquareSet(402653184, 24);
}
static backranks() {
return new _SquareSet(255, 4278190080);
}
static backrank(color) {
return color === "white" ? new _SquareSet(255, 0) : new _SquareSet(0, 4278190080);
}
static lightSquares() {
return new _SquareSet(1437226410, 1437226410);
}
static darkSquares() {
return new _SquareSet(2857740885, 2857740885);
}
complement() {
return new _SquareSet(~this.lo, ~this.hi);
}
xor(other) {
return new _SquareSet(this.lo ^ other.lo, this.hi ^ other.hi);
}
union(other) {
return new _SquareSet(this.lo | other.lo, this.hi | other.hi);
}
intersect(other) {
return new _SquareSet(this.lo & other.lo, this.hi & other.hi);
}
diff(other) {
return new _SquareSet(this.lo & ~other.lo, this.hi & ~other.hi);
}
intersects(other) {
return this.intersect(other).nonEmpty();
}
isDisjoint(other) {
return this.intersect(other).isEmpty();
}
supersetOf(other) {
return other.diff(this).isEmpty();
}
subsetOf(other) {
return this.diff(other).isEmpty();
}
shr64(shift) {
if (shift >= 64)
return _SquareSet.empty();
if (shift >= 32)
return new _SquareSet(this.hi >>> shift - 32, 0);
if (shift > 0)
return new _SquareSet(this.lo >>> shift ^ this.hi << 32 - shift, this.hi >>> shift);
return this;
}
shl64(shift) {
if (shift >= 64)
return _SquareSet.empty();
if (shift >= 32)
return new _SquareSet(0, this.lo << shift - 32);
if (shift > 0)
return new _SquareSet(this.lo << shift, this.hi << shift ^ this.lo >>> 32 - shift);
return this;
}
bswap64() {
return new _SquareSet(bswap32(this.hi), bswap32(this.lo));
}
rbit64() {
return new _SquareSet(rbit32(this.hi), rbit32(this.lo));
}
minus64(other) {
const lo = this.lo - other.lo;
const c = (lo & other.lo & 1) + (other.lo >>> 1) + (lo >>> 1) >>> 31;
return new _SquareSet(lo, this.hi - (other.hi + c));
}
equals(other) {
return this.lo === other.lo && this.hi === other.hi;
}
size() {
return popcnt32(this.lo) + popcnt32(this.hi);
}
isEmpty() {
return this.lo === 0 && this.hi === 0;
}
nonEmpty() {
return this.lo !== 0 || this.hi !== 0;
}
has(square) {
return (square >= 32 ? this.hi & 1 << square - 32 : this.lo & 1 << square) !== 0;
}
set(square, on) {
return on ? this.with(square) : this.without(square);
}
with(square) {
return square >= 32 ? new _SquareSet(this.lo, this.hi | 1 << square - 32) : new _SquareSet(this.lo | 1 << square, this.hi);
}
without(square) {
return square >= 32 ? new _SquareSet(this.lo, this.hi & ~(1 << square - 32)) : new _SquareSet(this.lo & ~(1 << square), this.hi);
}
toggle(square) {
return square >= 32 ? new _SquareSet(this.lo, this.hi ^ 1 << square - 32) : new _SquareSet(this.lo ^ 1 << square, this.hi);
}
last() {
if (this.hi !== 0)
return 63 - Math.clz32(this.hi);
if (this.lo !== 0)
return 31 - Math.clz32(this.lo);
return;
}
first() {
if (this.lo !== 0)
return 31 - Math.clz32(this.lo & -this.lo);
if (this.hi !== 0)
return 63 - Math.clz32(this.hi & -this.hi);
return;
}
withoutFirst() {
if (this.lo !== 0)
return new _SquareSet(this.lo & this.lo - 1, this.hi);
return new _SquareSet(0, this.hi & this.hi - 1);
}
moreThanOne() {
return this.hi !== 0 && this.lo !== 0 || (this.lo & this.lo - 1) !== 0 || (this.hi & this.hi - 1) !== 0;
}
singleSquare() {
return this.moreThanOne() ? void 0 : this.last();
}
*[Symbol.iterator]() {
let lo = this.lo;
let hi = this.hi;
while (lo !== 0) {
const idx = 31 - Math.clz32(lo & -lo);
lo ^= 1 << idx;
yield idx;
}
while (hi !== 0) {
const idx = 31 - Math.clz32(hi & -hi);
hi ^= 1 << idx;
yield 32 + idx;
}
}
*reversed() {
let lo = this.lo;
let hi = this.hi;
while (hi !== 0) {
const idx = 31 - Math.clz32(hi);
hi ^= 1 << idx;
yield 32 + idx;
}
while (lo !== 0) {
const idx = 31 - Math.clz32(lo);
lo ^= 1 << idx;
yield idx;
}
}
};
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/attacks.js
var computeRange = (square, deltas) => {
let range = SquareSet.empty();
for (const delta of deltas) {
const sq = square + delta;
if (0 <= sq && sq < 64 && Math.abs(squareFile(square) - squareFile(sq)) <= 2) {
range = range.with(sq);
}
}
return range;
};
var tabulate = (f) => {
const table = [];
for (let square = 0; square < 64; square++)
table[square] = f(square);
return table;
};
var KING_ATTACKS = tabulate((sq) => computeRange(sq, [-9, -8, -7, -1, 1, 7, 8, 9]));
var KNIGHT_ATTACKS = tabulate((sq) => computeRange(sq, [-17, -15, -10, -6, 6, 10, 15, 17]));
var PAWN_ATTACKS = {
white: tabulate((sq) => computeRange(sq, [7, 9])),
black: tabulate((sq) => computeRange(sq, [-7, -9]))
};
var kingAttacks = (square) => KING_ATTACKS[square];
var knightAttacks = (square) => KNIGHT_ATTACKS[square];
var pawnAttacks = (color, square) => PAWN_ATTACKS[color][square];
var FILE_RANGE = tabulate((sq) => SquareSet.fromFile(squareFile(sq)).without(sq));
var RANK_RANGE = tabulate((sq) => SquareSet.fromRank(squareRank(sq)).without(sq));
var DIAG_RANGE = tabulate((sq) => {
const diag = new SquareSet(134480385, 2151686160);
const shift = 8 * (squareRank(sq) - squareFile(sq));
return (shift >= 0 ? diag.shl64(shift) : diag.shr64(-shift)).without(sq);
});
var ANTI_DIAG_RANGE = tabulate((sq) => {
const diag = new SquareSet(270549120, 16909320);
const shift = 8 * (squareRank(sq) + squareFile(sq) - 7);
return (shift >= 0 ? diag.shl64(shift) : diag.shr64(-shift)).without(sq);
});
var hyperbola = (bit, range, occupied) => {
let forward = occupied.intersect(range);
let reverse = forward.bswap64();
forward = forward.minus64(bit);
reverse = reverse.minus64(bit.bswap64());
return forward.xor(reverse.bswap64()).intersect(range);
};
var fileAttacks = (square, occupied) => hyperbola(SquareSet.fromSquare(square), FILE_RANGE[square], occupied);
var rankAttacks = (square, occupied) => {
const range = RANK_RANGE[square];
let forward = occupied.intersect(range);
let reverse = forward.rbit64();
forward = forward.minus64(SquareSet.fromSquare(square));
reverse = reverse.minus64(SquareSet.fromSquare(63 - square));
return forward.xor(reverse.rbit64()).intersect(range);
};
var bishopAttacks = (square, occupied) => {
const bit = SquareSet.fromSquare(square);
return hyperbola(bit, DIAG_RANGE[square], occupied).xor(hyperbola(bit, ANTI_DIAG_RANGE[square], occupied));
};
var rookAttacks = (square, occupied) => fileAttacks(square, occupied).xor(rankAttacks(square, occupied));
var queenAttacks = (square, occupied) => bishopAttacks(square, occupied).xor(rookAttacks(square, occupied));
var attacks = (piece, square, occupied) => {
switch (piece.role) {
case "pawn":
return pawnAttacks(piece.color, square);
case "knight":
return knightAttacks(square);
case "bishop":
return bishopAttacks(square, occupied);
case "rook":
return rookAttacks(square, occupied);
case "queen":
return queenAttacks(square, occupied);
case "king":
return kingAttacks(square);
}
};
var ray = (a, b) => {
const other = SquareSet.fromSquare(b);
if (RANK_RANGE[a].intersects(other))
return RANK_RANGE[a].with(a);
if (ANTI_DIAG_RANGE[a].intersects(other))
return ANTI_DIAG_RANGE[a].with(a);
if (DIAG_RANGE[a].intersects(other))
return DIAG_RANGE[a].with(a);
if (FILE_RANGE[a].intersects(other))
return FILE_RANGE[a].with(a);
return SquareSet.empty();
};
var between = (a, b) => ray(a, b).intersect(SquareSet.full().shl64(a).xor(SquareSet.full().shl64(b))).withoutFirst();
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/board.js
var Board = class _Board {
constructor() {
}
static default() {
const board = new _Board();
board.reset();
return board;
}
/**
* Resets all pieces to the default starting position for standard chess.
*/
reset() {
this.occupied = new SquareSet(65535, 4294901760);
this.promoted = SquareSet.empty();
this.white = new SquareSet(65535, 0);
this.black = new SquareSet(0, 4294901760);
this.pawn = new SquareSet(65280, 16711680);
this.knight = new SquareSet(66, 1107296256);
this.bishop = new SquareSet(36, 603979776);
this.rook = new SquareSet(129, 2164260864);
this.queen = new SquareSet(8, 134217728);
this.king = new SquareSet(16, 268435456);
}
static empty() {
const board = new _Board();
board.clear();
return board;
}
clear() {
this.occupied = SquareSet.empty();
this.promoted = SquareSet.empty();
for (const color of COLORS)
this[color] = SquareSet.empty();
for (const role of ROLES)
this[role] = SquareSet.empty();
}
clone() {
const board = new _Board();
board.occupied = this.occupied;
board.promoted = this.promoted;
for (const color of COLORS)
board[color] = this[color];
for (const role of ROLES)
board[role] = this[role];
return board;
}
getColor(square) {
if (this.white.has(square))
return "white";
if (this.black.has(square))
return "black";
return;
}
getRole(square) {
for (const role of ROLES) {
if (this[role].has(square))
return role;
}
return;
}
get(square) {
const color = this.getColor(square);
if (!color)
return;
const role = this.getRole(square);
const promoted = this.promoted.has(square);
return { color, role, promoted };
}
/**
* Removes and returns the piece from the given `square`, if any.
*/
take(square) {
const piece = this.get(square);
if (piece) {
this.occupied = this.occupied.without(square);
this[piece.color] = this[piece.color].without(square);
this[piece.role] = this[piece.role].without(square);
if (piece.promoted)
this.promoted = this.promoted.without(square);
}
return piece;
}
/**
* Put `piece` onto `square`, potentially replacing an existing piece.
* Returns the existing piece, if any.
*/
set(square, piece) {
const old = this.take(square);
this.occupied = this.occupied.with(square);
this[piece.color] = this[piece.color].with(square);
this[piece.role] = this[piece.role].with(square);
if (piece.promoted)
this.promoted = this.promoted.with(square);
return old;
}
has(square) {
return this.occupied.has(square);
}
*[Symbol.iterator]() {
for (const square of this.occupied) {
yield [square, this.get(square)];
}
}
pieces(color, role) {
return this[color].intersect(this[role]);
}
rooksAndQueens() {
return this.rook.union(this.queen);
}
bishopsAndQueens() {
return this.bishop.union(this.queen);
}
/**
* Finds the unique king of the given `color`, if any.
*/
kingOf(color) {
return this.pieces(color, "king").singleSquare();
}
};
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/setup.js
var MaterialSide = class _MaterialSide {
constructor() {
}
static empty() {
const m = new _MaterialSide();
for (const role of ROLES)
m[role] = 0;
return m;
}
static fromBoard(board, color) {
const m = new _MaterialSide();
for (const role of ROLES)
m[role] = board.pieces(color, role).size();
return m;
}
clone() {
const m = new _MaterialSide();
for (const role of ROLES)
m[role] = this[role];
return m;
}
equals(other) {
return ROLES.every((role) => this[role] === other[role]);
}
add(other) {
const m = new _MaterialSide();
for (const role of ROLES)
m[role] = this[role] + other[role];
return m;
}
nonEmpty() {
return ROLES.some((role) => this[role] > 0);
}
isEmpty() {
return !this.nonEmpty();
}
hasPawns() {
return this.pawn > 0;
}
hasNonPawns() {
return this.knight > 0 || this.bishop > 0 || this.rook > 0 || this.queen > 0 || this.king > 0;
}
size() {
return this.pawn + this.knight + this.bishop + this.rook + this.queen + this.king;
}
};
var Material = class _Material {
constructor(white, black) {
this.white = white;
this.black = black;
}
static empty() {
return new _Material(MaterialSide.empty(), MaterialSide.empty());
}
static fromBoard(board) {
return new _Material(MaterialSide.fromBoard(board, "white"), MaterialSide.fromBoard(board, "black"));
}
clone() {
return new _Material(this.white.clone(), this.black.clone());
}
equals(other) {
return this.white.equals(other.white) && this.black.equals(other.black);
}
add(other) {
return new _Material(this.white.add(other.white), this.black.add(other.black));
}
count(role) {
return this.white[role] + this.black[role];
}
size() {
return this.white.size() + this.black.size();
}
isEmpty() {
return this.white.isEmpty() && this.black.isEmpty();
}
nonEmpty() {
return !this.isEmpty();
}
hasPawns() {
return this.white.hasPawns() || this.black.hasPawns();
}
hasNonPawns() {
return this.white.hasNonPawns() || this.black.hasNonPawns();
}
};
var RemainingChecks = class _RemainingChecks {
constructor(white, black) {
this.white = white;
this.black = black;
}
static default() {
return new _RemainingChecks(3, 3);
}
clone() {
return new _RemainingChecks(this.white, this.black);
}
equals(other) {
return this.white === other.white && this.black === other.black;
}
};
// node_modules/.pnpm/@badrap+result@0.2.13/node_modules/@badrap/result/dist/index.modern.mjs
var r = class {
unwrap(r2, t2) {
const e2 = this._chain((t3) => n.ok(r2 ? r2(t3) : t3), (r3) => t2 ? n.ok(t2(r3)) : n.err(r3));
if (e2.isErr)
throw e2.error;
return e2.value;
}
map(r2, t2) {
return this._chain((t3) => n.ok(r2(t3)), (r3) => n.err(t2 ? t2(r3) : r3));
}
chain(r2, t2) {
return this._chain(r2, t2 || ((r3) => n.err(r3)));
}
};
var t = class extends r {
constructor(r2) {
super(), this.value = void 0, this.isOk = true, this.isErr = false, this.value = r2;
}
_chain(r2, t2) {
return r2(this.value);
}
};
var e = class extends r {
constructor(r2) {
super(), this.error = void 0, this.isOk = false, this.isErr = true, this.error = r2;
}
_chain(r2, t2) {
return t2(this.error);
}
};
var n;
!function(r2) {
r2.ok = function(r3) {
return new t(r3);
}, r2.err = function(r3) {
return new e(r3 || new Error());
}, r2.all = function(t2) {
if (Array.isArray(t2)) {
const e3 = [];
for (let r3 = 0; r3 < t2.length; r3++) {
const n3 = t2[r3];
if (n3.isErr)
return n3;
e3.push(n3.value);
}
return r2.ok(e3);
}
const e2 = {}, n2 = Object.keys(t2);
for (let r3 = 0; r3 < n2.length; r3++) {
const s = t2[n2[r3]];
if (s.isErr)
return s;
e2[n2[r3]] = s.value;
}
return r2.ok(e2);
};
}(n || (n = {}));
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/chess.js
var IllegalSetup;
(function(IllegalSetup2) {
IllegalSetup2["Empty"] = "ERR_EMPTY";
IllegalSetup2["OppositeCheck"] = "ERR_OPPOSITE_CHECK";
IllegalSetup2["ImpossibleCheck"] = "ERR_IMPOSSIBLE_CHECK";
IllegalSetup2["PawnsOnBackrank"] = "ERR_PAWNS_ON_BACKRANK";
IllegalSetup2["Kings"] = "ERR_KINGS";
IllegalSetup2["Variant"] = "ERR_VARIANT";
})(IllegalSetup || (IllegalSetup = {}));
var PositionError = class extends Error {
};
var attacksTo = (square, attacker, board, occupied) => board[attacker].intersect(rookAttacks(square, occupied).intersect(board.rooksAndQueens()).union(bishopAttacks(square, occupied).intersect(board.bishopsAndQueens())).union(knightAttacks(square).intersect(board.knight)).union(kingAttacks(square).intersect(board.king)).union(pawnAttacks(opposite(attacker), square).intersect(board.pawn)));
var Castles = class _Castles {
constructor() {
}
static default() {
const castles = new _Castles();
castles.unmovedRooks = SquareSet.corners();
castles.rook = {
white: { a: 0, h: 7 },
black: { a: 56, h: 63 }
};
castles.path = {
white: { a: new SquareSet(14, 0), h: new SquareSet(96, 0) },
black: { a: new SquareSet(0, 234881024), h: new SquareSet(0, 1610612736) }
};
return castles;
}
static empty() {
const castles = new _Castles();
castles.unmovedRooks = SquareSet.empty();
castles.rook = {
white: { a: void 0, h: void 0 },
black: { a: void 0, h: void 0 }
};
castles.path = {
white: { a: SquareSet.empty(), h: SquareSet.empty() },
black: { a: SquareSet.empty(), h: SquareSet.empty() }
};
return castles;
}
clone() {
const castles = new _Castles();
castles.unmovedRooks = this.unmovedRooks;
castles.rook = {
white: { a: this.rook.white.a, h: this.rook.white.h },
black: { a: this.rook.black.a, h: this.rook.black.h }
};
castles.path = {
white: { a: this.path.white.a, h: this.path.white.h },
black: { a: this.path.black.a, h: this.path.black.h }
};
return castles;
}
add(color, side, king2, rook2) {
const kingTo = kingCastlesTo(color, side);
const rookTo = rookCastlesTo(color, side);
this.unmovedRooks = this.unmovedRooks.with(rook2);
this.rook[color][side] = rook2;
this.path[color][side] = between(rook2, rookTo).with(rookTo).union(between(king2, kingTo).with(kingTo)).without(king2).without(rook2);
}
static fromSetup(setup) {
const castles = _Castles.empty();
const rooks = setup.unmovedRooks.intersect(setup.board.rook);
for (const color of COLORS) {
const backrank = SquareSet.backrank(color);
const king2 = setup.board.kingOf(color);
if (!defined(king2) || !backrank.has(king2))
continue;
const side = rooks.intersect(setup.board[color]).intersect(backrank);
const aSide = side.first();
if (defined(aSide) && aSide < king2)
castles.add(color, "a", king2, aSide);
const hSide = side.last();
if (defined(hSide) && king2 < hSide)
castles.add(color, "h", king2, hSide);
}
return castles;
}
discardRook(square) {
if (this.unmovedRooks.has(square)) {
this.unmovedRooks = this.unmovedRooks.without(square);
for (const color of COLORS) {
for (const side of CASTLING_SIDES) {
if (this.rook[color][side] === square)
this.rook[color][side] = void 0;
}
}
}
}
discardColor(color) {
this.unmovedRooks = this.unmovedRooks.diff(SquareSet.backrank(color));
this.rook[color].a = void 0;
this.rook[color].h = void 0;
}
};
var Position = class {
constructor(rules) {
this.rules = rules;
}
reset() {
this.board = Board.default();
this.pockets = void 0;
this.turn = "white";
this.castles = Castles.default();
this.epSquare = void 0;
this.remainingChecks = void 0;
this.halfmoves = 0;
this.fullmoves = 1;
}
setupUnchecked(setup) {
this.board = setup.board.clone();
this.board.promoted = SquareSet.empty();
this.pockets = void 0;
this.turn = setup.turn;
this.castles = Castles.fromSetup(setup);
this.epSquare = validEpSquare(this, setup.epSquare);
this.remainingChecks = void 0;
this.halfmoves = setup.halfmoves;
this.fullmoves = setup.fullmoves;
}
// When subclassing overwrite at least:
//
// - static default()
// - static fromSetup()
// - static clone()
//
// - dests()
// - isVariantEnd()
// - variantOutcome()
// - hasInsufficientMaterial()
// - isStandardMaterial()
kingAttackers(square, attacker, occupied) {
return attacksTo(square, attacker, this.board, occupied);
}
playCaptureAt(square, captured) {
this.halfmoves = 0;
if (captured.role === "rook")
this.castles.discardRook(square);
if (this.pockets)
this.pockets[opposite(captured.color)][captured.promoted ? "pawn" : captured.role]++;
}
ctx() {
const variantEnd = this.isVariantEnd();
const king2 = this.board.kingOf(this.turn);
if (!defined(king2))
return { king: king2, blockers: SquareSet.empty(), checkers: SquareSet.empty(), variantEnd, mustCapture: false };
const snipers = rookAttacks(king2, SquareSet.empty()).intersect(this.board.rooksAndQueens()).union(bishopAttacks(king2, SquareSet.empty()).intersect(this.board.bishopsAndQueens())).intersect(this.board[opposite(this.turn)]);
let blockers = SquareSet.empty();
for (const sniper of snipers) {
const b = between(king2, sniper).intersect(this.board.occupied);
if (!b.moreThanOne())
blockers = blockers.union(b);
}
const checkers = this.kingAttackers(king2, opposite(this.turn), this.board.occupied);
return {
king: king2,
blockers,
checkers,
variantEnd,
mustCapture: false
};
}
clone() {
var _a, _b;
const pos = new this.constructor();
pos.board = this.board.clone();
pos.pockets = (_a = this.pockets) === null || _a === void 0 ? void 0 : _a.clone();
pos.turn = this.turn;
pos.castles = this.castles.clone();
pos.epSquare = this.epSquare;
pos.remainingChecks = (_b = this.remainingChecks) === null || _b === void 0 ? void 0 : _b.clone();
pos.halfmoves = this.halfmoves;
pos.fullmoves = this.fullmoves;
return pos;
}
validate(opts) {
if (this.board.occupied.isEmpty())
return n.err(new PositionError(IllegalSetup.Empty));
if (this.board.king.size() !== 2)
return n.err(new PositionError(IllegalSetup.Kings));
if (!defined(this.board.kingOf(this.turn)))
return n.err(new PositionError(IllegalSetup.Kings));
const otherKing = this.board.kingOf(opposite(this.turn));
if (!defined(otherKing))
return n.err(new PositionError(IllegalSetup.Kings));
if (this.kingAttackers(otherKing, this.turn, this.board.occupied).nonEmpty())
return n.err(new PositionError(IllegalSetup.OppositeCheck));
if (SquareSet.backranks().intersects(this.board.pawn))
return n.err(new PositionError(IllegalSetup.PawnsOnBackrank));
return (opts === null || opts === void 0 ? void 0 : opts.ignoreImpossibleCheck) ? n.ok(void 0) : this.validateCheckers();
}
validateCheckers() {
const ourKing = this.board.kingOf(this.turn);
if (defined(ourKing)) {
const checkers = this.kingAttackers(ourKing, opposite(this.turn), this.board.occupied);
if (checkers.nonEmpty()) {
if (defined(this.epSquare)) {
const pushedTo = this.epSquare ^ 8;
const pushedFrom = this.epSquare ^ 24;
if (checkers.moreThanOne() || checkers.first() !== pushedTo && this.kingAttackers(ourKing, opposite(this.turn), this.board.occupied.without(pushedTo).with(pushedFrom)).nonEmpty())
return n.err(new PositionError(IllegalSetup.ImpossibleCheck));
} else {
if (checkers.size() > 2 || checkers.size() === 2 && ray(checkers.first(), checkers.last()).has(ourKing))
return n.err(new PositionError(IllegalSetup.ImpossibleCheck));
}
}
}
return n.ok(void 0);
}
dropDests(_ctx) {
return SquareSet.empty();
}
dests(square, ctx) {
ctx = ctx || this.ctx();
if (ctx.variantEnd)
return SquareSet.empty();
const piece = this.board.get(square);
if (!piece || piece.color !== this.turn)
return SquareSet.empty();
let pseudo, legal;
if (piece.role === "pawn") {
pseudo = pawnAttacks(this.turn, square).intersect(this.board[opposite(this.turn)]);
const delta = this.turn === "white" ? 8 : -8;
const step2 = square + delta;
if (0 <= step2 && step2 < 64 && !this.board.occupied.has(step2)) {
pseudo = pseudo.with(step2);
const canDoubleStep = this.turn === "white" ? square < 16 : square >= 64 - 16;
const doubleStep = step2 + delta;
if (canDoubleStep && !this.board.occupied.has(doubleStep)) {
pseudo = pseudo.with(doubleStep);
}
}
if (defined(this.epSquare) && canCaptureEp(this, square, ctx)) {
const pawn2 = this.epSquare - delta;
if (ctx.checkers.isEmpty() || ctx.checkers.singleSquare() === pawn2) {
legal = SquareSet.fromSquare(this.epSquare);
}
}
} else if (piece.role === "bishop")
pseudo = bishopAttacks(square, this.board.occupied);
else if (piece.role === "knight")
pseudo = knightAttacks(square);
else if (piece.role === "rook")
pseudo = rookAttacks(square, this.board.occupied);
else if (piece.role === "queen")
pseudo = queenAttacks(square, this.board.occupied);
else
pseudo = kingAttacks(square);
pseudo = pseudo.diff(this.board[this.turn]);
if (defined(ctx.king)) {
if (piece.role === "king") {
const occ = this.board.occupied.without(square);
for (const to of pseudo) {
if (this.kingAttackers(to, opposite(this.turn), occ).nonEmpty())
pseudo = pseudo.without(to);
}
return pseudo.union(castlingDest(this, "a", ctx)).union(castlingDest(this, "h", ctx));
}
if (ctx.checkers.nonEmpty()) {
const checker = ctx.checkers.singleSquare();
if (!defined(checker))
return SquareSet.empty();
pseudo = pseudo.intersect(between(checker, ctx.king).with(checker));
}
if (ctx.blockers.has(square))
pseudo = pseudo.intersect(ray(square, ctx.king));
}
if (legal)
pseudo = pseudo.union(legal);
return pseudo;
}
isVariantEnd() {
return false;
}
variantOutcome(_ctx) {
return;
}
hasInsufficientMaterial(color) {
if (this.board[color].intersect(this.board.pawn.union(this.board.rooksAndQueens())).nonEmpty())
return false;
if (this.board[color].intersects(this.board.knight)) {
return this.board[color].size() <= 2 && this.board[opposite(color)].diff(this.board.king).diff(this.board.queen).isEmpty();
}
if (this.board[color].intersects(this.board.bishop)) {
const sameColor = !this.board.bishop.intersects(SquareSet.darkSquares()) || !this.board.bishop.intersects(SquareSet.lightSquares());
return sameColor && this.board.pawn.isEmpty() && this.board.knight.isEmpty();
}
return true;
}
// The following should be identical in all subclasses
toSetup() {
var _a, _b;
return {
board: this.board.clone(),
pockets: (_a = this.pockets) === null || _a === void 0 ? void 0 : _a.clone(),
turn: this.turn,
unmovedRooks: this.castles.unmovedRooks,
epSquare: legalEpSquare(this),
remainingChecks: (_b = this.remainingChecks) === null || _b === void 0 ? void 0 : _b.clone(),
halfmoves: Math.min(this.halfmoves, 150),
fullmoves: Math.min(Math.max(this.fullmoves, 1), 9999)
};
}
isInsufficientMaterial() {
return COLORS.every((color) => this.hasInsufficientMaterial(color));
}
hasDests(ctx) {
ctx = ctx || this.ctx();
for (const square of this.board[this.turn]) {
if (this.dests(square, ctx).nonEmpty())
return true;
}
return this.dropDests(ctx).nonEmpty();
}
isLegal(move3, ctx) {
if (isDrop(move3)) {
if (!this.pockets || this.pockets[this.turn][move3.role] <= 0)
return false;
if (move3.role === "pawn" && SquareSet.backranks().has(move3.to))
return false;
return this.dropDests(ctx).has(move3.to);
} else {
if (move3.promotion === "pawn")
return false;
if (move3.promotion === "king" && this.rules !== "antichess")
return false;
if (!!move3.promotion !== (this.board.pawn.has(move3.from) && SquareSet.backranks().has(move3.to)))
return false;
const dests = this.dests(move3.from, ctx);
return dests.has(move3.to) || dests.has(normalizeMove(this, move3).to);
}
}
isCheck() {
const king2 = this.board.kingOf(this.turn);
return defined(king2) && this.kingAttackers(king2, opposite(this.turn), this.board.occupied).nonEmpty();
}
isEnd(ctx) {
if (ctx ? ctx.variantEnd : this.isVariantEnd())
return true;
return this.isInsufficientMaterial() || !this.hasDests(ctx);
}
isCheckmate(ctx) {
ctx = ctx || this.ctx();
return !ctx.variantEnd && ctx.checkers.nonEmpty() && !this.hasDests(ctx);
}
isStalemate(ctx) {
ctx = ctx || this.ctx();
return !ctx.variantEnd && ctx.checkers.isEmpty() && !this.hasDests(ctx);
}
outcome(ctx) {
const variantOutcome = this.variantOutcome(ctx);
if (variantOutcome)
return variantOutcome;
ctx = ctx || this.ctx();
if (this.isCheckmate(ctx))
return { winner: opposite(this.turn) };
else if (this.isInsufficientMaterial() || this.isStalemate(ctx))
return { winner: void 0 };
else
return;
}
allDests(ctx) {
ctx = ctx || this.ctx();
const d = /* @__PURE__ */ new Map();
if (ctx.variantEnd)
return d;
for (const square of this.board[this.turn]) {
d.set(square, this.dests(square, ctx));
}
return d;
}
play(move3) {
const turn = this.turn;
const epSquare = this.epSquare;
const castling = castlingSide(this, move3);
this.epSquare = void 0;
this.halfmoves += 1;
if (turn === "black")
this.fullmoves += 1;
this.turn = opposite(turn);
if (isDrop(move3)) {
this.board.set(move3.to, { role: move3.role, color: turn });
if (this.pockets)
this.pockets[turn][move3.role]--;
if (move3.role === "pawn")
this.halfmoves = 0;
} else {
const piece = this.board.take(move3.from);
if (!piece)
return;
let epCapture;
if (piece.role === "pawn") {
this.halfmoves = 0;
if (move3.to === epSquare) {
epCapture = this.board.take(move3.to + (turn === "white" ? -8 : 8));
}
const delta = move3.from - move3.to;
if (Math.abs(delta) === 16 && 8 <= move3.from && move3.from <= 55) {
this.epSquare = move3.from + move3.to >> 1;
}
if (move3.promotion) {
piece.role = move3.promotion;
piece.promoted = !!this.pockets;
}
} else if (piece.role === "rook") {
this.castles.discardRook(move3.from);
} else if (piece.role === "king") {
if (castling) {
const rookFrom = this.castles.rook[turn][castling];
if (defined(rookFrom)) {
const rook2 = this.board.take(rookFrom);
this.board.set(kingCastlesTo(turn, castling), piece);
if (rook2)
this.board.set(rookCastlesTo(turn, castling), rook2);
}
}
this.castles.discardColor(turn);
}
if (!castling) {
const capture = this.board.set(move3.to, piece) || epCapture;
if (capture)
this.playCaptureAt(move3.to, capture);
}
}
if (this.remainingChecks) {
if (this.isCheck())
this.remainingChecks[turn] = Math.max(this.remainingChecks[turn] - 1, 0);
}
}
};
var Chess = class extends Position {
constructor() {
super("chess");
}
static default() {
const pos = new this();
pos.reset();
return pos;
}
static fromSetup(setup, opts) {
const pos = new this();
pos.setupUnchecked(setup);
return pos.validate(opts).map((_) => pos);
}
clone() {
return super.clone();
}
};
var validEpSquare = (pos, square) => {
if (!defined(square))
return;
const epRank = pos.turn === "white" ? 5 : 2;
const forward = pos.turn === "white" ? 8 : -8;
if (squareRank(square) !== epRank)
return;
if (pos.board.occupied.has(square + forward))
return;
const pawn2 = square - forward;
if (!pos.board.pawn.has(pawn2) || !pos.board[opposite(pos.turn)].has(pawn2))
return;
return square;
};
var legalEpSquare = (pos) => {
if (!defined(pos.epSquare))
return;
const ctx = pos.ctx();
const ourPawns = pos.board.pieces(pos.turn, "pawn");
const candidates = ourPawns.intersect(pawnAttacks(opposite(pos.turn), pos.epSquare));
for (const candidate of candidates) {
if (pos.dests(candidate, ctx).has(pos.epSquare))
return pos.epSquare;
}
return;
};
var canCaptureEp = (pos, pawn2, ctx) => {
if (!defined(pos.epSquare))
return false;
if (!pawnAttacks(pos.turn, pawn2).has(pos.epSquare))
return false;
if (!defined(ctx.king))
return true;
const captured = pos.epSquare + (pos.turn === "white" ? -8 : 8);
const occupied = pos.board.occupied.toggle(pawn2).toggle(pos.epSquare).toggle(captured);
return !pos.kingAttackers(ctx.king, opposite(pos.turn), occupied).intersects(occupied);
};
var castlingDest = (pos, side, ctx) => {
if (!defined(ctx.king) || ctx.checkers.nonEmpty())
return SquareSet.empty();
const rook2 = pos.castles.rook[pos.turn][side];
if (!defined(rook2))
return SquareSet.empty();
if (pos.castles.path[pos.turn][side].intersects(pos.board.occupied))
return SquareSet.empty();
const kingTo = kingCastlesTo(pos.turn, side);
const kingPath = between(ctx.king, kingTo);
const occ = pos.board.occupied.without(ctx.king);
for (const sq of kingPath) {
if (pos.kingAttackers(sq, opposite(pos.turn), occ).nonEmpty())
return SquareSet.empty();
}
const rookTo = rookCastlesTo(pos.turn, side);
const after = pos.board.occupied.toggle(ctx.king).toggle(rook2).toggle(rookTo);
if (pos.kingAttackers(kingTo, opposite(pos.turn), after).nonEmpty())
return SquareSet.empty();
return SquareSet.fromSquare(rook2);
};
var pseudoDests = (pos, square, ctx) => {
if (ctx.variantEnd)
return SquareSet.empty();
const piece = pos.board.get(square);
if (!piece || piece.color !== pos.turn)
return SquareSet.empty();
let pseudo = attacks(piece, square, pos.board.occupied);
if (piece.role === "pawn") {
let captureTargets = pos.board[opposite(pos.turn)];
if (defined(pos.epSquare))
captureTargets = captureTargets.with(pos.epSquare);
pseudo = pseudo.intersect(captureTargets);
const delta = pos.turn === "white" ? 8 : -8;
const step2 = square + delta;
if (0 <= step2 && step2 < 64 && !pos.board.occupied.has(step2)) {
pseudo = pseudo.with(step2);
const canDoubleStep = pos.turn === "white" ? square < 16 : square >= 64 - 16;
const doubleStep = step2 + delta;
if (canDoubleStep && !pos.board.occupied.has(doubleStep)) {
pseudo = pseudo.with(doubleStep);
}
}
return pseudo;
} else {
pseudo = pseudo.diff(pos.board[pos.turn]);
}
if (square === ctx.king)
return pseudo.union(castlingDest(pos, "a", ctx)).union(castlingDest(pos, "h", ctx));
else
return pseudo;
};
var castlingSide = (pos, move3) => {
if (isDrop(move3))
return;
const delta = move3.to - move3.from;
if (Math.abs(delta) !== 2 && !pos.board[pos.turn].has(move3.to))
return;
if (!pos.board.king.has(move3.from))
return;
return delta > 0 ? "h" : "a";
};
var normalizeMove = (pos, move3) => {
const side = castlingSide(pos, move3);
if (!side)
return move3;
const rookFrom = pos.castles.rook[pos.turn][side];
return {
from: move3.from,
to: defined(rookFrom) ? rookFrom : move3.to
};
};
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/compat.js
var scalachessCharPair = (move3) => isDrop(move3) ? String.fromCharCode(35 + move3.to, 35 + 64 + 8 * 5 + ["queen", "rook", "bishop", "knight", "pawn"].indexOf(move3.role)) : String.fromCharCode(35 + move3.from, move3.promotion ? 35 + 64 + 8 * ["queen", "rook", "bishop", "knight", "king"].indexOf(move3.promotion) + squareFile(move3.to) : 35 + move3.to);
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/fen.js
var INITIAL_BOARD_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
var INITIAL_EPD = INITIAL_BOARD_FEN + " w KQkq -";
var INITIAL_FEN = INITIAL_EPD + " 0 1";
var EMPTY_BOARD_FEN = "8/8/8/8/8/8/8/8";
var EMPTY_EPD = EMPTY_BOARD_FEN + " w - -";
var EMPTY_FEN = EMPTY_EPD + " 0 1";
var InvalidFen;
(function(InvalidFen2) {
InvalidFen2["Fen"] = "ERR_FEN";
InvalidFen2["Board"] = "ERR_BOARD";
InvalidFen2["Pockets"] = "ERR_POCKETS";
InvalidFen2["Turn"] = "ERR_TURN";
InvalidFen2["Castling"] = "ERR_CASTLING";
InvalidFen2["EpSquare"] = "ERR_EP_SQUARE";
InvalidFen2["RemainingChecks"] = "ERR_REMAINING_CHECKS";
InvalidFen2["Halfmoves"] = "ERR_HALFMOVES";
InvalidFen2["Fullmoves"] = "ERR_FULLMOVES";
})(InvalidFen || (InvalidFen = {}));
var FenError = class extends Error {
};
var nthIndexOf = (haystack, needle, n2) => {
let index = haystack.indexOf(needle);
while (n2-- > 0) {
if (index === -1)
break;
index = haystack.indexOf(needle, index + needle.length);
}
return index;
};
var parseSmallUint = (str) => /^\d{1,4}$/.test(str) ? parseInt(str, 10) : void 0;
var charToPiece = (ch) => {
const role = charToRole(ch);
return role && { role, color: ch.toLowerCase() === ch ? "black" : "white" };
};
var parseBoardFen = (boardPart) => {
const board = Board.empty();
let rank = 7;
let file = 0;
for (let i = 0; i < boardPart.length; i++) {
const c = boardPart[i];
if (c === "/" && file === 8) {
file = 0;
rank--;
} else {
const step2 = parseInt(c, 10);
if (step2 > 0)
file += step2;
else {
if (file >= 8 || rank < 0)
return n.err(new FenError(InvalidFen.Board));
const square = file + rank * 8;
const piece = charToPiece(c);
if (!piece)
return n.err(new FenError(InvalidFen.Board));
if (boardPart[i + 1] === "~") {
piece.promoted = true;
i++;
}
board.set(square, piece);
file++;
}
}
}
if (rank !== 0 || file !== 8)
return n.err(new FenError(InvalidFen.Board));
return n.ok(board);
};
var parsePockets = (pocketPart) => {
if (pocketPart.length > 64)
return n.err(new FenError(InvalidFen.Pockets));
const pockets = Material.empty();
for (const c of pocketPart) {
const piece = charToPiece(c);
if (!piece)
return n.err(new FenError(InvalidFen.Pockets));
pockets[piece.color][piece.role]++;
}
return n.ok(pockets);
};
var parseCastlingFen = (board, castlingPart) => {
let unmovedRooks = SquareSet.empty();
if (castlingPart === "-")
return n.ok(unmovedRooks);
for (const c of castlingPart) {
const lower = c.toLowerCase();
const color = c === lower ? "black" : "white";
const backrank = SquareSet.backrank(color).intersect(board[color]);
let candidates;
if (lower === "q")
candidates = backrank;
else if (lower === "k")
candidates = backrank.reversed();
else if ("a" <= lower && lower <= "h")
candidates = SquareSet.fromFile(lower.charCodeAt(0) - "a".charCodeAt(0)).intersect(backrank);
else
return n.err(new FenError(InvalidFen.Castling));
for (const square of candidates) {
if (board.king.has(square))
break;
if (board.rook.has(square)) {
unmovedRooks = unmovedRooks.with(square);
break;
}
}
}
if (COLORS.some((color) => SquareSet.backrank(color).intersect(unmovedRooks).size() > 2))
return n.err(new FenError(InvalidFen.Castling));
return n.ok(unmovedRooks);
};
var parseRemainingChecks = (part) => {
const parts = part.split("+");
if (parts.length === 3 && parts[0] === "") {
const white = parseSmallUint(parts[1]);
const black = parseSmallUint(parts[2]);
if (!defined(white) || white > 3 || !defined(black) || black > 3)
return n.err(new FenError(InvalidFen.RemainingChecks));
return n.ok(new RemainingChecks(3 - white, 3 - black));
} else if (parts.length === 2) {
const white = parseSmallUint(parts[0]);
const black = parseSmallUint(parts[1]);
if (!defined(white) || white > 3 || !defined(black) || black > 3)
return n.err(new FenError(InvalidFen.RemainingChecks));
return n.ok(new RemainingChecks(white, black));
} else
return n.err(new FenError(InvalidFen.RemainingChecks));
};
var parseFen = (fen) => {
const parts = fen.split(/[\s_]+/);
const boardPart = parts.shift();
let board;
let pockets = n.ok(void 0);
if (boardPart.endsWith("]")) {
const pocketStart = boardPart.indexOf("[");
if (pocketStart === -1)
return n.err(new FenError(InvalidFen.Fen));
board = parseBoardFen(boardPart.slice(0, pocketStart));
pockets = parsePockets(boardPart.slice(pocketStart + 1, -1));
} else {
const pocketStart = nthIndexOf(boardPart, "/", 7);
if (pocketStart === -1)
board = parseBoardFen(boardPart);
else {
board = parseBoardFen(boardPart.slice(0, pocketStart));
pockets = parsePockets(boardPart.slice(pocketStart + 1));
}
}
let turn;
const turnPart = parts.shift();
if (!defined(turnPart) || turnPart === "w")
turn = "white";
else if (turnPart === "b")
turn = "black";
else
return n.err(new FenError(InvalidFen.Turn));
return board.chain((board2) => {
const castlingPart = parts.shift();
const unmovedRooks = defined(castlingPart) ? parseCastlingFen(board2, castlingPart) : n.ok(SquareSet.empty());
const epPart = parts.shift();
let epSquare;
if (defined(epPart) && epPart !== "-") {
epSquare = parseSquare(epPart);
if (!defined(epSquare))
return n.err(new FenError(InvalidFen.EpSquare));
}
let halfmovePart = parts.shift();
let earlyRemainingChecks;
if (defined(halfmovePart) && halfmovePart.includes("+")) {
earlyRemainingChecks = parseRemainingChecks(halfmovePart);
halfmovePart = parts.shift();
}
const halfmoves = defined(halfmovePart) ? parseSmallUint(halfmovePart) : 0;
if (!defined(halfmoves))
return n.err(new FenError(InvalidFen.Halfmoves));
const fullmovesPart = parts.shift();
const fullmoves = defined(fullmovesPart) ? parseSmallUint(fullmovesPart) : 1;
if (!defined(fullmoves))
return n.err(new FenError(InvalidFen.Fullmoves));
const remainingChecksPart = parts.shift();
let remainingChecks = n.ok(void 0);
if (defined(remainingChecksPart)) {
if (defined(earlyRemainingChecks))
return n.err(new FenError(InvalidFen.RemainingChecks));
remainingChecks = parseRemainingChecks(remainingChecksPart);
} else if (defined(earlyRemainingChecks)) {
remainingChecks = earlyRemainingChecks;
}
if (parts.length > 0)
return n.err(new FenError(InvalidFen.Fen));
return pockets.chain((pockets2) => unmovedRooks.chain((unmovedRooks2) => remainingChecks.map((remainingChecks2) => {
return {
board: board2,
pockets: pockets2,
turn,
unmovedRooks: unmovedRooks2,
remainingChecks: remainingChecks2,
epSquare,
halfmoves,
fullmoves: Math.max(1, fullmoves)
};
})));
});
};
var makePiece = (piece) => {
let r2 = roleToChar(piece.role);
if (piece.color === "white")
r2 = r2.toUpperCase();
if (piece.promoted)
r2 += "~";
return r2;
};
var makeBoardFen = (board) => {
let fen = "";
let empty = 0;
for (let rank = 7; rank >= 0; rank--) {
for (let file = 0; file < 8; file++) {
const square = file + rank * 8;
const piece = board.get(square);
if (!piece)
empty++;
else {
if (empty > 0) {
fen += empty;
empty = 0;
}
fen += makePiece(piece);
}
if (file === 7) {
if (empty > 0) {
fen += empty;
empty = 0;
}
if (rank !== 0)
fen += "/";
}
}
}
return fen;
};
var makePocket = (material) => ROLES.map((role) => roleToChar(role).repeat(material[role])).join("");
var makePockets = (pocket) => makePocket(pocket.white).toUpperCase() + makePocket(pocket.black);
var makeCastlingFen = (board, unmovedRooks) => {
let fen = "";
for (const color of COLORS) {
const backrank = SquareSet.backrank(color);
let king2 = board.kingOf(color);
if (defined(king2) && !backrank.has(king2))
king2 = void 0;
const candidates = board.pieces(color, "rook").intersect(backrank);
for (const rook2 of unmovedRooks.intersect(candidates).reversed()) {
if (rook2 === candidates.first() && defined(king2) && rook2 < king2) {
fen += color === "white" ? "Q" : "q";
} else if (rook2 === candidates.last() && defined(king2) && king2 < rook2) {
fen += color === "white" ? "K" : "k";
} else {
const file = FILE_NAMES[squareFile(rook2)];
fen += color === "white" ? file.toUpperCase() : file;
}
}
}
return fen || "-";
};
var makeRemainingChecks = (checks) => `${checks.white}+${checks.black}`;
var makeFen = (setup, opts) => [
makeBoardFen(setup.board) + (setup.pockets ? `[${makePockets(setup.pockets)}]` : ""),
setup.turn[0],
makeCastlingFen(setup.board, setup.unmovedRooks),
defined(setup.epSquare) ? makeSquare(setup.epSquare) : "-",
...setup.remainingChecks ? [makeRemainingChecks(setup.remainingChecks)] : [],
...(opts === null || opts === void 0 ? void 0 : opts.epd) ? [] : [Math.max(0, Math.min(setup.halfmoves, 9999)), Math.max(1, Math.min(setup.fullmoves, 9999))]
].join(" ");
// node_modules/.pnpm/chessops@0.12.7/node_modules/chessops/san.js
var makeSanWithoutSuffix = (pos, move3) => {
let san = "";
if (isDrop(move3)) {
if (move3.role !== "pawn")
san = roleToChar(move3.role).toUpperCase();
san += "@" + makeSquare(move3.to);
} else {
const role = pos.board.getRole(move3.from);
if (!role)
return "--";
if (role === "king" && (pos.board[pos.turn].has(move3.to) || Math.abs(move3.to - move3.from) === 2)) {
san = move3.to > move3.from ? "O-O" : "O-O-O";
} else {
const capture = pos.board.occupied.has(move3.to) || role === "pawn" && squareFile(move3.from) !== squareFile(move3.to);
if (role !== "pawn") {
san = roleToChar(role).toUpperCase();
let others;
if (role === "king")
others = kingAttacks(move3.to).intersect(pos.board.king);
else if (role === "queen")
others = queenAttacks(move3.to, pos.board.occupied).intersect(pos.board.queen);
else if (role === "rook")
others = rookAttacks(move3.to, pos.board.occupied).intersect(pos.board.rook);
else if (role === "bishop")
others = bishopAttacks(move3.to, pos.board.occupied).intersect(pos.board.bishop);
else
others = knightAttacks(move3.to).intersect(pos.board.knight);
others = others.intersect(pos.board[pos.turn]).without(move3.from);
if (others.nonEmpty()) {
const ctx = pos.ctx();