refactor: migrate GameRules.legalMoves signature to GameContext
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package de.nowchess.chess.logic
|
package de.nowchess.chess.logic
|
||||||
|
|
||||||
import de.nowchess.api.board.*
|
import de.nowchess.api.board.*
|
||||||
|
import de.nowchess.chess.logic.GameContext
|
||||||
|
|
||||||
enum PositionStatus:
|
enum PositionStatus:
|
||||||
case Normal, InCheck, Mated, Drawn
|
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. */
|
/** All (from, to) moves for `color` that do not leave their own king in check. */
|
||||||
def legalMoves(board: Board, color: Color): Set[(Square, Square)] =
|
def legalMoves(ctx: GameContext, color: Color): Set[(Square, Square)] =
|
||||||
board.pieces
|
ctx.board.pieces
|
||||||
.collect { case (from, piece) if piece.color == color => from }
|
.collect { case (from, piece) if piece.color == color => from }
|
||||||
.flatMap { from =>
|
.flatMap { from =>
|
||||||
MoveValidator.legalTargets(board, from)
|
MoveValidator.legalTargets(ctx.board, from)
|
||||||
.filter { to =>
|
.filter { to =>
|
||||||
val (newBoard, _) = board.withMove(from, to)
|
val (newBoard, _) = ctx.board.withMove(from, to)
|
||||||
!isInCheck(newBoard, color)
|
!isInCheck(newBoard, color)
|
||||||
}
|
}
|
||||||
.map(to => from -> to)
|
.map(to => from -> to)
|
||||||
@@ -34,7 +35,7 @@ object GameRules:
|
|||||||
|
|
||||||
/** Position status for the side whose turn it is (`color`). */
|
/** Position status for the side whose turn it is (`color`). */
|
||||||
def gameStatus(board: Board, color: Color): PositionStatus =
|
def gameStatus(board: Board, color: Color): PositionStatus =
|
||||||
val moves = legalMoves(board, color)
|
val moves = legalMoves(GameContext(board), color)
|
||||||
val inCheck = isInCheck(board, color)
|
val inCheck = isInCheck(board, color)
|
||||||
if moves.isEmpty && inCheck then PositionStatus.Mated
|
if moves.isEmpty && inCheck then PositionStatus.Mated
|
||||||
else if moves.isEmpty then PositionStatus.Drawn
|
else if moves.isEmpty then PositionStatus.Drawn
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.nowchess.chess.logic
|
package de.nowchess.chess.logic
|
||||||
|
|
||||||
import de.nowchess.api.board.*
|
import de.nowchess.api.board.*
|
||||||
|
import de.nowchess.chess.logic.GameContext
|
||||||
import org.scalatest.funsuite.AnyFunSuite
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
import org.scalatest.matchers.should.Matchers
|
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 sq(f: File, r: Rank): Square = Square(f, r)
|
||||||
private def board(entries: (Square, Piece)*): Board = Board(entries.toMap)
|
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 ──────────────────────────────────────────────────────
|
// ──── isInCheck ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
test("isInCheck: king attacked by enemy rook on same rank"):
|
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"):
|
test("legalMoves: move that exposes own king to rook is excluded"):
|
||||||
// White King E1, White Rook E4 (pinned on E-file), Black Rook E8
|
// 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
|
// 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.R1) -> Piece.WhiteKing,
|
||||||
sq(File.E, Rank.R4) -> Piece.WhiteRook,
|
sq(File.E, Rank.R4) -> Piece.WhiteRook,
|
||||||
sq(File.E, Rank.R8) -> Piece.BlackRook
|
sq(File.E, Rank.R8) -> Piece.BlackRook
|
||||||
)
|
), Color.White)
|
||||||
val moves = GameRules.legalMoves(b, Color.White)
|
|
||||||
moves should not contain (sq(File.E, Rank.R4) -> sq(File.D, Rank.R4))
|
moves should not contain (sq(File.E, Rank.R4) -> sq(File.D, Rank.R4))
|
||||||
|
|
||||||
test("legalMoves: move that blocks check is included"):
|
test("legalMoves: move that blocks check is included"):
|
||||||
// White King E1 in check from Black Rook E8; White Rook A5 can interpose on E5
|
// 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.E, Rank.R1) -> Piece.WhiteKing,
|
||||||
sq(File.A, Rank.R5) -> Piece.WhiteRook,
|
sq(File.A, Rank.R5) -> Piece.WhiteRook,
|
||||||
sq(File.E, Rank.R8) -> Piece.BlackRook
|
sq(File.E, Rank.R8) -> Piece.BlackRook
|
||||||
)
|
), Color.White)
|
||||||
val moves = GameRules.legalMoves(b, Color.White)
|
|
||||||
moves should contain(sq(File.A, Rank.R5) -> sq(File.E, Rank.R5))
|
moves should contain(sq(File.A, Rank.R5) -> sq(File.E, Rank.R5))
|
||||||
|
|
||||||
// ──── gameStatus ──────────────────────────────────────────────────────
|
// ──── gameStatus ──────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user