[Critical] Chess: getPossibleMovesForPiece is incomplete (missing castling, en passant, double pawn push) - breaks checkmate/stalemate detection #55

Closed
opened 2026-05-05 04:14:51 +00:00 by spikerj · 1 comment
Owner

Severity: Critical (Game correctness)

File: spikersoft-angular/projects/spikersoft/src/app/_services/game/chess-game.service.ts (~773-834)

Problem: The recent fix for #51/#52 expanded getPossibleMovesForPiece to all piece types, but the implementation still omits:

  • Pawn double-push from starting rank
  • En passant capture
  • King castling (kingside/queenside)

Both hasAnyLegalMove and isKingInCheck depend on this helper. As a result, checkmate and stalemate detection will produce wrong verdicts when the only legal escape from check is a special move (e.g. king must castle to escape, or only en-passant captures the checking pawn).

Fix options:

  1. Have hasAnyLegalMove / check-detection helpers reuse getPossibleMoves against a synthesized minimal GameState (board + enPassantTarget + castlingRights + currentPlayer)
  2. Or extend getPossibleMovesForPiece to take full GameState and mirror logic from getPawnMoves / getKingMoves

Acceptance criteria:

  • Stalemate detected when only legal pawn move is double-push
  • Checkmate not falsely declared when castling is the legal escape
  • En passant correctly counted as legal escape from check
  • Regression tests for each scenario
**Severity:** Critical (Game correctness) **File:** `spikersoft-angular/projects/spikersoft/src/app/_services/game/chess-game.service.ts` (~773-834) **Problem:** The recent fix for #51/#52 expanded `getPossibleMovesForPiece` to all piece types, but the implementation still omits: - Pawn double-push from starting rank - En passant capture - King castling (kingside/queenside) Both `hasAnyLegalMove` and `isKingInCheck` depend on this helper. As a result, checkmate and stalemate detection will produce **wrong verdicts** when the only legal escape from check is a special move (e.g. king must castle to escape, or only en-passant captures the checking pawn). **Fix options:** 1. Have `hasAnyLegalMove` / check-detection helpers reuse `getPossibleMoves` against a synthesized minimal `GameState` (board + enPassantTarget + castlingRights + currentPlayer) 2. Or extend `getPossibleMovesForPiece` to take full `GameState` and mirror logic from `getPawnMoves` / `getKingMoves` **Acceptance criteria:** - [ ] Stalemate detected when only legal pawn move is double-push - [ ] Checkmate not falsely declared when castling is the legal escape - [ ] En passant correctly counted as legal escape from check - [ ] Regression tests for each scenario
Author
Owner

Resolved.

Introduced getPseudoLegalMovesWithState(piece, state), which dispatches to the existing state-aware getPawnMoves and getKingMoves for pawns and kings (so castling, en-passant, and double-push are included) and falls back to the board-only generator for other piece types. hasAnyLegalMove now uses this helper -- so checkmate and stalemate detection see special-move escapes.

Additionally fixed two related correctness bugs:

  1. applyMove was passing { ...state, board: newBoard } into hasAnyLegalMove, leaving stale castlingRights/enPassantTarget. Now it threads newCastlingRights, newEnPassantTarget, and newCurrentPlayer through the temp state.
  2. wouldMovePutKingInCheck did not simulate en-passant capture removal or rook movement during castling. It now removes the en-passant captured pawn and moves the rook for kingside/queenside castles before running the attacker scan.

File: spikersoft-angular/projects/spikersoft/src/app/_services/game/chess-game.service.ts. Tests added in #69.

**Resolved.** Introduced `getPseudoLegalMovesWithState(piece, state)`, which dispatches to the existing state-aware `getPawnMoves` and `getKingMoves` for pawns and kings (so castling, en-passant, and double-push are included) and falls back to the board-only generator for other piece types. `hasAnyLegalMove` now uses this helper -- so checkmate and stalemate detection see special-move escapes. Additionally fixed two related correctness bugs: 1. `applyMove` was passing `{ ...state, board: newBoard }` into `hasAnyLegalMove`, leaving stale `castlingRights`/`enPassantTarget`. Now it threads `newCastlingRights`, `newEnPassantTarget`, and `newCurrentPlayer` through the temp state. 2. `wouldMovePutKingInCheck` did not simulate en-passant capture removal or rook movement during castling. It now removes the en-passant captured pawn and moves the rook for kingside/queenside castles before running the attacker scan. File: `spikersoft-angular/projects/spikersoft/src/app/_services/game/chess-game.service.ts`. Tests added in #69.
Sign in to join this conversation.