musical-chess / src /App.tsx
Maximus Powers
final
3568151
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