# Implementation Guide - HTML Chess Game ## 🎯 Purpose This guide provides a step-by-step roadmap for implementing the HTML chess game. Follow this guide sequentially to build a robust, maintainable chess application. ## 📋 Prerequisites Before starting implementation: - [ ] Read [HANDOFF_CHECKLIST.md](HANDOFF_CHECKLIST.md) - [ ] Review [API_REFERENCE.md](API_REFERENCE.md) - [ ] Study architecture diagrams in [diagrams/](diagrams/) - [ ] Understand chess rules from [CHESS_RULES.md](CHESS_RULES.md) - [ ] Set up development environment (see [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md)) ## 🏗️ Implementation Phases ### Phase 1: Project Setup and Core Architecture (Days 1-3) #### 1.1 Initialize Project Structure ```bash # Create directory structure mkdir -p chess-game/{css,js/{game,pieces,moves,ui,utils},assets/pieces,tests/{unit,integration}} cd chess-game ``` #### 1.2 Create Base HTML (index.html) ```html Chess Game

Chess Game

``` **Key Points:** - Use semantic HTML5 elements - Include meta viewport for responsiveness - Link CSS files in correct order - Use type="module" for ES6 modules #### 1.3 Create Core Classes **Step 1:** Board.js - Board state management ```javascript // js/game/Board.js export class Board { constructor() { this.grid = this.initializeGrid(); this.setupInitialPosition(); } initializeGrid() { return Array(8).fill(null).map(() => Array(8).fill(null)); } setupInitialPosition() { // Initialize pieces in starting position // See API_REFERENCE.md for detailed method signatures } getPiece(row, col) { return this.grid[row][col]; } setPiece(row, col, piece) { this.grid[row][col] = piece; } movePiece(fromRow, fromCol, toRow, toCol) { // Move piece and return captured piece if any } } ``` **Step 2:** Piece.js - Base piece class ```javascript // js/pieces/Piece.js export class Piece { constructor(color, position) { this.color = color; // 'white' or 'black' this.position = position; // {row, col} this.hasMoved = false; } getValidMoves(board) { // Override in subclasses throw new Error('getValidMoves must be implemented'); } isValidMove(board, toRow, toCol) { const validMoves = this.getValidMoves(board); return validMoves.some(move => move.row === toRow && move.col === toCol ); } } ``` **Step 3:** ChessGame.js - Game controller ```javascript // js/game/ChessGame.js export class ChessGame { constructor() { this.board = new Board(); this.currentTurn = 'white'; this.gameState = 'active'; // 'active', 'check', 'checkmate', 'stalemate', 'draw' this.moveHistory = []; this.selectedSquare = null; } makeMove(fromRow, fromCol, toRow, toCol) { // Validate and execute move // Update game state // Record in history // Switch turns } } ``` **Testing Phase 1:** ```javascript // tests/unit/Board.test.js describe('Board', () => { it('should initialize 8x8 grid', () => { const board = new Board(); expect(board.grid.length).toBe(8); expect(board.grid[0].length).toBe(8); }); it('should setup initial position correctly', () => { const board = new Board(); // Verify piece positions }); }); ``` --- ### Phase 2: Piece Implementation (Days 4-7) #### 2.1 Implement Each Piece Type **Implementation Order (simplest to most complex):** 1. Rook (straight lines) 2. Bishop (diagonals) 3. Queen (rook + bishop) 4. Knight (L-shapes) 5. King (one square) 6. Pawn (most complex with special moves) **Example: Rook.js** ```javascript // js/pieces/Rook.js import { Piece } from './Piece.js'; export class Rook extends Piece { constructor(color, position) { super(color, position); this.type = 'rook'; } getValidMoves(board) { const moves = []; const directions = [ [-1, 0], // up [1, 0], // down [0, -1], // left [0, 1] // right ]; for (const [dRow, dCol] of directions) { let currentRow = this.position.row + dRow; let currentCol = this.position.col + dCol; while (this.isInBounds(currentRow, currentCol)) { const targetPiece = board.getPiece(currentRow, currentCol); if (!targetPiece) { // Empty square - can move here moves.push({row: currentRow, col: currentCol}); } else { // Piece in the way if (targetPiece.color !== this.color) { // Can capture opponent piece moves.push({row: currentRow, col: currentCol}); } break; // Can't move further in this direction } currentRow += dRow; currentCol += dCol; } } return moves; } isInBounds(row, col) { return row >= 0 && row < 8 && col >= 0 && col < 8; } } ``` **Critical Implementation Notes:** **Pawn.js Special Cases:** ```javascript getValidMoves(board) { const moves = []; const direction = this.color === 'white' ? -1 : 1; const startRow = this.color === 'white' ? 6 : 1; // Forward move const oneForward = this.position.row + direction; if (!board.getPiece(oneForward, this.position.col)) { moves.push({row: oneForward, col: this.position.col}); // Two squares forward from starting position if (this.position.row === startRow) { const twoForward = this.position.row + (direction * 2); if (!board.getPiece(twoForward, this.position.col)) { moves.push({row: twoForward, col: this.position.col}); } } } // Diagonal captures const captureOffsets = [-1, 1]; for (const offset of captureOffsets) { const captureCol = this.position.col + offset; const targetPiece = board.getPiece(oneForward, captureCol); if (targetPiece && targetPiece.color !== this.color) { moves.push({row: oneForward, col: captureCol}); } } // En passant (handled in SpecialMoves.js) return moves; } ``` **Testing Each Piece:** ```javascript // tests/unit/pieces/Rook.test.js describe('Rook', () => { it('should move vertically and horizontally', () => { const board = new Board(); const rook = new Rook('white', {row: 4, col: 4}); board.setPiece(4, 4, rook); const moves = rook.getValidMoves(board); // Should have 14 moves (7 vertical + 7 horizontal) expect(moves.length).toBe(14); }); it('should be blocked by pieces', () => { // Test blocking scenarios }); it('should capture opponent pieces', () => { // Test capture scenarios }); }); ``` --- ### Phase 3: Move Validation and Special Moves (Days 8-12) #### 3.1 MoveValidator.js **Purpose:** Validate moves, check for check/checkmate ```javascript // js/moves/MoveValidator.js export class MoveValidator { static isMoveLegal(board, piece, toRow, toCol, gameState) { // 1. Check if move is in piece's valid moves if (!piece.isValidMove(board, toRow, toCol)) { return false; } // 2. Simulate move const simulatedBoard = this.simulateMove(board, piece, toRow, toCol); // 3. Check if own king is in check after move if (this.isKingInCheck(simulatedBoard, piece.color)) { return false; } return true; } static isKingInCheck(board, color) { // Find king position const kingPos = this.findKing(board, color); // Check if any opponent piece can attack king for (let row = 0; row < 8; row++) { for (let col = 0; col < 8; col++) { const piece = board.getPiece(row, col); if (piece && piece.color !== color) { const moves = piece.getValidMoves(board); if (moves.some(m => m.row === kingPos.row && m.col === kingPos.col)) { return true; } } } } return false; } static isCheckmate(board, color) { // King must be in check if (!this.isKingInCheck(board, color)) { return false; } // Check if any legal move exists return !this.hasAnyLegalMove(board, color); } static isStalemate(board, color) { // King must NOT be in check if (this.isKingInCheck(board, color)) { return false; } // But no legal moves available return !this.hasAnyLegalMove(board, color); } } ``` #### 3.2 SpecialMoves.js **Castling Implementation:** ```javascript // js/moves/SpecialMoves.js export class SpecialMoves { static canCastle(board, king, rook) { // 1. Neither piece has moved if (king.hasMoved || rook.hasMoved) { return false; } // 2. No pieces between king and rook const [minCol, maxCol] = [ Math.min(king.position.col, rook.position.col), Math.max(king.position.col, rook.position.col) ]; for (let col = minCol + 1; col < maxCol; col++) { if (board.getPiece(king.position.row, col)) { return false; } } // 3. King not in check if (MoveValidator.isKingInCheck(board, king.color)) { return false; } // 4. King doesn't pass through check const direction = rook.position.col > king.position.col ? 1 : -1; for (let i = 1; i <= 2; i++) { const col = king.position.col + (direction * i); const simulatedBoard = this.simulateKingMove(board, king, col); if (MoveValidator.isKingInCheck(simulatedBoard, king.color)) { return false; } } return true; } static executeCastle(board, king, rook) { // Move king two squares // Move rook to other side of king // Mark both as moved } } ``` **En Passant Implementation:** ```javascript static canEnPassant(board, pawn, targetCol, gameState) { // 1. Pawn must be on correct rank const correctRank = pawn.color === 'white' ? 3 : 4; if (pawn.position.row !== correctRank) { return false; } // 2. Adjacent square has opponent pawn const adjacentPawn = board.getPiece(pawn.position.row, targetCol); if (!adjacentPawn || adjacentPawn.type !== 'pawn' || adjacentPawn.color === pawn.color) { return false; } // 3. That pawn just moved two squares const lastMove = gameState.moveHistory[gameState.moveHistory.length - 1]; if (!lastMove || lastMove.piece !== adjacentPawn) { return false; } const moveDistance = Math.abs(lastMove.to.row - lastMove.from.row); return moveDistance === 2; } ``` **Pawn Promotion:** ```javascript static canPromote(pawn) { const promotionRank = pawn.color === 'white' ? 0 : 7; return pawn.position.row === promotionRank; } static promote(board, pawn, pieceType) { // Replace pawn with chosen piece (queen, rook, bishop, knight) const PieceClass = this.getPieceClass(pieceType); const newPiece = new PieceClass(pawn.color, pawn.position); board.setPiece(pawn.position.row, pawn.position.col, newPiece); return newPiece; } ``` --- ### Phase 4: UI Implementation (Days 13-17) #### 4.1 BoardRenderer.js ```javascript // js/ui/BoardRenderer.js export class BoardRenderer { constructor(boardElement) { this.boardElement = boardElement; this.selectedSquare = null; this.highlightedMoves = []; } renderBoard(board, gameState) { this.boardElement.innerHTML = ''; for (let row = 0; row < 8; row++) { for (let col = 0; col < 8; col++) { const square = this.createSquare(row, col); const piece = board.getPiece(row, col); if (piece) { const pieceElement = this.createPieceElement(piece); square.appendChild(pieceElement); } this.boardElement.appendChild(square); } } } createSquare(row, col) { const square = document.createElement('div'); square.className = 'square'; square.classList.add((row + col) % 2 === 0 ? 'light' : 'dark'); square.dataset.row = row; square.dataset.col = col; return square; } createPieceElement(piece) { const pieceEl = document.createElement('div'); pieceEl.className = `piece ${piece.color} ${piece.type}`; pieceEl.draggable = true; pieceEl.innerHTML = this.getPieceSymbol(piece); return pieceEl; } highlightMoves(moves) { this.clearHighlights(); moves.forEach(move => { const square = this.getSquare(move.row, move.col); square.classList.add('legal-move'); }); this.highlightedMoves = moves; } clearHighlights() { this.highlightedMoves.forEach(move => { const square = this.getSquare(move.row, move.col); square.classList.remove('legal-move'); }); this.highlightedMoves = []; } } ``` #### 4.2 DragDropHandler.js ```javascript // js/ui/DragDropHandler.js export class DragDropHandler { constructor(game, renderer) { this.game = game; this.renderer = renderer; this.setupEventListeners(); } setupEventListeners() { const board = this.renderer.boardElement; board.addEventListener('dragstart', (e) => this.onDragStart(e)); board.addEventListener('dragover', (e) => this.onDragOver(e)); board.addEventListener('drop', (e) => this.onDrop(e)); board.addEventListener('dragend', (e) => this.onDragEnd(e)); // Also support click-to-move board.addEventListener('click', (e) => this.onClick(e)); } onDragStart(e) { if (!e.target.classList.contains('piece')) return; const square = e.target.parentElement; const row = parseInt(square.dataset.row); const col = parseInt(square.dataset.col); e.dataTransfer.setData('text/plain', JSON.stringify({row, col})); e.dataTransfer.effectAllowed = 'move'; // Highlight legal moves const piece = this.game.board.getPiece(row, col); if (piece && piece.color === this.game.currentTurn) { const legalMoves = this.game.getLegalMoves(piece); this.renderer.highlightMoves(legalMoves); } } onDragOver(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; } onDrop(e) { e.preventDefault(); const from = JSON.parse(e.dataTransfer.getData('text/plain')); const square = e.target.closest('.square'); if (!square) return; const toRow = parseInt(square.dataset.row); const toCol = parseInt(square.dataset.col); this.game.makeMove(from.row, from.col, toRow, toCol); } onDragEnd(e) { this.renderer.clearHighlights(); } } ``` #### 4.3 CSS Styling **board.css:** ```css #chess-board { display: grid; grid-template-columns: repeat(8, 60px); grid-template-rows: repeat(8, 60px); gap: 0; border: 2px solid #333; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .square { width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; position: relative; transition: background-color 0.2s; } .square.light { background-color: #f0d9b5; } .square.dark { background-color: #b58863; } .square.selected { background-color: #9bc700; } .square.legal-move::after { content: ''; width: 20px; height: 20px; border-radius: 50%; background-color: rgba(0, 0, 0, 0.2); position: absolute; } .square.legal-move.has-piece::after { width: 100%; height: 100%; border-radius: 0; border: 3px solid rgba(255, 0, 0, 0.5); } ``` --- ### Phase 5: Game State Management (Days 18-21) #### 5.1 GameState.js ```javascript // js/game/GameState.js export class GameState { constructor() { this.moveHistory = []; this.capturedPieces = {white: [], black: []}; this.currentMove = 0; this.status = 'active'; } recordMove(from, to, piece, captured, notation) { this.moveHistory.push({ from, to, piece, captured, notation, timestamp: Date.now() }); this.currentMove++; } undo() { if (this.currentMove === 0) return null; this.currentMove--; return this.moveHistory[this.currentMove]; } redo() { if (this.currentMove >= this.moveHistory.length) return null; const move = this.moveHistory[this.currentMove]; this.currentMove++; return move; } toFEN() { // Export game state to FEN notation // See CHESS_RULES.md for FEN format } fromFEN(fen) { // Import game state from FEN notation } toPGN() { // Export move history to PGN notation } } ``` #### 5.2 Storage Integration ```javascript // js/utils/storage.js export class GameStorage { static save(gameState, board) { const data = { fen: gameState.toFEN(), pgn: gameState.toPGN(), timestamp: Date.now() }; localStorage.setItem('chess-game-save', JSON.stringify(data)); } static load() { const saved = localStorage.getItem('chess-game-save'); return saved ? JSON.parse(saved) : null; } static clear() { localStorage.removeItem('chess-game-save'); } } ``` --- ### Phase 6: Testing and Polish (Days 22-25) #### 6.1 Unit Testing Checklist - [ ] All piece movement tests - [ ] Move validation tests - [ ] Special move tests (castling, en passant, promotion) - [ ] Check/checkmate detection tests - [ ] Stalemate detection tests - [ ] FEN/PGN notation tests - [ ] Storage tests #### 6.2 Integration Testing ```javascript // tests/integration/gameplay.test.js describe('Full Game Scenarios', () => { it('should handle Scholar\'s Mate', () => { const game = new ChessGame(); game.makeMove(6, 4, 4, 4); // e4 game.makeMove(1, 4, 3, 4); // e5 game.makeMove(7, 5, 4, 2); // Bc4 game.makeMove(1, 1, 2, 2); // Nc6 game.makeMove(7, 3, 3, 7); // Qh5 game.makeMove(1, 6, 2, 5); // Nf6 game.makeMove(3, 7, 1, 5); // Qxf7# expect(game.gameState.status).toBe('checkmate'); expect(game.winner).toBe('white'); }); }); ``` #### 6.3 Performance Optimization - Memoize legal move calculations - Use efficient data structures - Minimize DOM manipulations - Lazy load piece images - Debounce UI updates #### 6.4 Accessibility - Add ARIA labels - Keyboard navigation support - Screen reader compatibility - High contrast mode - Focus indicators --- ## 🚨 Common Pitfalls and Solutions ### Problem 1: Check Detection Recursion **Issue:** Checking for check while validating moves causes infinite recursion **Solution:** ```javascript // Separate validation from check detection static getValidMoves(board, piece) { // Get moves without check validation } static getLegalMoves(board, piece) { // Filter valid moves by check constraint return this.getValidMoves(board, piece) .filter(move => !this.leavesKingInCheck(board, piece, move)); } ``` ### Problem 2: En Passant State **Issue:** En passant opportunity expires after one turn **Solution:** Track in game state: ```javascript class GameState { constructor() { this.enPassantTarget = null; // Reset after each move } } ``` ### Problem 3: Castling Through Check **Issue:** Need to validate intermediate squares **Solution:** Check each square king passes through: ```javascript for (let col = kingCol; col !== targetCol; col += direction) { if (isSquareAttacked(board, kingRow, col, opponentColor)) { return false; } } ``` ### Problem 4: Drag and Drop Touch Support **Issue:** Drag and drop doesn't work on mobile **Solution:** Add touch event handlers: ```javascript boardElement.addEventListener('touchstart', handleTouchStart); boardElement.addEventListener('touchmove', handleTouchMove); boardElement.addEventListener('touchend', handleTouchEnd); ``` --- ## ✅ Implementation Checklist ### Core Functionality - [ ] Board initialization - [ ] All piece types implemented - [ ] Move validation working - [ ] Check detection - [ ] Checkmate detection - [ ] Stalemate detection - [ ] Castling - [ ] En passant - [ ] Pawn promotion ### UI - [ ] Board rendering - [ ] Piece rendering - [ ] Drag and drop - [ ] Click to move - [ ] Legal move highlighting - [ ] Game status display - [ ] Move history display - [ ] Captured pieces display ### Game Management - [ ] New game - [ ] Undo/redo - [ ] Save/load - [ ] Resign - [ ] Offer/accept draw ### Testing - [ ] Unit tests (80%+ coverage) - [ ] Integration tests - [ ] Manual testing completed - [ ] Browser compatibility tested ### Polish - [ ] Animations - [ ] Sound effects (optional) - [ ] Responsive design - [ ] Accessibility features - [ ] Error handling --- ## 📚 Next Steps After completing implementation: 1. Review [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) for deployment 2. Conduct code review using provided checklist 3. Perform user acceptance testing 4. Deploy to production --- **Questions?** Refer to [API_REFERENCE.md](API_REFERENCE.md) for detailed method signatures and [CHESS_RULES.md](CHESS_RULES.md) for chess logic clarification.