/** * main.js - Application entry point * Initializes game and connects all components */ import { GameController } from './controllers/GameController.js'; import { BoardRenderer } from './views/BoardRenderer.js'; import { DragDropHandler } from './controllers/DragDropHandler.js'; class ChessApp { constructor() { // Initialize components this.game = new GameController({ autoSave: true, enableTimer: false }); this.renderer = new BoardRenderer( document.getElementById('chess-board'), { showCoordinates: true, pieceStyle: 'symbols', highlightLastMove: true } ); this.dragDropHandler = new DragDropHandler(this.game, this.renderer); // Initialize UI this.initializeUI(); this.setupEventListeners(); this.setupGameEventListeners(); // Start new game this.game.newGame(); this.updateDisplay(); } /** * Initialize UI components */ initializeUI() { // Render initial board this.renderer.renderBoard(this.game.board, this.game.gameState); // Setup drag and drop this.dragDropHandler.setupEventListeners(); // Update status this.updateTurnIndicator(); } /** * Setup button event listeners */ setupEventListeners() { // New Game document.getElementById('btn-new-game').addEventListener('click', () => { if (confirm('Start a new game? Current game will be lost.')) { this.game.newGame(); this.updateDisplay(); this.showMessage('New game started!'); } }); // Undo document.getElementById('btn-undo').addEventListener('click', () => { if (this.game.undo()) { this.updateDisplay(); this.showMessage('Move undone'); } else { this.showMessage('Nothing to undo'); } }); // Redo document.getElementById('btn-redo').addEventListener('click', () => { if (this.game.redo()) { this.updateDisplay(); this.showMessage('Move redone'); } else { this.showMessage('Nothing to redo'); } }); // Offer Draw document.getElementById('btn-offer-draw').addEventListener('click', () => { this.game.offerDraw(); this.showMessage('Draw offered to opponent'); }); // Resign document.getElementById('btn-resign').addEventListener('click', () => { if (confirm('Are you sure you want to resign?')) { this.game.resign(); } }); } /** * Setup game event listeners */ setupGameEventListeners() { // Move made this.game.on('move', (data) => { this.updateDisplay(); this.playMoveSound(); }); // Check this.game.on('check', (data) => { this.showMessage(`Check! ${data.color} king is in check`); this.playCheckSound(); }); // Checkmate this.game.on('checkmate', (data) => { this.showMessage(`Checkmate! ${data.winner} wins!`, 'success'); this.dragDropHandler.disable(); this.playCheckmateSound(); }); // Stalemate this.game.on('stalemate', () => { this.showMessage('Stalemate! Game is a draw', 'info'); this.dragDropHandler.disable(); }); // Draw this.game.on('draw', (data) => { this.showMessage(`Draw by ${data.reason}`, 'info'); this.dragDropHandler.disable(); }); // Resign this.game.on('resign', (data) => { const winner = data.loser === 'white' ? 'Black' : 'White'; this.showMessage(`${data.loser} resigned. ${winner} wins!`, 'success'); this.dragDropHandler.disable(); }); // Promotion this.game.on('promotion', (data) => { this.showPromotionDialog(data.pawn, data.position); }); // New Game this.game.on('newgame', () => { this.dragDropHandler.enable(); this.updateDisplay(); }); } /** * Update all display elements */ updateDisplay() { // Re-render board this.renderer.renderBoard(this.game.board, this.game.gameState); // Update turn indicator this.updateTurnIndicator(); // Update move history this.updateMoveHistory(); // Update captured pieces this.updateCapturedPieces(); } /** * Update turn indicator */ updateTurnIndicator() { const indicator = document.getElementById('turn-indicator'); const turn = this.game.currentTurn; indicator.textContent = `${turn.charAt(0).toUpperCase() + turn.slice(1)} to move`; indicator.style.color = turn === 'white' ? '#ffffff' : '#333333'; } /** * Update move history display */ updateMoveHistory() { const moveList = document.getElementById('move-list'); const history = this.game.gameState.moveHistory; if (history.length === 0) { moveList.innerHTML = '

No moves yet

'; return; } let html = ''; for (let i = 0; i < history.length; i += 2) { const moveNumber = Math.floor(i / 2) + 1; const whiteMove = history[i]; const blackMove = history[i + 1]; html += `
${moveNumber}. ${whiteMove.notation}`; if (blackMove) { html += ` ${blackMove.notation}`; } html += '
'; } moveList.innerHTML = html; moveList.scrollTop = moveList.scrollHeight; } /** * Update captured pieces display */ updateCapturedPieces() { const whiteCaptured = document.getElementById('white-captured'); const blackCaptured = document.getElementById('black-captured'); const captured = this.game.gameState.capturedPieces; whiteCaptured.innerHTML = captured.black.map(piece => `${piece.getSymbol()}` ).join('') || '-'; blackCaptured.innerHTML = captured.white.map(piece => `${piece.getSymbol()}` ).join('') || '-'; } /** * Show message to user * @param {string} message - Message text * @param {string} type - Message type (info, success, error) */ showMessage(message, type = 'info') { const statusMessage = document.getElementById('status-message'); statusMessage.textContent = message; statusMessage.style.display = 'block'; // Auto-hide after 3 seconds setTimeout(() => { statusMessage.style.display = 'none'; }, 3000); } /** * Show promotion dialog * @param {Pawn} pawn - Pawn to promote * @param {Position} position - Pawn position */ showPromotionDialog(pawn, position) { const overlay = document.getElementById('promotion-overlay'); const dialog = document.getElementById('promotion-dialog'); overlay.style.display = 'block'; dialog.style.display = 'block'; // Update symbols for current color const symbols = pawn.color === 'white' ? { queen: '♕', rook: '♖', bishop: '♗', knight: '♘' } : { queen: '♛', rook: '♜', bishop: '♝', knight: '♞' }; document.querySelectorAll('.promotion-piece .symbol').forEach(el => { const type = el.parentElement.dataset.type; el.textContent = symbols[type]; el.style.color = pawn.color === 'white' ? '#ffffff' : '#000000'; }); // Handle selection const handleSelection = (e) => { const pieceType = e.currentTarget.dataset.type; // Promote pawn import('./engine/SpecialMoves.js').then(({ SpecialMoves }) => { SpecialMoves.promote(this.game.board, pawn, pieceType); this.updateDisplay(); }); // Hide dialog overlay.style.display = 'none'; dialog.style.display = 'none'; // Remove listeners document.querySelectorAll('.promotion-piece').forEach(el => { el.removeEventListener('click', handleSelection); }); }; document.querySelectorAll('.promotion-piece').forEach(el => { el.addEventListener('click', handleSelection); }); } /** * Play move sound (optional - can be implemented) */ playMoveSound() { // TODO: Add sound effect } /** * Play check sound (optional - can be implemented) */ playCheckSound() { // TODO: Add sound effect } /** * Play checkmate sound (optional - can be implemented) */ playCheckmateSound() { // TODO: Add sound effect } } // Initialize app when DOM is ready document.addEventListener('DOMContentLoaded', () => { window.chessApp = new ChessApp(); console.log('Chess game initialized successfully!'); });