UNPKG

@chess-labs/core

Version:

A lightweight, framework-agnostic chess engine written entirely in TypeScript

1,146 lines (1,091 loc) 40.2 kB
"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