import { useState, useCallback, useRef, useEffect } from 'react' import { Chess, Square, Move, Color } from 'chess.js' import { GameState, GameHistoryEntry, DraggedPiece } from '../types/chess' import { evaluateGameState, isPotentialPromotion } from '../utils/chessUtils' import { ChessAI } from '../engines/ChessAI' export function useChessGame() { const [gameState, setGameState] = useState({ board: new Chess(), gameActive: false, playerColor: 'w', selectedSquare: null, legalMoves: [], gameHistory: [], gameOver: false, gameResult: null, promotionMove: null, promotionDialogActive: false, aiThinking: false, aiModelLoaded: false, aiModelLoading: false }) const [selectedModel, setSelectedModel] = useState('mlabonne/chesspythia-70m') const [draggedPiece, setDraggedPiece] = useState(null) const aiTimeoutRef = useRef() const chessAI = useRef(null) // init chess model useEffect(() => { const initializeAI = async () => { setGameState(prev => ({ ...prev, aiModelLoading: true })) chessAI.current = new ChessAI(selectedModel) try { await chessAI.current.initialize() setGameState(prev => ({ ...prev, aiModelLoaded: true, aiModelLoading: false })) console.log(`Chess AI model ${selectedModel} loaded successfully`) } catch (error) { console.error(`Failed to load Chess AI model ${selectedModel}:`, error) setGameState(prev => ({ ...prev, aiModelLoaded: false, aiModelLoading: false })) } } initializeAI() return () => { if (aiTimeoutRef.current) { clearTimeout(aiTimeoutRef.current) } } }, [selectedModel]) const startNewGame = useCallback(() => { clearTimeout(aiTimeoutRef.current) setGameState(prev => ({ ...prev, board: new Chess(), gameActive: true, gameOver: false, gameHistory: [], selectedSquare: null, legalMoves: [], gameResult: null, promotionDialogActive: false, promotionMove: null, aiThinking: false })) if (gameState.playerColor === 'b') { setGameState(prev => ({ ...prev, aiThinking: true })) aiTimeoutRef.current = setTimeout(() => { makeAIMove() }, 500) } }, [gameState.playerColor]) const resignGame = useCallback(() => { clearTimeout(aiTimeoutRef.current) const winner = gameState.playerColor === 'w' ? 'b' : 'w' const gameResult = { isGameOver: true, winner: winner as Color, message: `Game Over! ${gameState.playerColor === 'w' ? 'White' : 'Black'} resigned - ${winner === 'w' ? 'White' : 'Black'} wins!`, terminationReason: 'resignation', details: `${gameState.playerColor === 'w' ? 'White' : 'Black'} player resigned the game` } setGameState(prev => ({ ...prev, gameActive: false, gameOver: true, gameResult, aiThinking: false })) }, [gameState.playerColor]) const togglePlayerColor = useCallback(() => { if (!gameState.gameActive) { setGameState(prev => ({ ...prev, playerColor: prev.playerColor === 'w' ? 'b' : 'w' })) } }, [gameState.gameActive]) const selectSquare = useCallback((square: Square) => { if (!gameState.gameActive || gameState.board.turn() !== gameState.playerColor || gameState.aiThinking) { return } const piece = gameState.board.get(square) if (piece && piece.color === gameState.playerColor) { const moves = gameState.board.moves({ square, verbose: true }) setGameState(prev => ({ ...prev, selectedSquare: square, legalMoves: moves })) } }, [gameState]) const attemptMove = useCallback((from: Square, to: Square) => { console.log('attemptMove called:', from, 'to', to) if (!gameState.gameActive || gameState.board.turn() !== gameState.playerColor) { console.log('Move rejected: game not active or not player turn') return false } if (isPotentialPromotion(gameState.board, from, to)) { const possibleMoves = gameState.board.moves({ square: from, verbose: true }) const hasLegalPromotion = possibleMoves.some(move => move.to === to && move.promotion !== undefined ) if (hasLegalPromotion) { setGameState(prev => ({ ...prev, promotionMove: { from, to, promotion: 'q' } as Move, promotionDialogActive: true, selectedSquare: null, legalMoves: [] })) return true } } try { const testBoard = new Chess(gameState.board.fen()) const move = testBoard.move({ from, to }) if (move) { const historyEntry: GameHistoryEntry = { move: move.san, moveData: move, player: 'Human', timestamp: new Date(), capturedPiece: move.captured ? { type: move.captured, color: gameState.board.turn() === 'w' ? 'b' : 'w' } : undefined } setGameState(prev => ({ ...prev, board: testBoard, gameHistory: [...prev.gameHistory, historyEntry], selectedSquare: null, legalMoves: [] })) const gameResult = evaluateGameState(testBoard) if (gameResult.isGameOver) { setGameState(prev => ({ ...prev, gameActive: false, gameOver: true, gameResult })) } else { // trigger AI move setGameState(prev => ({ ...prev, aiThinking: true })) aiTimeoutRef.current = setTimeout(() => { makeAIMove() }, 1000) } return true } else { console.log('Move returned null/false') } } catch (error) { console.log('Invalid move attempted:', error) } return false }, [gameState]) const makeAIMove = useCallback(async () => { if (!chessAI.current) { setGameState(prev => ({ ...prev, aiThinking: false })) return } try { setGameState(prev => { const currentBoard = new Chess(prev.board.fen()) if (!prev.gameActive || currentBoard.turn() === prev.playerColor) { return { ...prev, aiThinking: false } } const possibleMoves = currentBoard.moves({ verbose: true }) if (possibleMoves.length === 0) { return { ...prev, aiThinking: false } } chessAI.current!.getMove(currentBoard, 10000).then(aiMove => { if (!aiMove) { setGameState(prev => ({ ...prev, aiThinking: false })) return } const moveResult = currentBoard.move(aiMove) if (!moveResult) { setGameState(prev => ({ ...prev, aiThinking: false })) return } const historyEntry: GameHistoryEntry = { move: moveResult.san, moveData: moveResult, player: 'AI', timestamp: new Date(), capturedPiece: moveResult.captured ? { type: moveResult.captured, color: currentBoard.turn() === 'w' ? 'b' : 'w' } : undefined } const gameResult = evaluateGameState(currentBoard) setGameState(prev => ({ ...prev, board: currentBoard, gameHistory: [...prev.gameHistory, historyEntry], aiThinking: false, gameActive: !gameResult.isGameOver, gameOver: gameResult.isGameOver, gameResult: gameResult.isGameOver ? gameResult : null })) }).catch(error => { console.error('Error in AI move generation:', error) setGameState(prev => ({ ...prev, aiThinking: false })) }) return prev }) } catch (error) { console.error('Error in AI move setup:', error) setGameState(prev => ({ ...prev, aiThinking: false })) } }, []) const completePromotion = useCallback((promotionPiece: 'q' | 'r' | 'b' | 'n') => { if (!gameState.promotionMove) return try { const testBoard = new Chess(gameState.board.fen()) const move = testBoard.move({ from: gameState.promotionMove.from, to: gameState.promotionMove.to, promotion: promotionPiece }) if (move) { const historyEntry: GameHistoryEntry = { move: move.san, moveData: move, player: 'Human', timestamp: new Date(), capturedPiece: move.captured ? { type: move.captured, color: gameState.board.turn() === 'w' ? 'b' : 'w' } : undefined } setGameState(prev => ({ ...prev, board: testBoard, gameHistory: [...prev.gameHistory, historyEntry], selectedSquare: null, legalMoves: [], promotionDialogActive: false, promotionMove: null })) const gameResult = evaluateGameState(testBoard) if (gameResult.isGameOver) { setGameState(prev => ({ ...prev, gameActive: false, gameOver: true, gameResult })) } else { setGameState(prev => ({ ...prev, aiThinking: true })) aiTimeoutRef.current = setTimeout(() => { makeAIMove() }, 1000) } } else { setGameState(prev => ({ ...prev, promotionDialogActive: false, promotionMove: null })) } } catch (error) { console.error('Error during promotion:', error) setGameState(prev => ({ ...prev, promotionDialogActive: false, promotionMove: null })) } }, [gameState.promotionMove, gameState.board, makeAIMove]) const startDrag = useCallback((square: Square) => { if (!gameState.gameActive || gameState.board.turn() !== gameState.playerColor || gameState.aiThinking) { return } const piece = gameState.board.get(square) if (piece && piece.color === gameState.playerColor) { setDraggedPiece({ piece, square }) const moves = gameState.board.moves({ square, verbose: true }) setGameState(prev => ({ ...prev, selectedSquare: square, legalMoves: moves })) } }, [gameState]) const endDrag = useCallback((targetSquare: Square | null) => { console.log('Ending drag at:', targetSquare, 'from:', draggedPiece?.square) let moveSuccessful = false if (draggedPiece && targetSquare && targetSquare !== draggedPiece.square) { moveSuccessful = attemptMove(draggedPiece.square, targetSquare) } setDraggedPiece(null) if (!moveSuccessful) { setGameState(prev => ({ ...prev, selectedSquare: null, legalMoves: [] })) } }, [draggedPiece, attemptMove]) const getAIStatus = useCallback(() => { if (!chessAI.current) return 'Not initialized' return chessAI.current.getModelInfo() }, []) const changeModel = useCallback((modelId: string) => { if (gameState.gameActive) return setSelectedModel(modelId) }, [gameState.gameActive]) useEffect(() => { return () => { if (aiTimeoutRef.current) { clearTimeout(aiTimeoutRef.current) } } }, []) return { gameState, draggedPiece, selectedModel, startNewGame, resignGame, togglePlayerColor, selectSquare, attemptMove, completePromotion, startDrag, endDrag, getAIStatus, changeModel } }