@chess-labs/core
Version:
A lightweight, framework-agnostic chess engine written entirely in TypeScript
1,146 lines (1,091 loc) • 40.2 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/types.ts
var PieceType = /* @__PURE__ */ ((PieceType2) => {
PieceType2["PAWN"] = "pawn";
PieceType2["ROOK"] = "rook";
PieceType2["KNIGHT"] = "knight";
PieceType2["BISHOP"] = "bishop";
PieceType2["QUEEN"] = "queen";
PieceType2["KING"] = "king";
return PieceType2;
})(PieceType || {});
var Color = /* @__PURE__ */ ((Color5) => {
Color5["WHITE"] = "white";
Color5["BLACK"] = "black";
return Color5;
})(Color || {});
// src/board.ts
var initBoard = /* @__PURE__ */ __name(() => {
const board = Array(8).fill(null).map(() => Array(8).fill(null));
for (let col = 0; col < 8; col++) {
placePiece(board, { col, row: 1 }, { type: "pawn" /* PAWN */, color: "black" /* BLACK */ });
placePiece(board, { col, row: 6 }, { type: "pawn" /* PAWN */, color: "white" /* WHITE */ });
}
placePiece(board, { col: 0, row: 0 }, { type: "rook" /* ROOK */, color: "black" /* BLACK */ });
placePiece(board, { col: 7, row: 0 }, { type: "rook" /* ROOK */, color: "black" /* BLACK */ });
placePiece(board, { col: 0, row: 7 }, { type: "rook" /* ROOK */, color: "white" /* WHITE */ });
placePiece(board, { col: 7, row: 7 }, { type: "rook" /* ROOK */, color: "white" /* WHITE */ });
placePiece(board, { col: 1, row: 0 }, { type: "knight" /* KNIGHT */, color: "black" /* BLACK */ });
placePiece(board, { col: 6, row: 0 }, { type: "knight" /* KNIGHT */, color: "black" /* BLACK */ });
placePiece(board, { col: 1, row: 7 }, { type: "knight" /* KNIGHT */, color: "white" /* WHITE */ });
placePiece(board, { col: 6, row: 7 }, { type: "knight" /* KNIGHT */, color: "white" /* WHITE */ });
placePiece(board, { col: 2, row: 0 }, { type: "bishop" /* BISHOP */, color: "black" /* BLACK */ });
placePiece(board, { col: 5, row: 0 }, { type: "bishop" /* BISHOP */, color: "black" /* BLACK */ });
placePiece(board, { col: 2, row: 7 }, { type: "bishop" /* BISHOP */, color: "white" /* WHITE */ });
placePiece(board, { col: 5, row: 7 }, { type: "bishop" /* BISHOP */, color: "white" /* WHITE */ });
placePiece(board, { col: 3, row: 0 }, { type: "queen" /* QUEEN */, color: "black" /* BLACK */ });
placePiece(board, { col: 3, row: 7 }, { type: "queen" /* QUEEN */, color: "white" /* WHITE */ });
placePiece(board, { col: 4, row: 0 }, { type: "king" /* KING */, color: "black" /* BLACK */ });
placePiece(board, { col: 4, row: 7 }, { type: "king" /* KING */, color: "white" /* WHITE */ });
return board;
}, "initBoard");
var isValidPosition = /* @__PURE__ */ __name((position) => {
return position.col >= 0 && position.col < 8 && position.row >= 0 && position.row < 8;
}, "isValidPosition");
var getPieceAt = /* @__PURE__ */ __name((position, board) => {
if (!isValidPosition(position)) {
return null;
}
return board[position.row][position.col];
}, "getPieceAt");
var placePiece = /* @__PURE__ */ __name((board, position, piece) => {
if (!isValidPosition(position)) {
return false;
}
board[position.row][position.col] = piece;
return true;
}, "placePiece");
var removePiece = /* @__PURE__ */ __name((board, position) => {
if (!isValidPosition(position)) {
return null;
}
const piece = board[position.row][position.col];
board[position.row][position.col] = null;
return piece;
}, "removePiece");
var movePiece = /* @__PURE__ */ __name((board, from, to) => {
const piece = getPieceAt(from, board);
if (!piece || !isValidPosition(to)) {
return false;
}
const capturedPiece = removePiece(board, to);
removePiece(board, from);
placePiece(board, to, piece);
return capturedPiece || true;
}, "movePiece");
var clearPosition = /* @__PURE__ */ __name((board, position) => {
if (!isValidPosition(position)) {
return false;
}
board[position.row][position.col] = null;
return true;
}, "clearPosition");
var clearBoard = /* @__PURE__ */ __name((board) => {
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
board[row][col] = null;
}
}
return board;
}, "clearBoard");
var isPathClear = /* @__PURE__ */ __name((from, to, board) => {
const deltaCol = Math.sign(to.col - from.col);
const deltaRow = Math.sign(to.row - from.row);
if (deltaCol === 0 && deltaRow === 0) return true;
const xDiff = Math.abs(to.col - from.col);
const yDiff = Math.abs(to.row - from.row);
const isStraightPath = deltaCol === 0 || deltaRow === 0 || xDiff === yDiff;
if (!isStraightPath) {
throw new Error("Path must be straight (horizontal, vertical, or diagonal)");
}
let col = from.col + deltaCol;
let row = from.row + deltaRow;
while (col !== to.col || row !== to.row) {
if (board[row][col] !== null) {
return false;
}
col += deltaCol;
row += deltaRow;
}
return true;
}, "isPathClear");
var algebraicToPosition = /* @__PURE__ */ __name((algebraic) => {
if (!/^[a-h][1-8]$/.test(algebraic)) {
throw new Error('Invalid algebraic notation. Expected format like "e4".');
}
const file = algebraic.charCodeAt(0) - "a".charCodeAt(0);
const rank = 8 - Number.parseInt(algebraic[1], 10);
return { col: file, row: rank };
}, "algebraicToPosition");
var cloneBoard = /* @__PURE__ */ __name((board) => {
return board.map((row) => row.map((piece) => piece ? { ...piece } : null));
}, "cloneBoard");
var positionToAlgebraic = /* @__PURE__ */ __name((position) => {
if (!isValidPosition(position)) {
throw new Error("Invalid position. Coordinates must be between 0 and 7.");
}
const file = String.fromCharCode("a".charCodeAt(0) + position.col);
const rank = 8 - position.row;
return `${file}${rank}`;
}, "positionToAlgebraic");
// src/helper.ts
var arePositionsEqual = /* @__PURE__ */ __name((a, b) => {
return a.col === b.col && a.row === b.row;
}, "arePositionsEqual");
// src/moves/pawn.ts
var getPawnMoves = /* @__PURE__ */ __name((position, gameState) => {
const { board } = gameState;
const piece = getPieceAt(position, board);
if (!piece || piece.type !== "pawn" /* PAWN */) return [];
const moves = [];
const direction = piece.color === "white" /* WHITE */ ? -1 : 1;
const startingRank = piece.color === "white" /* WHITE */ ? 6 : 1;
const promotionRank = piece.color === "white" /* WHITE */ ? 0 : 7;
const forwardMoves = getForwardMoves(position, direction, startingRank, promotionRank, board);
moves.push(...forwardMoves);
const captureMoves = getCaptureMoves(position, direction, piece.color, promotionRank, board);
moves.push(...captureMoves);
const enPassantMoves = getEnPassantMoves(position, direction, piece.color, gameState);
moves.push(...enPassantMoves);
return moves;
}, "getPawnMoves");
var getForwardMoves = /* @__PURE__ */ __name((position, direction, startingRank, promotionRank, board) => {
const moves = [];
const oneSquareForward = { col: position.col, row: position.row + direction };
if (isValidPosition(oneSquareForward) && !getPieceAt(oneSquareForward, board)) {
if (oneSquareForward.row === promotionRank) {
moves.push({
from: position,
to: oneSquareForward,
special: "promotion"
});
} else {
moves.push({
from: position,
to: oneSquareForward
});
}
if (position.row === startingRank) {
const twoSquaresForward = { col: position.col, row: position.row + direction * 2 };
if (isValidPosition(twoSquaresForward) && !getPieceAt(twoSquaresForward, board)) {
moves.push({
from: position,
to: twoSquaresForward,
special: "two-square-advance"
});
}
}
}
return moves;
}, "getForwardMoves");
var getCaptureMoves = /* @__PURE__ */ __name((position, direction, color, promotionRank, board) => {
const moves = [];
const capturePositions = [
{ col: position.col - 1, row: position.row + direction },
// Left capture
{ col: position.col + 1, row: position.row + direction }
// Right capture
];
for (const capturePos of capturePositions) {
if (!isValidPosition(capturePos)) continue;
const targetPiece = getPieceAt(capturePos, board);
if (targetPiece && targetPiece.color !== color) {
if (capturePos.row === promotionRank) {
moves.push({
from: position,
to: capturePos,
capture: true,
special: "promotion"
});
} else {
moves.push({
from: position,
to: capturePos,
capture: true
});
}
}
}
return moves;
}, "getCaptureMoves");
var getEnPassantMoves = /* @__PURE__ */ __name((position, direction, color, gameState) => {
const moves = [];
const { moveHistory } = gameState;
if (moveHistory.length === 0) return moves;
const lastMove = moveHistory[moveHistory.length - 1];
if (lastMove.piece.type === "pawn" /* PAWN */ && lastMove.piece.color !== color && lastMove.special === "two-square-advance" && Math.abs(lastMove.from.row - lastMove.to.row) === 2 && position.row === lastMove.to.row) {
const colDiff = lastMove.to.col - position.col;
if (Math.abs(colDiff) === 1) {
moves.push({
from: position,
to: { col: lastMove.to.col, row: position.row + direction },
capture: true,
special: "en-passant",
capturedPiecePosition: lastMove.to
// Add this to help with move execution
});
}
}
return moves;
}, "getEnPassantMoves");
// src/moves/rook.ts
var getRookMoves = /* @__PURE__ */ __name((position, gameState) => {
const { board } = gameState;
const piece = getPieceAt(position, board);
if (!piece || piece.type !== "rook" /* ROOK */) return [];
const moves = [];
const directions = [
{ deltaCol: -1, deltaRow: 0 },
// left
{ deltaCol: 1, deltaRow: 0 },
// right
{ deltaCol: 0, deltaRow: -1 },
// up
{ deltaCol: 0, deltaRow: 1 }
// down
];
for (const direction of directions) {
moves.push(...getMovesInDirection(position, direction.deltaCol, direction.deltaRow, piece.color, board));
}
return moves;
}, "getRookMoves");
var getMovesInDirection = /* @__PURE__ */ __name((position, deltaCol, deltaRow, pieceColor, board) => {
const moves = [];
let currentCol = position.col + deltaCol;
let currentRow = position.row + deltaRow;
while (true) {
if (!isValidPosition({ col: currentCol, row: currentRow })) break;
const targetPosition = { col: currentCol, row: currentRow };
const targetPiece = getPieceAt(targetPosition, board);
if (targetPiece === null) {
moves.push({
from: position,
to: targetPosition,
capture: false
});
} else {
if (targetPiece.color !== pieceColor) {
moves.push({
from: position,
to: targetPosition,
capture: true
});
}
break;
}
currentCol += deltaCol;
currentRow += deltaRow;
}
return moves;
}, "getMovesInDirection");
// src/moves/knight.ts
var getKnightMoves = /* @__PURE__ */ __name((position) => {
const { col, row } = position;
const possibleMoves = [
{ col: col - 2, row: row - 1 },
{ col: col - 2, row: row + 1 },
{ col: col - 1, row: row - 2 },
{ col: col - 1, row: row + 2 },
{ col: col + 1, row: row - 2 },
{ col: col + 1, row: row + 2 },
{ col: col + 2, row: row - 1 },
{ col: col + 2, row: row + 1 }
];
return possibleMoves.filter((move) => move.col >= 0 && move.col <= 7 && move.row >= 0 && move.row <= 7);
}, "getKnightMoves");
// src/moves/bishop.ts
var getBishopMoves = /* @__PURE__ */ __name((from, gameState) => {
const piece = getPieceAt(from, gameState.board);
if (!piece || piece.type !== "bishop" /* BISHOP */) {
return [];
}
const moves = [];
const directions = [
{ col: 1, row: 1 },
// down-right
{ col: 1, row: -1 },
// up-right
{ col: -1, row: 1 },
// down-left
{ col: -1, row: -1 }
// up-left
];
for (const dir of directions) {
let currentPos = { col: from.col, row: from.row };
while (true) {
currentPos = { col: currentPos.col + dir.col, row: currentPos.row + dir.row };
if (!isValidPosition(currentPos)) {
break;
}
const targetPiece = getPieceAt(currentPos, gameState.board);
if (targetPiece && targetPiece.color === piece.color) {
break;
}
const move = {
from,
to: { ...currentPos },
capture: targetPiece !== null
};
moves.push(move);
if (targetPiece) {
break;
}
}
}
return moves;
}, "getBishopMoves");
// src/moves/queen.ts
var getQueenMoves = /* @__PURE__ */ __name((position, gameState) => {
const { board } = gameState;
const piece = getPieceAt(position, board);
if (!piece || piece.type !== "queen" /* QUEEN */) return [];
const moves = [];
const directions = [
{ deltaCol: -1, deltaRow: 0 },
// left
{ deltaCol: 1, deltaRow: 0 },
// right
{ deltaCol: 0, deltaRow: -1 },
// up
{ deltaCol: 0, deltaRow: 1 },
// down
{ deltaCol: -1, deltaRow: -1 },
// up-left
{ deltaCol: 1, deltaRow: -1 },
// up-right
{ deltaCol: -1, deltaRow: 1 },
// down-left
{ deltaCol: 1, deltaRow: 1 }
// down-right
];
for (const direction of directions) {
moves.push(...getMovesInDirection2(position, direction.deltaCol, direction.deltaRow, piece.color, board));
}
return moves;
}, "getQueenMoves");
var getMovesInDirection2 = /* @__PURE__ */ __name((position, deltaCol, deltaRow, pieceColor, board) => {
const moves = [];
let currentCol = position.col + deltaCol;
let currentRow = position.row + deltaRow;
while (true) {
if (!isValidPosition({ col: currentCol, row: currentRow })) break;
const targetPosition = { col: currentCol, row: currentRow };
const targetPiece = getPieceAt(targetPosition, board);
if (targetPiece === null) {
moves.push({
from: position,
to: targetPosition,
capture: false
});
} else {
if (targetPiece.color !== pieceColor) {
moves.push({
from: position,
to: targetPosition,
capture: true
});
}
break;
}
currentCol += deltaCol;
currentRow += deltaRow;
}
return moves;
}, "getMovesInDirection");
// src/moves/king.ts
var getKingMoves = /* @__PURE__ */ __name((position, gameState) => {
const { col, row } = position;
const { board } = gameState;
const kingPiece = getPieceAt(position, board);
if (!kingPiece || kingPiece.type !== "king" /* KING */) {
return [];
}
const kingColor = kingPiece.color;
const moves = [];
const directions = [
{ deltaCol: -1, deltaRow: -1 },
// top-left
{ deltaCol: 0, deltaRow: -1 },
// top
{ deltaCol: 1, deltaRow: -1 },
// top-right
{ deltaCol: -1, deltaRow: 0 },
// left
{ deltaCol: 1, deltaRow: 0 },
// right
{ deltaCol: -1, deltaRow: 1 },
// bottom-left
{ deltaCol: 0, deltaRow: 1 },
// bottom
{ deltaCol: 1, deltaRow: 1 }
// bottom-right
];
for (const { deltaCol, deltaRow } of directions) {
const newCol = col + deltaCol;
const newRow = row + deltaRow;
if (newCol < 0 || newCol > 7 || newRow < 0 || newRow > 7) {
continue;
}
const targetSquare = board[newRow][newCol];
if (!targetSquare || targetSquare.color !== kingColor) {
const move = {
from: { col, row },
to: { col: newCol, row: newRow }
};
if (targetSquare && targetSquare.color !== kingColor) {
move.capture = true;
}
moves.push(move);
}
}
if (!kingPiece.hasMoved) {
const kingsideRookPos = { col: 7, row };
if (canCastle(position, kingsideRookPos, gameState)) {
moves.push({
from: position,
to: { col: col + 2, row },
special: "castling"
});
}
const queensideRookPos = { col: 0, row };
if (canCastle(position, queensideRookPos, gameState)) {
moves.push({
from: position,
to: { col: col - 2, row },
special: "castling"
});
}
}
return moves;
}, "getKingMoves");
var canCastle = /* @__PURE__ */ __name((kingPosition, rookPosition, gameState) => {
const { board } = gameState;
const king = getPieceAt(kingPosition, board);
const rook = getPieceAt(rookPosition, board);
if (!king || king.type !== "king" /* KING */ || king.hasMoved) {
return false;
}
if (!rook || rook.type !== "rook" /* ROOK */ || rook.hasMoved) {
return false;
}
if (king.color !== rook.color) {
return false;
}
if (kingPosition.row !== rookPosition.row) {
return false;
}
if (isPlayerInCheck(gameState, king.color)) {
return false;
}
if (!isPathClear(kingPosition, rookPosition, board)) {
return false;
}
const direction = rookPosition.col > kingPosition.col ? 1 : -1;
for (let i = 1; i <= 2; i++) {
const checkPos = { col: kingPosition.col + direction * i, row: kingPosition.row };
const clonedBoard = cloneBoard(board);
clearPosition(clonedBoard, kingPosition);
placePiece(clonedBoard, checkPos, king);
const clonedGameState = { ...gameState, board: clonedBoard };
if (isPlayerInCheck(clonedGameState, king.color)) {
return false;
}
}
return true;
}, "canCastle");
// src/moves/index.ts
var getLegalMoves = /* @__PURE__ */ __name((position, gameState) => {
if (!isValidPosition(position)) {
return [];
}
const piece = getPieceAt(position, gameState.board);
if (!piece) {
return [];
}
if (piece.color !== gameState.currentTurn) {
return [];
}
let potentialMoves = [];
switch (piece.type) {
case "pawn" /* PAWN */:
potentialMoves = getPawnMoves(position, gameState);
break;
case "rook" /* ROOK */:
potentialMoves = getRookMoves(position, gameState);
break;
case "knight" /* KNIGHT */: {
const knightPositions = getKnightMoves(position);
potentialMoves = knightPositions.flatMap((to) => {
const targetPiece = getPieceAt(to, gameState.board);
if (targetPiece && targetPiece.color === piece.color) {
return [];
}
return [
{
from: position,
to,
capture: targetPiece !== null
}
];
});
break;
}
case "bishop" /* BISHOP */:
potentialMoves = getBishopMoves(position, gameState);
break;
case "queen" /* QUEEN */:
potentialMoves = getQueenMoves(position, gameState);
break;
case "king" /* KING */:
potentialMoves = getKingMoves(position, gameState);
break;
default:
return [];
}
return potentialMoves.filter((move) => {
const clonedBoard = cloneBoard(gameState.board);
const movingPiece = getPieceAt(position, clonedBoard);
if (!movingPiece) return false;
clearPosition(clonedBoard, position);
const targetPiece = getPieceAt(move.to, clonedBoard);
if (targetPiece) {
clearPosition(clonedBoard, move.to);
}
if (move.special === "en-passant" && move.capturedPiecePosition) {
clearPosition(clonedBoard, move.capturedPiecePosition);
}
placePiece(clonedBoard, move.to, { ...movingPiece, hasMoved: true });
const tempGameState = {
...gameState,
board: clonedBoard
};
return !isPlayerInCheck(tempGameState, piece.color);
});
}, "getLegalMoves");
// src/game.ts
var movePiece2 = /* @__PURE__ */ __name((from, to, gameState, promotionType = "queen" /* QUEEN */, skipStatusUpdate = false) => {
if (!isValidPosition(from) || !isValidPosition(to)) {
return null;
}
const piece = getPieceAt(from, gameState.board);
if (!piece) {
return null;
}
if (piece.color !== gameState.currentTurn) {
return null;
}
const legalMoves = getLegalMoves(from, gameState);
const validMove = legalMoves.find((move) => arePositionsEqual(move.to, to));
if (!validMove) {
return null;
}
const newBoard = cloneBoard(gameState.board);
let capturedPiece = null;
if (validMove.special === "en-passant" && validMove.capturedPiecePosition) {
capturedPiece = getPieceAt(validMove.capturedPiecePosition, newBoard);
clearPosition(newBoard, validMove.capturedPiecePosition);
} else {
capturedPiece = getPieceAt(to, newBoard);
}
newBoard[from.row][from.col] = null;
let movedPiece = {
...piece,
hasMoved: true
};
if (validMove.special === "promotion") {
const validPromotionTypes = ["rook" /* ROOK */, "knight" /* KNIGHT */, "bishop" /* BISHOP */, "queen" /* QUEEN */];
const finalPromotionType = validPromotionTypes.includes(promotionType) ? promotionType : "queen" /* QUEEN */;
movedPiece = {
color: piece.color,
type: finalPromotionType,
hasMoved: true
};
}
placePiece(newBoard, to, movedPiece);
if (validMove.special === "castling") {
const isKingside = to.col > from.col;
if (isKingside) {
const rookFrom = { col: 7, row: from.row };
const rookTo = { col: 5, row: from.row };
const rook = getPieceAt(rookFrom, newBoard);
if (rook) {
clearPosition(newBoard, rookFrom);
placePiece(newBoard, rookTo, { ...rook, hasMoved: true });
}
} else {
const rookFrom = { col: 0, row: from.row };
const rookTo = { col: 3, row: from.row };
const rook = getPieceAt(rookFrom, newBoard);
if (rook) {
clearPosition(newBoard, rookFrom);
placePiece(newBoard, rookTo, { ...rook, hasMoved: true });
}
}
}
const moveHistoryEntry = {
from,
to,
piece: { ...piece },
special: validMove.special,
captured: capturedPiece || void 0,
promotedTo: validMove.special === "promotion" ? promotionType : void 0
};
let newGameState = {
...gameState,
board: newBoard,
currentTurn: gameState.currentTurn === "white" /* WHITE */ ? "black" /* BLACK */ : "white" /* WHITE */,
moveHistory: [...gameState.moveHistory, moveHistoryEntry],
// Reset these flags - they will be recalculated
isCheck: false,
isCheckmate: false,
isStalemate: false
};
const playerColor = gameState.currentTurn;
const tempStateForCheck = {
...newGameState,
currentTurn: playerColor
};
if (isPlayerInCheck(tempStateForCheck, playerColor)) {
return null;
}
if (!skipStatusUpdate) {
newGameState = updateGameStatus(newGameState);
}
return newGameState;
}, "movePiece");
var isValidMove = /* @__PURE__ */ __name((from, to, gameState) => {
const legalMoves = getLegalMoves(from, gameState);
return legalMoves.some((move) => arePositionsEqual(move.to, to));
}, "isValidMove");
var initGameState = /* @__PURE__ */ __name(() => {
return {
board: initBoard(),
currentTurn: "white" /* WHITE */,
// White always moves first in standard chess
moveHistory: [],
isCheck: false,
isCheckmate: false,
isStalemate: false
};
}, "initGameState");
var switchTurn = /* @__PURE__ */ __name((gameState) => {
return {
...gameState,
currentTurn: gameState.currentTurn === "white" /* WHITE */ ? "black" /* BLACK */ : "white" /* WHITE */
};
}, "switchTurn");
var addMoveToHistory = /* @__PURE__ */ __name((gameState, from, to, piece, captured, special) => {
const moveHistoryEntry = {
from,
to,
piece: { ...piece },
captured,
special
};
return {
...gameState,
moveHistory: [...gameState.moveHistory, moveHistoryEntry]
};
}, "addMoveToHistory");
var getCurrentPlayer = /* @__PURE__ */ __name((gameState) => {
return gameState.currentTurn;
}, "getCurrentPlayer");
var getMoveHistory = /* @__PURE__ */ __name((gameState) => {
return [...gameState.moveHistory];
}, "getMoveHistory");
var isPlayerInCheck = /* @__PURE__ */ __name((gameState, color) => {
const kingPosition = findKingPosition(gameState, color);
if (!kingPosition) return false;
const opponentColor = color === "white" /* WHITE */ ? "black" /* BLACK */ : "white" /* WHITE */;
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const piece = gameState.board[row][col];
if (piece && piece.color === opponentColor) {
const piecePosition = { row, col };
if (canPieceAttackPosition(piece, piecePosition, kingPosition, gameState.board)) {
return true;
}
}
}
}
return false;
}, "isPlayerInCheck");
var canPieceAttackPosition = /* @__PURE__ */ __name((piece, piecePosition, targetPosition, board) => {
const deltaRow = targetPosition.row - piecePosition.row;
const deltaCol = targetPosition.col - piecePosition.col;
const absDeltaRow = Math.abs(deltaRow);
const absDeltaCol = Math.abs(deltaCol);
switch (piece.type) {
case "pawn" /* PAWN */: {
const direction = piece.color === "white" /* WHITE */ ? -1 : 1;
return absDeltaCol === 1 && deltaRow === direction;
}
case "rook" /* ROOK */:
if ((deltaRow === 0 || deltaCol === 0) && isPathClear(piecePosition, targetPosition, board)) {
return true;
}
return false;
case "knight" /* KNIGHT */:
return absDeltaRow === 2 && absDeltaCol === 1 || absDeltaRow === 1 && absDeltaCol === 2;
case "bishop" /* BISHOP */:
if (absDeltaRow === absDeltaCol && isPathClear(piecePosition, targetPosition, board)) {
return true;
}
return false;
case "queen" /* QUEEN */:
if ((deltaRow === 0 || deltaCol === 0 || absDeltaRow === absDeltaCol) && isPathClear(piecePosition, targetPosition, board)) {
return true;
}
return false;
case "king" /* KING */:
return absDeltaRow <= 1 && absDeltaCol <= 1;
default:
return false;
}
}, "canPieceAttackPosition");
var findKingPosition = /* @__PURE__ */ __name((gameState, color) => {
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const piece = gameState.board[row][col];
if (piece && piece.type === "king" /* KING */ && piece.color === color) {
return { row, col };
}
}
}
return null;
}, "findKingPosition");
var isCheckmate = /* @__PURE__ */ __name((gameState, color) => {
if (!isPlayerInCheck(gameState, color)) {
return false;
}
const kingPosition = findKingPosition(gameState, color);
if (!kingPosition) return false;
const kingPiece = getPieceAt(kingPosition, gameState.board);
if (!kingPiece) return false;
const kingMoves = getLegalMoves(kingPosition, gameState);
for (const move of kingMoves) {
const newGameState = movePiece2(kingPosition, move.to, gameState, void 0, true);
if (newGameState && !isPlayerInCheck(newGameState, color)) {
return false;
}
}
const attackingPieces = findAttackingPieces(gameState, color);
if (attackingPieces.length > 1) {
return true;
}
if (attackingPieces.length === 1) {
const attacker = attackingPieces[0];
const attackerPosition = { row: attacker.row, col: attacker.col };
const defenders = getActivePieces(gameState, color);
for (const defenderPos of defenders) {
if (defenderPos.row === kingPosition.row && defenderPos.col === kingPosition.col) {
continue;
}
if (isValidMove(defenderPos, attackerPosition, gameState)) {
const newGameState = movePiece2(defenderPos, attackerPosition, gameState, void 0, true);
if (newGameState && !isPlayerInCheck(newGameState, color)) {
return false;
}
}
}
if (attacker.piece.type === "bishop" /* BISHOP */ || attacker.piece.type === "rook" /* ROOK */ || attacker.piece.type === "queen" /* QUEEN */) {
const blockingSquares = getPathBetween(attackerPosition, kingPosition);
for (const defenderPos of defenders) {
if (defenderPos.row === kingPosition.row && defenderPos.col === kingPosition.col) {
continue;
}
for (const blockPos of blockingSquares) {
if (isValidMove(defenderPos, blockPos, gameState)) {
const newGameState = movePiece2(defenderPos, blockPos, gameState, void 0, true);
if (newGameState && !isPlayerInCheck(newGameState, color)) {
return false;
}
}
}
}
}
}
return true;
}, "isCheckmate");
function findAttackingPieces(gameState, defendingColor) {
const kingPosition = findKingPosition(gameState, defendingColor);
if (!kingPosition) return [];
const attackingColor = defendingColor === "white" /* WHITE */ ? "black" /* BLACK */ : "white" /* WHITE */;
const attackers = [];
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const piece = gameState.board[row][col];
if (piece && piece.color === attackingColor) {
const piecePosition = { row, col };
if (canPieceAttackPosition(piece, piecePosition, kingPosition, gameState.board)) {
attackers.push({ row, col, piece });
}
}
}
}
return attackers;
}
__name(findAttackingPieces, "findAttackingPieces");
function getPathBetween(from, to) {
const path = [];
const deltaRow = to.row - from.row;
const deltaCol = to.col - from.col;
const isLinear = deltaRow === 0 || deltaCol === 0 || Math.abs(deltaRow) === Math.abs(deltaCol);
if (!isLinear) return path;
const rowStep = deltaRow === 0 ? 0 : deltaRow > 0 ? 1 : -1;
const colStep = deltaCol === 0 ? 0 : deltaCol > 0 ? 1 : -1;
let row = from.row + rowStep;
let col = from.col + colStep;
while (row !== to.row || col !== to.col) {
path.push({ row, col });
row += rowStep;
col += colStep;
}
return path;
}
__name(getPathBetween, "getPathBetween");
var isStalemate = /* @__PURE__ */ __name((gameState, color) => {
if (isPlayerInCheck(gameState, color)) {
return false;
}
const activePieces = getActivePieces(gameState, color);
for (const piecePosition of activePieces) {
const legalMoves = getLegalMoves(piecePosition, gameState);
for (const move of legalMoves) {
const tempBoard = cloneBoard(gameState.board);
const piece = getPieceAt(piecePosition, tempBoard);
if (!piece) continue;
clearPosition(tempBoard, piecePosition);
placePiece(tempBoard, move.to, { ...piece, hasMoved: true });
const tempGameState = {
...gameState,
board: tempBoard,
currentTurn: color
};
if (!isPlayerInCheck(tempGameState, color)) {
return false;
}
}
}
return true;
}, "isStalemate");
var updateGameStatus = /* @__PURE__ */ __name((gameState) => {
const currentPlayer = gameState.currentTurn;
const isInCheck = isPlayerInCheck(gameState, currentPlayer);
const isInCheckmate = isInCheck && isCheckmate(gameState, currentPlayer);
const isInStalemate = !isInCheck && isStalemate(gameState, currentPlayer);
return {
...gameState,
isCheck: isInCheck,
isCheckmate: isInCheckmate,
isStalemate: isInStalemate
};
}, "updateGameStatus");
function getActivePieces(gameState, color) {
const pieces = [];
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const piece = gameState.board[row][col];
if (piece && piece.color === color) {
pieces.push({ row, col });
}
}
}
return pieces;
}
__name(getActivePieces, "getActivePieces");
// src/fen.ts
var PIECE_TO_CHAR_MAP = {
["pawn" /* PAWN */]: "p",
["rook" /* ROOK */]: "r",
["knight" /* KNIGHT */]: "n",
["bishop" /* BISHOP */]: "b",
["queen" /* QUEEN */]: "q",
["king" /* KING */]: "k"
};
var CHAR_TO_PIECE_TYPE_MAP = {
p: "pawn" /* PAWN */,
r: "rook" /* ROOK */,
n: "knight" /* KNIGHT */,
b: "bishop" /* BISHOP */,
q: "queen" /* QUEEN */,
k: "king" /* KING */
};
var pieceToFenChar = /* @__PURE__ */ __name((piece) => {
const char = PIECE_TO_CHAR_MAP[piece.type];
return piece.color === "white" /* WHITE */ ? char.toUpperCase() : char;
}, "pieceToFenChar");
var fenCharToPiece = /* @__PURE__ */ __name((fenChar) => {
if (fenChar === " " || fenChar === "/") return null;
const color = fenChar === fenChar.toUpperCase() ? "white" /* WHITE */ : "black" /* BLACK */;
const lowerChar = fenChar.toLowerCase();
const type = CHAR_TO_PIECE_TYPE_MAP[lowerChar];
if (!type) return null;
return { type, color };
}, "fenCharToPiece");
var boardToFenPieces = /* @__PURE__ */ __name((board) => {
let fen = "";
for (let row = 0; row < 8; row++) {
let emptyCount = 0;
for (let col = 0; col < 8; col++) {
const piece = board[row][col];
if (piece) {
if (emptyCount > 0) {
fen += emptyCount.toString();
emptyCount = 0;
}
fen += pieceToFenChar(piece);
} else {
emptyCount++;
}
}
if (emptyCount > 0) {
fen += emptyCount.toString();
}
if (row < 7) {
fen += "/";
}
}
return fen;
}, "boardToFenPieces");
var fenPiecesToBoard = /* @__PURE__ */ __name((fenPieces) => {
const board = initBoard();
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
board[row][col] = null;
}
}
const rows = fenPieces.split("/");
if (rows.length !== 8) {
throw new Error("Invalid FEN: must have 8 rows");
}
for (let row = 0; row < 8; row++) {
const rowStr = rows[row];
let col = 0;
for (const char of rowStr) {
if (char >= "1" && char <= "8") {
const emptyCount = Number.parseInt(char);
col += emptyCount;
} else {
const piece = fenCharToPiece(char);
if (!piece) {
throw new Error(`Invalid FEN: unrecognized character '${char}' in row ${row + 1}`);
}
if (col < 8) {
board[row][col] = piece;
}
col++;
}
}
if (col !== 8) {
throw new Error(`Invalid FEN: row ${row + 1} has ${col} squares instead of 8`);
}
}
return board;
}, "fenPiecesToBoard");
var getCastlingRights = /* @__PURE__ */ __name((gameState) => {
let rights = "";
if (gameState.castlingRights) {
if (gameState.castlingRights.whiteKingside) rights += "K";
if (gameState.castlingRights.whiteQueenside) rights += "Q";
if (gameState.castlingRights.blackKingside) rights += "k";
if (gameState.castlingRights.blackQueenside) rights += "q";
return rights || "-";
}
const whiteKing = gameState.board[7][4];
const blackKing = gameState.board[0][4];
const whiteKingsideRook = gameState.board[7][7];
const whiteQueensideRook = gameState.board[7][0];
const blackKingsideRook = gameState.board[0][7];
const blackQueensideRook = gameState.board[0][0];
if (whiteKing && !whiteKing.hasMoved && whiteKingsideRook && !whiteKingsideRook.hasMoved) {
rights += "K";
}
if (whiteKing && !whiteKing.hasMoved && whiteQueensideRook && !whiteQueensideRook.hasMoved) {
rights += "Q";
}
if (blackKing && !blackKing.hasMoved && blackKingsideRook && !blackKingsideRook.hasMoved) {
rights += "k";
}
if (blackKing && !blackKing.hasMoved && blackQueensideRook && !blackQueensideRook.hasMoved) {
rights += "q";
}
return rights || "-";
}, "getCastlingRights");
var getEnPassantTarget = /* @__PURE__ */ __name((gameState) => {
if (gameState.enPassantTarget !== void 0) {
return gameState.enPassantTarget || "-";
}
if (gameState.moveHistory.length === 0) return "-";
const lastMove = gameState.moveHistory[gameState.moveHistory.length - 1];
if (lastMove.piece.type === "pawn" /* PAWN */ && lastMove.special === "two-square-advance") {
const targetRow = lastMove.piece.color === "white" /* WHITE */ ? lastMove.to.row + 1 : lastMove.to.row - 1;
const targetCol = lastMove.to.col;
const file = String.fromCharCode(97 + targetCol);
const rank = (8 - targetRow).toString();
return file + rank;
}
return "-";
}, "getEnPassantTarget");
var calculateHalfmoveClock = /* @__PURE__ */ __name((moveHistory) => {
let halfmoves = 0;
for (let i = moveHistory.length - 1; i >= 0; i--) {
const move = moveHistory[i];
if (move.piece.type === "pawn" /* PAWN */ || move.captured) {
break;
}
halfmoves++;
}
return halfmoves;
}, "calculateHalfmoveClock");
var parseCastlingRights = /* @__PURE__ */ __name((castlingStr) => {
return {
whiteKingside: castlingStr.includes("K"),
whiteQueenside: castlingStr.includes("Q"),
blackKingside: castlingStr.includes("k"),
blackQueenside: castlingStr.includes("q")
};
}, "parseCastlingRights");
var gameStateToFen = /* @__PURE__ */ __name((gameState) => {
const pieces = boardToFenPieces(gameState.board);
const activeColor = gameState.currentTurn === "white" /* WHITE */ ? "w" : "b";
const castlingRights = getCastlingRights(gameState);
const enPassantTarget = getEnPassantTarget(gameState);
const halfmoveClock = gameState.halfmoveClock !== void 0 ? gameState.halfmoveClock.toString() : calculateHalfmoveClock(gameState.moveHistory).toString();
const fullmoveNumber = gameState.fullmoveNumber !== void 0 ? gameState.fullmoveNumber.toString() : (Math.floor(gameState.moveHistory.length / 2) + 1).toString();
return `${pieces} ${activeColor} ${castlingRights} ${enPassantTarget} ${halfmoveClock} ${fullmoveNumber}`;
}, "gameStateToFen");
var fenToGameState = /* @__PURE__ */ __name((fen) => {
const parts = fen.trim().split(/\s+/);
if (parts.length !== 6) {
throw new Error("Invalid FEN: must have 6 space-separated parts");
}
const [pieces, activeColor, castlingRights, enPassantTarget, halfmoveClock, fullmoveNumber] = parts;
const board = fenPiecesToBoard(pieces);
const currentTurn = activeColor === "w" ? "white" /* WHITE */ : "black" /* BLACK */;
const castling = parseCastlingRights(castlingRights);
const enPassant = enPassantTarget === "-" ? void 0 : enPassantTarget;
const halfmove = Number.parseInt(halfmoveClock) || 0;
const fullmove = Number.parseInt(fullmoveNumber) || 1;
const gameState = {
board,
currentTurn,
moveHistory: [],
// Cannot reconstruct move history from FEN alone
isCheck: false,
// Would need to calculate based on board position
isCheckmate: false,
// Would need to calculate based on available moves
isStalemate: false,
// Would need to calculate based on available moves
castlingRights: castling,
enPassantTarget: enPassant,
halfmoveClock: halfmove,
fullmoveNumber: fullmove
};
return gameState;
}, "fenToGameState");
var STARTING_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
exports.Color = Color; exports.PieceType = PieceType; exports.STARTING_FEN = STARTING_FEN; exports.addMoveToHistory = addMoveToHistory; exports.algebraicToPosition = algebraicToPosition; exports.arePositionsEqual = arePositionsEqual; exports.boardToFenPieces = boardToFenPieces; exports.clearBoard = clearBoard; exports.clearPosition = clearPosition; exports.cloneBoard = cloneBoard; exports.fenCharToPiece = fenCharToPiece; exports.fenPiecesToBoard = fenPiecesToBoard; exports.fenToGameState = fenToGameState; exports.gameStateToFen = gameStateToFen; exports.getBishopMoves = getBishopMoves; exports.getCastlingRights = getCastlingRights; exports.getCurrentPlayer = getCurrentPlayer; exports.getEnPassantTarget = getEnPassantTarget; exports.getKingMoves = getKingMoves; exports.getKnightMoves = getKnightMoves; exports.getLegalMoves = getLegalMoves; exports.getMoveHistory = getMoveHistory; exports.getPawnMoves = getPawnMoves; exports.getPieceAt = getPieceAt; exports.getQueenMoves = getQueenMoves; exports.getRookMoves = getRookMoves; exports.initBoard = initBoard; exports.initGameState = initGameState; exports.isCheckmate = isCheckmate; exports.isPathClear = isPathClear; exports.isPlayerInCheck = isPlayerInCheck; exports.isStalemate = isStalemate; exports.isValidMove = isValidMove; exports.isValidPosition = isValidPosition; exports.movePiece = movePiece2; exports.movePieceOnBoard = movePiece; exports.pieceToFenChar = pieceToFenChar; exports.placePiece = placePiece; exports.positionToAlgebraic = positionToAlgebraic; exports.removePiece = removePiece; exports.switchTurn = switchTurn; exports.updateGameStatus = updateGameStatus;
//# sourceMappingURL=index.cjs.map