module Chess.Internal.Game (applyMove, isCheckmate, isStalemate, isInsufficientMaterial, isDraw, getWinner) where import Data.List import Chess.Internal.Piece import Chess.Internal.Move import Chess.Internal.Board import Data.Maybe applyMove :: GameState -> Move -> Either MoveError GameState applyMove game move = case moveError of Just mError -> Left mError Nothing -> Right newGame where moveError = isMoveError game move newGame = State (updateBoard game move) (updatePlayer game) (updateWhiteCastlings game move) (updateBlackCastlings game move) (updateEnPassantSquare move) (updateHalfMoveClock game move) (updateMoveNumber game) updateBoard :: GameState -> Move -> Board updateBoard game move = fromJust $ boardAfterMove (stateBoard game) move updatePlayer :: GameState -> Color updatePlayer game = opponent (currentPlayer game) updateWhiteCastlings :: GameState -> Move -> [CastlingType] updateWhiteCastlings (State _ _ castlings _ _ _ _) (Movement (Piece White Rook) (7, 0) _) = delete Long castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Movement (Piece White Rook) (7, 7) _) = delete Short castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Capture (Piece White Rook) (7, 0) _) = delete Long castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Capture (Piece White Rook) (7, 7) _) = delete Short castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Capture (Piece Black _) _ (7, 0)) = delete Long castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Capture (Piece Black _) _ (7, 7)) = delete Short castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Promotion (Piece Black _) _ (7, 0) _) = delete Long castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) (Promotion (Piece Black _) _ (7, 7) _) = delete Short castlings updateWhiteCastlings (State _ _ castlings _ _ _ _) move = updateCastlings White castlings move updateBlackCastlings :: GameState -> Move -> [CastlingType] updateBlackCastlings (State _ _ _ castlings _ _ _) (Movement (Piece Black Rook) (0, 0) _) = delete Long castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Movement (Piece Black Rook) (0, 7) _) = delete Short castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Capture (Piece Black Rook) (0, 0) _) = delete Long castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Capture (Piece Black Rook) (0, 7) _) = delete Short castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Capture (Piece White _) _ (0, 0)) = delete Long castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Capture (Piece White _) _ (0, 7)) = delete Short castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Promotion (Piece White _) _ (0, 0) _) = delete Long castlings updateBlackCastlings (State _ _ _ castlings _ _ _) (Promotion (Piece White _) _ (0, 7) _) = delete Short castlings updateBlackCastlings (State _ _ _ castlings _ _ _) move = updateCastlings Black castlings move updateCastlings :: Color -> [CastlingType] -> Move -> [CastlingType] updateCastlings player _ (Castling movePlayer _) | player == movePlayer = [] updateCastlings player _ (Movement (Piece movePlayer King) _ _) | player == movePlayer = [] updateCastlings player _ (Capture (Piece movePlayer King) _ _) | player == movePlayer = [] updateCastlings _ castlings _ = castlings updateEnPassantSquare :: Move -> Maybe Coordinates updateEnPassantSquare (PawnDoubleMove (Piece White Pawn) _ (row, col)) = Just (row + 1, col) updateEnPassantSquare (PawnDoubleMove (Piece Black Pawn) _ (row, col)) = Just (row - 1, col) updateEnPassantSquare _ = Nothing updateHalfMoveClock :: GameState -> Move -> Integer updateHalfMoveClock _ Capture{} = 0 updateHalfMoveClock _ (Movement (Piece _ Pawn) _ _) = 0 updateHalfMoveClock _ (PawnDoubleMove (Piece _ Pawn) _ _) = 0 updateHalfMoveClock _ (Promotion (Piece _ Pawn) _ _ _) = 0 updateHalfMoveClock (State _ _ _ _ _ number _) _ = number + 1 updateMoveNumber :: GameState -> Integer updateMoveNumber (State _ White _ _ _ _ number) = number updateMoveNumber (State _ Black _ _ _ _ number) = number + 1 isCheckmate :: GameState -> Bool isCheckmate game@(State board player _ _ _ _ _) = null (generateAllMoves game) && isCheck board player isStalemate :: GameState -> Bool isStalemate game@(State board player _ _ _ _ _) = null (generateAllMoves game) && not (isCheck board player) isInsufficientMaterial :: GameState -> Bool isInsufficientMaterial game = isInsufficientMaterialByPieces whitePieces blackPieces || isInsufficientMaterialWithBishops board whitePieces blackPieces where board = stateBoard game whitePieces = delete King $ getPlayerPieces board White blackPieces = delete King $ getPlayerPieces board Black isInsufficientMaterialByPieces :: [PieceType] -> [PieceType] -> Bool isInsufficientMaterialByPieces [] [] = True isInsufficientMaterialByPieces [Bishop] [] = True isInsufficientMaterialByPieces [Knight] [] = True isInsufficientMaterialByPieces [] [Bishop] = True isInsufficientMaterialByPieces [] [Knight] = True isInsufficientMaterialByPieces _ _ = False isInsufficientMaterialWithBishops :: Board -> [PieceType] -> [PieceType] -> Bool isInsufficientMaterialWithBishops _ white black | not (onlyBishops white && onlyBishops black) = False where onlyBishops pieces = not (any (/= Bishop) pieces) isInsufficientMaterialWithBishops board _ _ = bishopsOnWhite /= bishopsOnBlack where whiteSquaresWithBishops = filter (\x -> getSquareColor x == White) $ getSquaresWithPieces board Bishop blackSquaresWithBishops = filter (\x -> getSquareColor x == Black) $ getSquaresWithPieces board Bishop bishopsOnWhite = not (null whiteSquaresWithBishops) bishopsOnBlack = not (null blackSquaresWithBishops) isDraw :: GameState -> Bool isDraw game = isStalemate game || isInsufficientMaterial game getWinner :: GameState -> Maybe Color getWinner game | isCheckmate game = Just $ opponent $ currentPlayer game | otherwise = Nothing