refactor(core): migrate GameState to GameContext and update FEN handling
Build & Test (NowChessSystems) TeamCity build failed

This commit is contained in:
2026-04-04 17:55:08 +02:00
parent 6283db85c0
commit c08e5f8c62
12 changed files with 196 additions and 193 deletions
@@ -8,6 +8,7 @@ import de.nowchess.api.board.{Color, Square}
* @param kingSide king-side castling still legally available
* @param queenSide queen-side castling still legally available
*/
@deprecated("Use de.nowchess.api.board.CastlingRights via GameContext.", "NCS-22")
final case class CastlingRights(kingSide: Boolean, queenSide: Boolean)
object CastlingRights:
@@ -15,12 +16,14 @@ object CastlingRights:
val Both: CastlingRights = CastlingRights(kingSide = true, queenSide = true)
/** Outcome of a finished game. */
@deprecated("Use GameContext and derive game lifecycle from rules/engine state.", "NCS-22")
enum GameResult:
case WhiteWins
case BlackWins
case Draw
/** Lifecycle state of a game. */
@deprecated("Use GameContext and engine events for lifecycle handling.", "NCS-22")
enum GameStatus:
case NotStarted
case InProgress
@@ -42,6 +45,7 @@ enum GameStatus:
* @param fullMoveNumber increments after Black's move, starts at 1
* @param status current lifecycle status of the game
*/
@deprecated("Use GameContext for runtime state; keep GameState only for legacy compatibility.", "NCS-22")
final case class GameState(
piecePlacement: String,
activeColor: Color,
@@ -0,0 +1,63 @@
package de.nowchess.api.game
import de.nowchess.api.board.{Board, CastlingRights, Color, File, Rank, Square}
import de.nowchess.api.move.Move
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
class GameContextTest extends AnyFunSuite with Matchers:
test("GameContext.initial has initial board"):
GameContext.initial.board shouldBe Board.initial
test("GameContext.initial active color is White"):
GameContext.initial.turn shouldBe Color.White
test("GameContext.initial has full castling rights"):
GameContext.initial.castlingRights shouldBe CastlingRights.Initial
test("GameContext.initial en-passant square is None"):
GameContext.initial.enPassantSquare shouldBe None
test("GameContext.initial half-move clock is 0"):
GameContext.initial.halfMoveClock shouldBe 0
test("GameContext.initial move history is empty"):
GameContext.initial.moves shouldBe List.empty
test("withBoard updates only board"):
val square = Square(File.E, Rank.R4)
val updatedBoard = Board.initial.updated(square, de.nowchess.api.board.Piece.WhiteQueen)
val updated = GameContext.initial.withBoard(updatedBoard)
updated.board shouldBe updatedBoard
updated.turn shouldBe GameContext.initial.turn
updated.castlingRights shouldBe GameContext.initial.castlingRights
updated.enPassantSquare shouldBe GameContext.initial.enPassantSquare
updated.halfMoveClock shouldBe GameContext.initial.halfMoveClock
updated.moves shouldBe GameContext.initial.moves
test("withTurn updates only turn"):
val updated = GameContext.initial.withTurn(Color.Black)
updated.turn shouldBe Color.Black
updated.board shouldBe GameContext.initial.board
test("withCastlingRights updates castling rights"):
val rights = CastlingRights(
whiteKingSide = true,
whiteQueenSide = false,
blackKingSide = false,
blackQueenSide = true
)
GameContext.initial.withCastlingRights(rights).castlingRights shouldBe rights
test("withEnPassantSquare updates en-passant square"):
val square = Some(Square(File.E, Rank.R3))
GameContext.initial.withEnPassantSquare(square).enPassantSquare shouldBe square
test("withHalfMoveClock updates half-move clock"):
GameContext.initial.withHalfMoveClock(17).halfMoveClock shouldBe 17
test("withMove appends move to history"):
val move = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4))
GameContext.initial.withMove(move).moves shouldBe List(move)
@@ -1,77 +0,0 @@
package de.nowchess.api.game
import de.nowchess.api.board.Color
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
class GameStateTest extends AnyFunSuite with Matchers:
test("CastlingRights.None has both flags false") {
CastlingRights.None.kingSide shouldBe false
CastlingRights.None.queenSide shouldBe false
}
test("CastlingRights.Both has both flags true") {
CastlingRights.Both.kingSide shouldBe true
CastlingRights.Both.queenSide shouldBe true
}
test("CastlingRights constructor sets fields") {
val cr = CastlingRights(kingSide = true, queenSide = false)
cr.kingSide shouldBe true
cr.queenSide shouldBe false
}
test("GameResult cases exist") {
GameResult.WhiteWins shouldBe GameResult.WhiteWins
GameResult.BlackWins shouldBe GameResult.BlackWins
GameResult.Draw shouldBe GameResult.Draw
}
test("GameStatus.NotStarted") {
GameStatus.NotStarted shouldBe GameStatus.NotStarted
}
test("GameStatus.InProgress") {
GameStatus.InProgress shouldBe GameStatus.InProgress
}
test("GameStatus.Finished carries result") {
val status = GameStatus.Finished(GameResult.Draw)
status shouldBe GameStatus.Finished(GameResult.Draw)
status match
case GameStatus.Finished(r) => r shouldBe GameResult.Draw
case _ => fail("expected Finished")
}
test("GameState.initial has standard FEN piece placement") {
GameState.initial.piecePlacement shouldBe "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
}
test("GameState.initial active color is White") {
GameState.initial.activeColor shouldBe Color.White
}
test("GameState.initial white has full castling rights") {
GameState.initial.castlingWhite shouldBe CastlingRights.Both
}
test("GameState.initial black has full castling rights") {
GameState.initial.castlingBlack shouldBe CastlingRights.Both
}
test("GameState.initial en-passant target is None") {
GameState.initial.enPassantTarget shouldBe None
}
test("GameState.initial half-move clock is 0") {
GameState.initial.halfMoveClock shouldBe 0
}
test("GameState.initial full-move number is 1") {
GameState.initial.fullMoveNumber shouldBe 1
}
test("GameState.initial status is InProgress") {
GameState.initial.status shouldBe GameStatus.InProgress
}