refactor: migrate GameRules.legalMoves signature to GameContext

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
LQ63
2026-03-24 12:51:14 +01:00
parent ffe663a62e
commit 7c568581a7
2 changed files with 14 additions and 11 deletions
@@ -1,6 +1,7 @@
package de.nowchess.chess.logic
import de.nowchess.api.board.*
import de.nowchess.chess.logic.GameContext
enum PositionStatus:
case Normal, InCheck, Mated, Drawn
@@ -19,13 +20,13 @@ object GameRules:
}
/** All (from, to) moves for `color` that do not leave their own king in check. */
def legalMoves(board: Board, color: Color): Set[(Square, Square)] =
board.pieces
def legalMoves(ctx: GameContext, color: Color): Set[(Square, Square)] =
ctx.board.pieces
.collect { case (from, piece) if piece.color == color => from }
.flatMap { from =>
MoveValidator.legalTargets(board, from)
MoveValidator.legalTargets(ctx.board, from)
.filter { to =>
val (newBoard, _) = board.withMove(from, to)
val (newBoard, _) = ctx.board.withMove(from, to)
!isInCheck(newBoard, color)
}
.map(to => from -> to)
@@ -34,7 +35,7 @@ object GameRules:
/** Position status for the side whose turn it is (`color`). */
def gameStatus(board: Board, color: Color): PositionStatus =
val moves = legalMoves(board, color)
val moves = legalMoves(GameContext(board), color)
val inCheck = isInCheck(board, color)
if moves.isEmpty && inCheck then PositionStatus.Mated
else if moves.isEmpty then PositionStatus.Drawn
@@ -1,6 +1,7 @@
package de.nowchess.chess.logic
import de.nowchess.api.board.*
import de.nowchess.chess.logic.GameContext
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
@@ -9,6 +10,9 @@ class GameRulesTest extends AnyFunSuite with Matchers:
private def sq(f: File, r: Rank): Square = Square(f, r)
private def board(entries: (Square, Piece)*): Board = Board(entries.toMap)
/** Wrap a board in a GameContext with no castling rights — for non-castling tests. */
private def ctx(entries: (Square, Piece)*): GameContext = GameContext(Board(entries.toMap))
// ──── isInCheck ──────────────────────────────────────────────────────
test("isInCheck: king attacked by enemy rook on same rank"):
@@ -36,22 +40,20 @@ class GameRulesTest extends AnyFunSuite with Matchers:
test("legalMoves: move that exposes own king to rook is excluded"):
// White King E1, White Rook E4 (pinned on E-file), Black Rook E8
// Moving the White Rook off the E-file would expose the king
val b = board(
val moves = GameRules.legalMoves(ctx(
sq(File.E, Rank.R1) -> Piece.WhiteKing,
sq(File.E, Rank.R4) -> Piece.WhiteRook,
sq(File.E, Rank.R8) -> Piece.BlackRook
)
val moves = GameRules.legalMoves(b, Color.White)
), Color.White)
moves should not contain (sq(File.E, Rank.R4) -> sq(File.D, Rank.R4))
test("legalMoves: move that blocks check is included"):
// White King E1 in check from Black Rook E8; White Rook A5 can interpose on E5
val b = board(
val moves = GameRules.legalMoves(ctx(
sq(File.E, Rank.R1) -> Piece.WhiteKing,
sq(File.A, Rank.R5) -> Piece.WhiteRook,
sq(File.E, Rank.R8) -> Piece.BlackRook
)
val moves = GameRules.legalMoves(b, Color.White)
), Color.White)
moves should contain(sq(File.A, Rank.R5) -> sq(File.E, Rank.R5))
// ──── gameStatus ──────────────────────────────────────────────────────