feat: add castling execution to processMove
Detect castle moves via MoveValidator.isCastle and dispatch to Board.withCastle so both king and rook are moved atomically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ package de.nowchess.chess.controller
|
||||
|
||||
import scala.io.StdIn
|
||||
import de.nowchess.api.board.{Board, Color, Piece}
|
||||
import de.nowchess.chess.logic.{GameContext, MoveValidator, GameRules, PositionStatus}
|
||||
import de.nowchess.chess.logic.{GameContext, MoveValidator, GameRules, PositionStatus, CastleSide, withCastle}
|
||||
import de.nowchess.chess.view.Renderer
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -48,7 +48,12 @@ object GameController:
|
||||
if !MoveValidator.isLegal(ctx, from, to) then
|
||||
MoveResult.IllegalMove
|
||||
else
|
||||
val (newBoard, captured) = ctx.board.withMove(from, to)
|
||||
val castleOpt = if MoveValidator.isCastle(ctx.board, from, to)
|
||||
then Some(MoveValidator.castleSide(from, to))
|
||||
else None
|
||||
val (newBoard, captured) = castleOpt match
|
||||
case Some(side) => (ctx.board.withCastle(turn, side), None)
|
||||
case None => ctx.board.withMove(from, to)
|
||||
val newCtx = ctx.copy(board = newBoard)
|
||||
GameRules.gameStatus(newCtx, turn.opposite) match
|
||||
case PositionStatus.Normal => MoveResult.Moved(newCtx, captured, turn.opposite)
|
||||
|
||||
@@ -203,3 +203,41 @@ class GameControllerTest extends AnyFunSuite with Matchers:
|
||||
GameController.gameLoop(GameContext(b), Color.White)
|
||||
output should include("captures")
|
||||
output should include("Black is in check!")
|
||||
|
||||
// ──── castling execution ─────────────────────────────────────────────
|
||||
|
||||
test("processMove: e1g1 returns Moved with king on g1 and rook on f1"):
|
||||
val ctx = GameContext(
|
||||
board = Board(Map(
|
||||
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
|
||||
)
|
||||
GameController.processMove(ctx, Color.White, "e1g1") match
|
||||
case MoveResult.Moved(newCtx, captured, newTurn) =>
|
||||
newCtx.board.pieceAt(sq(File.G, Rank.R1)) shouldBe Some(Piece.WhiteKing)
|
||||
newCtx.board.pieceAt(sq(File.F, Rank.R1)) shouldBe Some(Piece.WhiteRook)
|
||||
newCtx.board.pieceAt(sq(File.E, Rank.R1)) shouldBe None
|
||||
newCtx.board.pieceAt(sq(File.H, Rank.R1)) shouldBe None
|
||||
captured shouldBe None
|
||||
newTurn shouldBe Color.Black
|
||||
case other => fail(s"Expected Moved, got $other")
|
||||
|
||||
test("processMove: e1c1 returns Moved with king on c1 and rook on d1"):
|
||||
val ctx = GameContext(
|
||||
board = Board(Map(
|
||||
sq(File.E, Rank.R1) -> Piece.WhiteKing,
|
||||
sq(File.A, Rank.R1) -> Piece.WhiteRook,
|
||||
sq(File.H, Rank.R8) -> Piece.BlackKing
|
||||
)),
|
||||
whiteCastling = CastlingRights.Both,
|
||||
blackCastling = CastlingRights.None
|
||||
)
|
||||
GameController.processMove(ctx, Color.White, "e1c1") match
|
||||
case MoveResult.Moved(newCtx, _, _) =>
|
||||
newCtx.board.pieceAt(sq(File.C, Rank.R1)) shouldBe Some(Piece.WhiteKing)
|
||||
newCtx.board.pieceAt(sq(File.D, Rank.R1)) shouldBe Some(Piece.WhiteRook)
|
||||
case other => fail(s"Expected Moved, got $other")
|
||||
|
||||
Reference in New Issue
Block a user