Skip to content

Commit be454c1

Browse files
Copilotmgierschdev
andcommitted
fix: implement king-safety filtering in getValidMoves to prevent illegal moves
- Add filterMovesLeavingKingInCheck() to simulate moves and reject those leaving king in check - Extract getCandidateMoves() to avoid infinite recursion with isKingInCheck() - Update isKingInCheck() to use getCandidateMoves() instead of getValidMoves() - Enable tests for king moving into check and pinned pieces (both now pass) - Update castling test to reflect that castling is not yet implemented This fixes: - Kings can no longer move into check - Pinned pieces can no longer move and expose the king - All pieces respect king safety when calculating valid moves Co-authored-by: mgierschdev <62764972+mgierschdev@users.noreply.github.com>
1 parent 07cee54 commit be454c1

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

backend/src/main/java/com/backend/domain/Chessboard.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ public boolean isKingInCheck(Color color){
154154
for(int c = 0; c < board[r].length; c++){
155155
ChessPiece piece = board[r][c];
156156
if(piece.color() == opponent){
157-
Position[] moves = getValidMoves(new Position(r,c));
157+
// Use getCandidateMoves to avoid infinite recursion
158+
Position[] moves = getCandidateMoves(new Position(r,c), piece);
158159
for(Position pos : moves){
159160
if(pos.row == kingPos.row && pos.col == kingPos.col){
160161
return true;
@@ -300,7 +301,21 @@ public Position[] getValidMoves(Position position) {
300301
}
301302

302303
ChessPiece chessPiece = board[position.row][position.col];
304+
Position[] candidateMoves = getCandidateMoves(position, chessPiece);
303305

306+
// Filter out moves that would leave the player's king in check
307+
return filterMovesLeavingKingInCheck(position, candidateMoves, chessPiece.color());
308+
}
309+
310+
/**
311+
* Gets candidate moves for a piece without checking king safety.
312+
* This is used internally to avoid infinite recursion when checking if king is in check.
313+
*
314+
* @param position The position of the piece
315+
* @param chessPiece The piece to get moves for
316+
* @return Array of candidate moves (may leave king in check)
317+
*/
318+
private Position[] getCandidateMoves(Position position, ChessPiece chessPiece) {
304319
switch (chessPiece.type()) {
305320
case Pawn -> {
306321
return getValidMovesPawn(position, chessPiece);
@@ -320,8 +335,41 @@ public Position[] getValidMoves(Position position) {
320335
case Bishop -> {
321336
return getValidMovesBishop(position, chessPiece);
322337
}
338+
default -> {
339+
return new Position[0];
340+
}
341+
}
342+
}
343+
344+
/**
345+
* Filters out moves that would leave the player's king in check.
346+
* Simulates each move and checks if the king is in check after the move.
347+
*
348+
* @param from The source position
349+
* @param candidateMoves All potential moves for the piece
350+
* @param playerColor The color of the player making the move
351+
* @return Array of legal moves that don't leave king in check
352+
*/
353+
private Position[] filterMovesLeavingKingInCheck(Position from, Position[] candidateMoves, Color playerColor) {
354+
List<Position> legalMoves = new ArrayList<>();
355+
356+
for (Position to : candidateMoves) {
357+
// Simulate the move
358+
ChessPiece captured = simulateMove(from, to);
359+
360+
// Check if this move leaves the king in check
361+
boolean kingInCheck = isKingInCheck(playerColor);
362+
363+
// Undo the move
364+
undoMove(from, to, captured);
365+
366+
// If the king is not in check after this move, it's legal
367+
if (!kingInCheck) {
368+
legalMoves.add(to);
369+
}
323370
}
324-
return new Position[0];
371+
372+
return legalMoves.toArray(Position[]::new);
325373
}
326374

327375
private Position[] getValidMovesRock(Position position, ChessPiece chessPiece) {

backend/src/test/java/com/backend/domain/ChessRulesTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
public class ChessRulesTest {
2222

2323
@Test
24-
@Disabled("TODO: Implement castling through check validation - current implementation may not prevent this")
24+
@Disabled("TODO: Implement castling first, then add validation for castling through check")
2525
public void testCastlingThroughCheckIsRejected() {
2626
Chessboard chessboard = new Chessboard();
2727
ChessPiece[][] board = chessboard.getBoard();
@@ -88,7 +88,6 @@ public void testEnPassantOnlyOnImmediateNextMove() {
8888
}
8989

9090
@Test
91-
@Disabled("TODO: Implement pinned piece validation - current implementation may allow pinned pieces to move")
9291
public void testPinnedPieceCannotExposeKing() {
9392
Chessboard chessboard = new Chessboard();
9493
ChessPiece[][] board = chessboard.getBoard();
@@ -130,7 +129,6 @@ public void testPinnedPieceCannotExposeKing() {
130129
}
131130

132131
@Test
133-
@Disabled("TODO: Verify king cannot move into check - current implementation may have issues with this")
134132
public void testKingCannotMoveIntoCheck() {
135133
Chessboard chessboard = new Chessboard();
136134
ChessPiece[][] board = chessboard.getBoard();

0 commit comments

Comments
 (0)