feat: include castling moves in GameRules.legalMoves

Switch legalMoves to the context-aware MoveValidator.legalTargets(ctx, from)
so castling destinations are included, and simulate castle moves via withCastle
when filtering for self-check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
LQ63
2026-03-24 12:55:00 +01:00
parent 7c568581a7
commit 417a475d84
2 changed files with 32 additions and 2 deletions
@@ -24,9 +24,13 @@ object GameRules:
ctx.board.pieces
.collect { case (from, piece) if piece.color == color => from }
.flatMap { from =>
MoveValidator.legalTargets(ctx.board, from)
MoveValidator.legalTargets(ctx, from) // context-aware: includes castling
.filter { to =>
val (newBoard, _) = ctx.board.withMove(from, to)
val newBoard =
if MoveValidator.isCastle(ctx.board, from, to) then
ctx.board.withCastle(color, MoveValidator.castleSide(from, to))
else
ctx.board.withMove(from, to)._1
!isInCheck(newBoard, color)
}
.map(to => from -> to)
@@ -1,6 +1,7 @@
package de.nowchess.chess.logic
import de.nowchess.api.board.*
import de.nowchess.api.game.CastlingRights
import de.nowchess.chess.logic.GameContext
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
@@ -88,3 +89,28 @@ class GameRulesTest extends AnyFunSuite with Matchers:
test("gameStatus: normal starting position returns Normal"):
GameRules.gameStatus(Board.initial, Color.White) shouldBe PositionStatus.Normal
test("legalMoves: includes castling destination when available"):
val c = GameContext(
board = board(
sq(File.E, Rank.R1) -> Piece.WhiteKing,
sq(File.H, Rank.R1) -> Piece.WhiteRook,
sq(File.H, Rank.R8) -> Piece.BlackKing
),
whiteCastling = CastlingRights.Both,
blackCastling = CastlingRights.None
)
GameRules.legalMoves(c, Color.White) should contain(sq(File.E, Rank.R1) -> sq(File.G, Rank.R1))
test("legalMoves: excludes castling when king is in check"):
val c = GameContext(
board = board(
sq(File.E, Rank.R1) -> Piece.WhiteKing,
sq(File.H, Rank.R1) -> Piece.WhiteRook,
sq(File.E, Rank.R8) -> Piece.BlackRook,
sq(File.A, Rank.R8) -> Piece.BlackKing
),
whiteCastling = CastlingRights.Both,
blackCastling = CastlingRights.None
)
GameRules.legalMoves(c, Color.White) should not contain (sq(File.E, Rank.R1) -> sq(File.G, Rank.R1))