refactor: NCS-19 Currying (#18)
Build & Test (NowChessSystems) TeamCity build finished

Summary

  - Curried candidateMoves, legalMoves, and applyMove in the RuleSet trait to separate (context) as the world being
  operated on from the computation parameter
  - Updated DefaultRules overrides and all internal call sites
  - Updated all external call sites: GameEngine, PgnParser, PgnExporter, ChessBoardView, and all affected tests

  Test plan

  - All existing tests pass (./gradlew build)
  - No behaviour changes — pure style refactoring, existing test suite is the regression guard

Co-authored-by: LQ63 <lkhermann@web.de>
Reviewed-on: #18
Reviewed-by: Janis <janis-e@gmx.de>
Co-authored-by: Leon Hermann <lq@blackhole.local>
Co-committed-by: Leon Hermann <lq@blackhole.local>
This commit was merged in pull request #18.
This commit is contained in:
2026-04-06 21:03:17 +02:00
committed by Janis
parent 638139602c
commit 217f14f899
9 changed files with 59 additions and 59 deletions
@@ -9,10 +9,10 @@ import de.nowchess.api.move.Move
*/
trait RuleSet:
/** All pseudo-legal moves for the piece on `square` (ignores check). */
def candidateMoves(context: GameContext, square: Square): List[Move]
def candidateMoves(context: GameContext)(square: Square): List[Move]
/** Legal moves for `square`: candidates that don't leave own king in check. */
def legalMoves(context: GameContext, square: Square): List[Move]
def legalMoves(context: GameContext)(square: Square): List[Move]
/** All legal moves for the side to move. */
def allLegalMoves(context: GameContext): List[Move]
@@ -36,4 +36,4 @@ trait RuleSet:
* Handles all special move types: castling, en passant, promotion.
* Updates castling rights, en passant square, half-move clock, turn, and move history.
*/
def applyMove(context: GameContext, move: Move): GameContext
def applyMove(context: GameContext)(move: Move): GameContext
@@ -26,7 +26,7 @@ object DefaultRules extends RuleSet:
// ── Public API ─────────────────────────────────────────────────────
override def candidateMoves(context: GameContext, square: Square): List[Move] =
override def candidateMoves(context: GameContext)(square: Square): List[Move] =
context.board.pieceAt(square).fold(List.empty[Move]) { piece =>
if piece.color != context.turn then List.empty[Move]
else piece.pieceType match
@@ -38,13 +38,13 @@ object DefaultRules extends RuleSet:
case PieceType.King => kingCandidates(context, square, piece.color)
}
override def legalMoves(context: GameContext, square: Square): List[Move] =
candidateMoves(context, square).filter { move =>
override def legalMoves(context: GameContext)(square: Square): List[Move] =
candidateMoves(context)(square).filter { move =>
!leavesKingInCheck(context, move)
}
override def allLegalMoves(context: GameContext): List[Move] =
Square.all.flatMap(sq => legalMoves(context, sq)).toList
Square.all.flatMap(sq => legalMoves(context)(sq)).toList
override def isCheck(context: GameContext): Boolean =
kingSquare(context.board, context.turn)
@@ -284,7 +284,7 @@ object DefaultRules extends RuleSet:
// ── Move application ───────────────────────────────────────────────
override def applyMove(context: GameContext, move: Move): GameContext =
override def applyMove(context: GameContext)(move: Move): GameContext =
val color = context.turn
val board = context.board