Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.2 KiB
Currying Public API — Design Spec
Branch: feat/NCS-11 Date: 2026-03-31
Overview
Refactor the public methods of MoveValidator and GameRules to use multiple parameter groups (currying), separating (board[, history]) as the context group from the computation parameters. This is a pure style refactoring — no behaviour changes, no new tests required.
Motivation
Currying clarifies intent: (board, history) is "the world being operated on"; the remaining parameters are "what varies." It also enables partial application at call sites where board/history are fixed across a loop (e.g. isInCheck, legalMoves).
Section 1: Methods Being Curried
MoveValidator (public API only)
| Before | After |
|---|---|
legalTargets(board, from) |
legalTargets(board)(from) |
legalTargets(board, history, from) |
legalTargets(board, history)(from) |
isLegal(board, from, to) |
isLegal(board)(from, to) |
isLegal(board, history, from, to) |
isLegal(board, history)(from, to) |
isCastle(board, from, to) |
isCastle(board)(from, to) |
castlingTargets(board, history, color) |
castlingTargets(board, history)(color) |
castleSide(from, to) |
unchanged — no board parameter |
Private helpers (isOwnPiece, isEnemyPiece, slide, pawnTargets, knightTargets, kingTargets) are not changed.
GameRules (all public methods)
| Before | After |
|---|---|
isInCheck(board, color) |
isInCheck(board)(color) |
legalMoves(board, history, color) |
legalMoves(board, history)(color) |
gameStatus(board, history, color) |
gameStatus(board, history)(color) |
Section 2: Call Sites Updated
| File | Methods affected |
|---|---|
modules/core/src/main/scala/de/nowchess/chess/logic/MoveValidator.scala |
Internal calls to legalTargets, castlingTargets, isCastle |
modules/core/src/main/scala/de/nowchess/chess/logic/GameRules.scala |
MoveValidator.legalTargets, MoveValidator.isCastle, isInCheck |
modules/core/src/main/scala/de/nowchess/chess/controller/GameController.scala |
MoveValidator.isLegal, MoveValidator.isCastle, GameRules.gameStatus |
modules/core/src/test/scala/de/nowchess/chess/logic/MoveValidatorTest.scala |
legalTargets, isLegal, castlingTargets |
modules/core/src/test/scala/de/nowchess/chess/logic/GameRulesTest.scala |
isInCheck, legalMoves, gameStatus |
EnPassantCalculator, CastlingRightsCalculator, and their tests are not touched.
Section 3: Concrete Style Gain
In GameRules.isInCheck and legalMoves, board is passed to legalTargets on every loop iteration today. After currying, it is factored out as a single partial application:
// Before
board.pieces.exists { case (sq, piece) =>
piece.color != color &&
MoveValidator.legalTargets(board, sq).contains(kingSq)
}
// After
val targets = MoveValidator.legalTargets(board)
board.pieces.exists { case (sq, piece) =>
piece.color != color &&
targets(sq).contains(kingSq)
}
Section 4: Testing
Pure refactoring — no new tests. All existing tests must pass after call sites are updated. The test suite is the regression guard.