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 |