Some checks failed
Fixes #2 and #3 - DOM element ID mismatches causing UI features to fail Changes: - Update move history element ID from 'move-list' to 'move-history' (line 185) - Update white captured pieces ID from 'white-captured' to 'captured-white-pieces' (line 214) - Update black captured pieces ID from 'black-captured' to 'captured-black-pieces' (line 215) These changes align JavaScript DOM queries with the actual element IDs defined in index.html, enabling move history and captured pieces displays to function correctly. Impact: - Move history now displays correctly in the UI sidebar - Captured pieces now display correctly for both white and black - No changes to game logic or business rules - Zero regression risk (simple ID corrections) Testing: - ESLint passes with 0 errors (6 warnings pre-existing) - Changes verified against HTML element IDs in index.html 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
319 lines
9.3 KiB
JavaScript
319 lines
9.3 KiB
JavaScript
/**
|
|
* 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-history');
|
|
const history = this.game.gameState.moveHistory;
|
|
|
|
if (history.length === 0) {
|
|
moveList.innerHTML = '<p style="color: #999; font-style: italic;">No moves yet</p>';
|
|
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 += `<div>${moveNumber}. ${whiteMove.notation}`;
|
|
if (blackMove) {
|
|
html += ` ${blackMove.notation}`;
|
|
}
|
|
html += '</div>';
|
|
}
|
|
|
|
moveList.innerHTML = html;
|
|
moveList.scrollTop = moveList.scrollHeight;
|
|
}
|
|
|
|
/**
|
|
* Update captured pieces display
|
|
*/
|
|
updateCapturedPieces() {
|
|
const whiteCaptured = document.getElementById('captured-white-pieces');
|
|
const blackCaptured = document.getElementById('captured-black-pieces');
|
|
|
|
const captured = this.game.gameState.capturedPieces;
|
|
|
|
whiteCaptured.innerHTML = captured.black.map(piece =>
|
|
`<span class="captured-piece black">${piece.getSymbol()}</span>`
|
|
).join('') || '-';
|
|
|
|
blackCaptured.innerHTML = captured.white.map(piece =>
|
|
`<span class="captured-piece white">${piece.getSymbol()}</span>`
|
|
).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!');
|
|
});
|