/** * @file pawn-implementation.js * @description Complete example implementation of the Pawn class * Use this as a reference for implementing other pieces */ import Piece from '../models/Piece.js'; import { DIRECTIONS } from '../utils/Constants.js'; import { isValidPosition } from '../utils/Helpers.js'; /** * @class Pawn * @extends Piece * @description Implements pawn movement rules * * Rules: * - Moves forward one square (or two from starting position) * - Captures diagonally forward * - En passant capture * - Promotion on reaching opposite end */ class Pawn extends Piece { constructor(color, position) { super(color, position, 'pawn'); /** * @property {number} _direction - Movement direction (-1 for white, 1 for black) */ this._direction = color === 'white' ? -1 : 1; /** * @property {number} _startRow - Starting row (6 for white, 1 for black) */ this._startRow = color === 'white' ? 6 : 1; } /** * Gets all valid moves for this pawn * * @param {Board} board - Current board state * @returns {Array} Array of valid positions */ getValidMoves(board) { const moves = []; const { row, col } = this.position; // One square forward const oneForward = { row: row + this._direction, col }; if (isValidPosition(oneForward.row, oneForward.col)) { const piece = board.getPieceAt(oneForward); if (!piece) { moves.push(oneForward); // Two squares forward (only from starting position) if (!this.hasMoved && row === this._startRow) { const twoForward = { row: row + (2 * this._direction), col }; const pieceTwoForward = board.getPieceAt(twoForward); if (!pieceTwoForward) { moves.push(twoForward); } } } } // Diagonal captures const captureOffsets = [-1, 1]; for (const offset of captureOffsets) { const capturePos = { row: row + this._direction, col: col + offset }; if (isValidPosition(capturePos.row, capturePos.col)) { const piece = board.getPieceAt(capturePos); if (piece && piece.color !== this.color) { moves.push(capturePos); } } } // TODO: Add en passant logic // Check if adjacent square has enemy pawn that just moved two squares // Add the en passant capture move if valid return moves; } /** * Checks if this pawn can be promoted * * @returns {boolean} True if on promotion row */ canPromote() { const promotionRow = this.color === 'white' ? 0 : 7; return this.position.row === promotionRow; } /** * Clones this pawn * * @returns {Pawn} Cloned pawn */ clone() { const clone = new Pawn(this.color, { ...this.position }); clone.hasMoved = this.hasMoved; return clone; } } export default Pawn; /** * IMPLEMENTATION NOTES: * * 1. Direction handling: * - White pawns move "up" the board (row decreases) * - Black pawns move "down" the board (row increases) * - Use _direction multiplier to handle both cases * * 2. Two-square move: * - Only allowed from starting position * - Must check that both squares are empty * - Sets up potential en passant capture * * 3. En passant: * - Complex special move requiring game state * - Need to check if adjacent pawn just moved two squares * - Capture happens "in passing" on empty square * * 4. Promotion: * - Handled by game controller, not move validation * - Pawn reaches opposite end (row 0 for white, row 7 for black) * - Player chooses replacement piece (usually queen) * * 5. Common bugs to avoid: * - Forgetting pawns can't move backward * - Allowing diagonal moves when no capture * - Allowing capture forward * - Forgetting to check if two-square path is clear */