import { type ChessPiece, PieceType, PieceColor, type Position, type MoveResult, GameMode } from "./chess-types" import { cloneBoard, findKingPosition } from "./chess-utils" // Check if a move is valid for a specific piece export function isValidMove( board: (ChessPiece | null)[][], from: Position, to: Position, currentPlayer: PieceColor, gameMode: GameMode = GameMode.CLASSIC, ): boolean { // Cannot move to the same position if (from.row === to.row && from.col === to.col) { return false } const piece = board[from.row][from.col] // No piece at the starting position or wrong player's piece if (!piece || piece.color !== currentPlayer) { return false } const targetPiece = board[to.row][to.col] // Cannot capture own piece if (targetPiece && targetPiece.color === piece.color) { return false } // Check piece-specific movement rules let validPieceMove = false switch (piece.type) { case PieceType.PAWN: validPieceMove = isValidPawnMove(board, from, to) break case PieceType.ROOK: validPieceMove = isValidRookMove(board, from, to) break case PieceType.KNIGHT: validPieceMove = isValidKnightMove(from, to) break case PieceType.BISHOP: validPieceMove = isValidBishopMove(board, from, to) break case PieceType.QUEEN: validPieceMove = isValidQueenMove(board, from, to) break case PieceType.KING: validPieceMove = isValidKingMove(board, from, to) break } if (!validPieceMove) { return false } // In ghost chess, forced captures take priority if (gameMode === GameMode.GHOST) { const captureMoves = getCaptureMoves(board, currentPlayer) const isCapture = targetPiece !== null // If there are capture moves available and this isn't a capture, it's invalid if (captureMoves.length > 0 && !isCapture) { return false } } // Check if the move would put or leave the king in check (only in classic mode) if (gameMode === GameMode.CLASSIC) { const newBoard = cloneBoard(board) newBoard[to.row][to.col] = newBoard[from.row][from.col] newBoard[from.row][from.col] = null return !isKingInCheck(newBoard, piece.color) } return true } // Make a move and return the new board state export function makeMove(board: (ChessPiece | null)[][], from: Position, to: Position): MoveResult { const newBoard = cloneBoard(board) const piece = newBoard[from.row][from.col] const capturedPiece = newBoard[to.row][to.col] if (!piece) { return { newBoard, capturedPiece: null } } // Update hasMoved property for pawns, kings, and rooks (for castling) if (piece.type === PieceType.PAWN || piece.type === PieceType.KING || piece.type === PieceType.ROOK) { piece.hasMoved = true } // Handle pawn promotion if (piece.type === PieceType.PAWN && (to.row === 0 || to.row === 7)) { piece.type = PieceType.QUEEN // Auto-promote to queen for simplicity } // Handle castling if (piece.type === PieceType.KING && Math.abs(from.col - to.col) === 2) { const isKingSide = to.col > from.col const rookCol = isKingSide ? 7 : 0 const newRookCol = isKingSide ? from.col + 1 : from.col - 1 // Move the rook newBoard[from.row][newRookCol] = newBoard[from.row][rookCol] newBoard[from.row][rookCol] = null if (newBoard[from.row][newRookCol]) { newBoard[from.row][newRookCol].hasMoved = true } } // Make the move newBoard[to.row][to.col] = piece newBoard[from.row][from.col] = null return { newBoard, capturedPiece } } // Check if the king of the given color is in check export function isCheck(board: (ChessPiece | null)[][], color: PieceColor): boolean { return isKingInCheck(board, color) } // Check if the king of the given color is in checkmate export function isCheckmate(board: (ChessPiece | null)[][], color: PieceColor, gameMode: GameMode = GameMode.CLASSIC): boolean { // Ghost chess doesn't have checkmate if (gameMode === GameMode.GHOST) { return false } // If the king is not in check, it's not checkmate if (!isKingInCheck(board, color)) { return false } // Check if any move can get the king out of check return !hasLegalMoves(board, color, gameMode) } // Check if the position is a stalemate export function isStalemate(board: (ChessPiece | null)[][], color: PieceColor, gameMode: GameMode = GameMode.CLASSIC): boolean { // Ghost chess doesn't have stalemate if (gameMode === GameMode.GHOST) { return false } // If the king is in check, it's not stalemate if (isKingInCheck(board, color)) { return false } // If the player has no legal moves, it's stalemate return !hasLegalMoves(board, color, gameMode) } // Helper function to check if a player has any legal moves function hasLegalMoves(board: (ChessPiece | null)[][], color: PieceColor, gameMode: GameMode = GameMode.CLASSIC): boolean { for (let fromRow = 0; fromRow < 8; fromRow++) { for (let fromCol = 0; fromCol < 8; fromCol++) { const piece = board[fromRow][fromCol] if (piece && piece.color === color) { for (let toRow = 0; toRow < 8; toRow++) { for (let toCol = 0; toCol < 8; toCol++) { if (isValidMove(board, { row: fromRow, col: fromCol }, { row: toRow, col: toCol }, color, gameMode)) { return true } } } } } } return false } // Helper function to check if the king is in check function isKingInCheck(board: (ChessPiece | null)[][], kingColor: PieceColor): boolean { const kingPosition = findKingPosition(board, kingColor) if (!kingPosition) return false const opponentColor = kingColor === PieceColor.WHITE ? PieceColor.BLACK : PieceColor.WHITE // Check if any opponent piece can capture the king for (let row = 0; row < 8; row++) { for (let col = 0; col < 8; col++) { const piece = board[row][col] if (piece && piece.color === opponentColor) { // Use a simplified version of isValidMove that doesn't check for check // to avoid infinite recursion if (canPieceMove(board, { row, col }, kingPosition)) { return true } } } } return false } // Simplified version of isValidMove that doesn't check for check function canPieceMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { const piece = board[from.row][from.col] if (!piece) return false const targetPiece = board[to.row][to.col] if (targetPiece && targetPiece.color === piece.color) return false switch (piece.type) { case PieceType.PAWN: return isValidPawnMove(board, from, to) case PieceType.ROOK: return isValidRookMove(board, from, to) case PieceType.KNIGHT: return isValidKnightMove(from, to) case PieceType.BISHOP: return isValidBishopMove(board, from, to) case PieceType.QUEEN: return isValidQueenMove(board, from, to) case PieceType.KING: return isValidKingMove(board, from, to) default: return false } } // Check if a pawn move is valid function isValidPawnMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { const piece = board[from.row][from.col] if (!piece || piece.type !== PieceType.PAWN) return false const direction = piece.color === PieceColor.WHITE ? -1 : 1 const startRow = piece.color === PieceColor.WHITE ? 6 : 1 // Moving forward one square if (from.col === to.col && from.row + direction === to.row && !board[to.row][to.col]) { return true } // Moving forward two squares from starting position if ( from.col === to.col && from.row === startRow && from.row + 2 * direction === to.row && !board[from.row + direction][from.col] && !board[to.row][to.col] ) { return true } // Capturing diagonally if ((from.col + 1 === to.col || from.col - 1 === to.col) && from.row + direction === to.row) { return board[to.row][to.col] !== null } return false } // Check if a rook move is valid function isValidRookMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { // Rooks move horizontally or vertically if (from.row !== to.row && from.col !== to.col) { return false } // Check if the path is clear if (from.row === to.row) { // Horizontal movement const start = Math.min(from.col, to.col) const end = Math.max(from.col, to.col) for (let col = start + 1; col < end; col++) { if (board[from.row][col] !== null) { return false } } } else { // Vertical movement const start = Math.min(from.row, to.row) const end = Math.max(from.row, to.row) for (let row = start + 1; row < end; row++) { if (board[row][from.col] !== null) { return false } } } return true } // Check if a knight move is valid function isValidKnightMove(from: Position, to: Position): boolean { const rowDiff = Math.abs(from.row - to.row) const colDiff = Math.abs(from.col - to.col) // Knights move in an L-shape: 2 squares in one direction and 1 square perpendicular return (rowDiff === 2 && colDiff === 1) || (rowDiff === 1 && colDiff === 2) } // Check if a bishop move is valid function isValidBishopMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { const rowDiff = Math.abs(from.row - to.row) const colDiff = Math.abs(from.col - to.col) // Bishops move diagonally if (rowDiff !== colDiff) { return false } // Check if the path is clear const rowDirection = from.row < to.row ? 1 : -1 const colDirection = from.col < to.col ? 1 : -1 for (let i = 1; i < rowDiff; i++) { if (board[from.row + i * rowDirection][from.col + i * colDirection] !== null) { return false } } return true } // Check if a queen move is valid function isValidQueenMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { // Queens can move like rooks or bishops return isValidRookMove(board, from, to) || isValidBishopMove(board, from, to) } // Check if a king move is valid function isValidKingMove(board: (ChessPiece | null)[][], from: Position, to: Position): boolean { const rowDiff = Math.abs(from.row - to.row) const colDiff = Math.abs(from.col - to.col) // Kings move one square in any direction if (rowDiff <= 1 && colDiff <= 1) { return true } // Check for castling const piece = board[from.row][from.col] if (piece && piece.type === PieceType.KING && !piece.hasMoved && rowDiff === 0 && colDiff === 2) { // Determine if it's kingside or queenside castling const isKingSide = to.col > from.col const rookCol = isKingSide ? 7 : 0 // Check if the rook is in place and hasn't moved const rook = board[from.row][rookCol] if (!rook || rook.type !== PieceType.ROOK || rook.color !== piece.color || rook.hasMoved) { return false } // Check if the path is clear const start = isKingSide ? from.col + 1 : rookCol + 1 const end = isKingSide ? rookCol : from.col for (let col = start; col < end; col++) { if (board[from.row][col] !== null) { return false } } // Check if the king is in check or would pass through check const tempBoard = cloneBoard(board) tempBoard[from.row][from.col] = null // Check if the king is in check if (isKingInCheck(tempBoard, piece.color)) { return false } // Check if the king would pass through check tempBoard[from.row][isKingSide ? from.col + 1 : from.col - 1] = piece if (isKingInCheck(tempBoard, piece.color)) { return false } return true } return false } // Get all possible capture moves for a player (for ghost chess) export function getCaptureMoves(board: (ChessPiece | null)[][], color: PieceColor): { from: Position; to: Position }[] { const captureMoves: { from: Position; to: Position }[] = [] for (let fromRow = 0; fromRow < 8; fromRow++) { for (let fromCol = 0; fromCol < 8; fromCol++) { const piece = board[fromRow][fromCol] if (piece && piece.color === color) { for (let toRow = 0; toRow < 8; toRow++) { for (let toCol = 0; toCol < 8; toCol++) { const targetPiece = board[toRow][toCol] if (targetPiece && targetPiece.color !== color) { // Check if this piece can capture the target if (canPieceMove(board, { row: fromRow, col: fromCol }, { row: toRow, col: toCol })) { captureMoves.push({ from: { row: fromRow, col: fromCol }, to: { row: toRow, col: toCol } }) } } } } } } } return captureMoves } // Check if a player has lost all their pieces (ghost chess win condition) export function hasLostAllPieces(board: (ChessPiece | null)[][], color: PieceColor): boolean { for (let row = 0; row < 8; row++) { for (let col = 0; col < 8; col++) { const piece = board[row][col] if (piece && piece.color === color) { return false } } } return true }