/** * Pawn.js - Pawn piece implementation * Handles forward movement, diagonal captures, en passant, and promotion */ import { Piece } from './Piece.js'; export class Pawn extends Piece { constructor(color, position) { super(color, position); this.type = 'pawn'; } /** * Get valid moves for pawn * @param {Board} board - Game board * @param {GameState} gameState - Optional game state for en passant * @returns {Position[]} Array of valid positions */ getValidMoves(board, gameState = null) { const moves = []; const direction = this.color === 'white' ? -1 : 1; const startRow = this.color === 'white' ? 6 : 1; const promotionRank = this.color === 'white' ? 0 : 7; // Forward one square const oneForward = this.position.row + direction; if (this.isInBounds(oneForward, this.position.col) && this.isEmpty(board, oneForward, this.position.col)) { const move = { row: oneForward, col: this.position.col }; if (oneForward === promotionRank) { move.promotion = true; } moves.push(move); // Forward two squares from starting position if (this.position.row === startRow) { const twoForward = this.position.row + (direction * 2); if (this.isEmpty(board, twoForward, this.position.col)) { moves.push({ row: twoForward, col: this.position.col }); } } } // Diagonal captures const captureOffsets = [-1, 1]; for (const offset of captureOffsets) { const captureRow = this.position.row + direction; const captureCol = this.position.col + offset; if (this.isInBounds(captureRow, captureCol)) { if (this.hasEnemyPiece(board, captureRow, captureCol)) { const move = { row: captureRow, col: captureCol }; if (captureRow === promotionRank) { move.promotion = true; } moves.push(move); } } } // En passant if (gameState && gameState.lastMove) { const enPassantMoves = this.getEnPassantMoves(board, gameState); for (const enPassantMove of enPassantMoves) { moves.push({ ...enPassantMove, enPassant: true }); } } return moves; } /** * Check if pawn can be promoted * @returns {boolean} True if at promotion rank */ canPromote() { const promotionRank = this.color === 'white' ? 0 : 7; return this.position.row === promotionRank; } /** * Get en passant target positions * @param {Board} board - Game board * @param {GameState} gameState - Game state with move history * @returns {Position[]} En passant target positions */ getEnPassantMoves(board, gameState) { const moves = []; const direction = this.color === 'white' ? -1 : 1; const enPassantRank = this.color === 'white' ? 3 : 4; // Must be on correct rank if (this.position.row !== enPassantRank) { return moves; } // Check adjacent squares for enemy pawns that just moved two squares const offsets = [-1, 1]; for (const offset of offsets) { const adjacentCol = this.position.col + offset; if (!this.isInBounds(this.position.row, adjacentCol)) { continue; } const adjacentPiece = board.getPiece(this.position.row, adjacentCol); if (adjacentPiece && adjacentPiece.type === 'pawn' && adjacentPiece.color !== this.color) { // Check if this pawn just moved two squares const lastMove = gameState.lastMove || (gameState.getLastMove && gameState.getLastMove()); if (lastMove && lastMove.piece === adjacentPiece && Math.abs(lastMove.to.row - lastMove.from.row) === 2) { const targetRow = this.position.row + direction; moves.push({ row: targetRow, col: adjacentCol }); } } } return moves; } }