Spaces:
Sleeping
Sleeping
| import { useState, useEffect, useRef } from 'react' | |
| import { ChessBoard } from './components/ChessBoard' | |
| import { GameControls } from './components/GameControls' | |
| import { PromotionDialog } from './components/PromotionDialog' | |
| import { AudioInfoPopup } from './components/AudioInfoPopup' | |
| import { useChessGame } from './hooks/useChessGame' | |
| import { AudioEngine } from './engines/AudioEngine' | |
| import './styles/App.css' | |
| function App() { | |
| const { | |
| gameState, | |
| draggedPiece, | |
| selectedModel, | |
| startNewGame, | |
| resignGame, | |
| togglePlayerColor, | |
| selectSquare, | |
| attemptMove, | |
| completePromotion, | |
| startDrag, | |
| endDrag, | |
| changeModel | |
| } = useChessGame() | |
| const [audioEnabled, setAudioEnabled] = useState(false) | |
| const [showAudioInfo, setShowAudioInfo] = useState(false) | |
| const [volumeSettings, setVolumeSettings] = useState<{ ambient: number, game: number}>({ | |
| ambient: 0.8, // Default even louder for ambient beats | |
| game: 0.08 // 8% default for game sounds (scaled) | |
| }) | |
| const audioEngineRef = useRef<AudioEngine | null>(null) | |
| // Initialize audio engine | |
| useEffect(() => { | |
| audioEngineRef.current = new AudioEngine() | |
| return () => { | |
| if (audioEngineRef.current) { | |
| audioEngineRef.current.cleanup() | |
| } | |
| } | |
| }, []) | |
| // Handle audio state changes | |
| useEffect(() => { | |
| if (audioEngineRef.current) { | |
| if (audioEnabled) { | |
| audioEngineRef.current.setVolume(0.7) | |
| audioEngineRef.current.setBoardFlipped(gameState.playerColor === 'b') | |
| if (gameState.gameActive) { | |
| audioEngineRef.current.updatePositionAudio(gameState.board, gameState.playerColor) | |
| } | |
| } else { | |
| audioEngineRef.current.setVolume(0) | |
| audioEngineRef.current.stopAllAudio() | |
| } | |
| } | |
| }, [audioEnabled, gameState.gameActive, gameState.playerColor]) | |
| // Handle move audio | |
| useEffect(() => { | |
| if (audioEnabled && audioEngineRef.current && gameState.gameHistory.length > 0) { | |
| const lastMove = gameState.gameHistory[gameState.gameHistory.length - 1] | |
| audioEngineRef.current.playMoveSound(lastMove.moveData, gameState.board, lastMove.capturedPiece) | |
| } | |
| }, [gameState.gameHistory.length, audioEnabled]) | |
| // Handle position audio updates | |
| useEffect(() => { | |
| if (audioEnabled && audioEngineRef.current && gameState.gameActive) { | |
| audioEngineRef.current.updateInitiativeVolumes(gameState.board, gameState.playerColor) | |
| } | |
| }, [gameState.board.fen(), audioEnabled, gameState.gameActive, gameState.playerColor]) | |
| // Stop audio when game ends | |
| useEffect(() => { | |
| if (audioEngineRef.current && gameState.gameOver) { | |
| audioEngineRef.current.stopPositionAudio() | |
| } | |
| }, [gameState.gameOver]) | |
| const handleStartGame = () => { | |
| startNewGame() | |
| // Enable audio context on user interaction | |
| if (audioEnabled && audioEngineRef.current) { | |
| audioEngineRef.current.ensureAudioContext() | |
| } | |
| } | |
| const handleToggleAudio = () => { | |
| setAudioEnabled(!audioEnabled) | |
| // Enable audio context on user interaction if turning on | |
| if (!audioEnabled && audioEngineRef.current) { | |
| audioEngineRef.current.ensureAudioContext() | |
| } | |
| } | |
| const handleVolumeChange = (type: "ambient" | "game", value: number) => { | |
| setVolumeSettings(prev => ({ | |
| ...prev, | |
| [type]: value | |
| })) | |
| // Update audio engine with new volume | |
| if (audioEngineRef.current) { | |
| switch (type) { | |
| case 'ambient': | |
| audioEngineRef.current.setAmbientVolume(value) | |
| break | |
| case 'game': | |
| audioEngineRef.current.setGameVolume(value) | |
| break | |
| } | |
| } | |
| } | |
| return ( | |
| <div className="app-container"> | |
| <div className="main-content"> | |
| <div className="header"> | |
| <h1 className="title">🎵♟️ Musical Chess</h1> | |
| <button | |
| className="audio-info-button" | |
| onClick={() => setShowAudioInfo(true)} | |
| title="Audio Guide" | |
| > | |
| ℹ️ | |
| </button> | |
| </div> | |
| <div className="game-area"> | |
| <div className="board-container"> | |
| <ChessBoard | |
| key={gameState.board.fen()} | |
| gameState={gameState} | |
| draggedPiece={draggedPiece} | |
| audioEngine={audioEngineRef.current} | |
| onSquareClick={selectSquare} | |
| onPieceDragStart={startDrag} | |
| onPieceDrop={endDrag} | |
| /> | |
| </div> | |
| <div className="sidebar"> | |
| <GameControls | |
| gameState={gameState} | |
| audioEnabled={audioEnabled} | |
| volumeSettings={volumeSettings} | |
| selectedModel={selectedModel} | |
| onStartGame={handleStartGame} | |
| onResignGame={resignGame} | |
| onToggleColor={togglePlayerColor} | |
| onToggleAudio={handleToggleAudio} | |
| onVolumeChange={handleVolumeChange} | |
| onModelChange={changeModel} | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| <PromotionDialog | |
| isVisible={gameState.promotionDialogActive} | |
| color={gameState.playerColor} | |
| onSelect={completePromotion} | |
| /> | |
| <AudioInfoPopup | |
| isVisible={showAudioInfo} | |
| onClose={() => setShowAudioInfo(false)} | |
| /> | |
| </div> | |
| ) | |
| } | |
| export default App |