# NCS-22: Module Refactoring with Interface Abstraction > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) to implement this plan task-by-task. **Goal:** Split `modules/core` into clean layers (api → rule → core) with RuleSet as single source of truth for chess rules. **Architecture:** Three-layer model with immutable GameContext bundling all game state. RuleSet interface abstracts all rule decisions. GameEngine calls RuleSet directly; GameController removed. **Tech Stack:** Scala 3, Gradle, scoverage (100% coverage required) --- ### Task 1: Create GameContext immutable type in modules/api **Files:** - Create: `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` **Dependency:** modules/api depends only on itself (no other modules) - [ ] **Step 1: Write GameContext case class with all game state** ```scala package de.nowchess.api.game import de.nowchess.api.board.{Board, Color, Square} import de.nowchess.api.move.Move /** Immutable bundle of complete game state. * All state changes produce new GameContext instances. */ case class GameContext( board: Board, turn: Color, castlingRights: CastlingRights, enPassantSquare: Option[Square], halfMoveClock: Int, moves: List[Move] ): /** Create new context with updated board. */ def withBoard(newBoard: Board): GameContext = copy(board = newBoard) /** Create new context with updated turn. */ def withTurn(newTurn: Color): GameContext = copy(turn = newTurn) /** Create new context with updated castling rights. */ def withCastlingRights(newRights: CastlingRights): GameContext = copy(castlingRights = newRights) /** Create new context with updated en passant square. */ def withEnPassantSquare(newSq: Option[Square]): GameContext = copy(enPassantSquare = newSq) /** Create new context with updated half-move clock. */ def withHalfMoveClock(newClock: Int): GameContext = copy(halfMoveClock = newClock) /** Create new context with move appended to history. */ def withMove(move: Move): GameContext = copy(moves = moves :+ move) object GameContext: /** Initial position: white to move, all castling rights, no en passant. */ def initial: GameContext = GameContext( board = Board.initial, turn = Color.White, castlingRights = CastlingRights.initial, enPassantSquare = None, halfMoveClock = 0, moves = List.empty ) ``` - [ ] **Step 2: Create CastlingRights type in modules/api** Create `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala`: ```scala package de.nowchess.api.board case class CastlingRights( whiteKingSide: Boolean, whiteQueenSide: Boolean, blackKingSide: Boolean, blackQueenSide: Boolean ): def removeWhiteKingSide: CastlingRights = copy(whiteKingSide = false) def removeWhiteQueenSide: CastlingRights = copy(whiteQueenSide = false) def removeBlackKingSide: CastlingRights = copy(blackKingSide = false) def removeBlackQueenSide: CastlingRights = copy(blackQueenSide = false) object CastlingRights: def initial: CastlingRights = CastlingRights(true, true, true, true) ``` - [ ] **Step 3: Verify GameContext compiles** Run: `./gradlew :modules:api:compileScala` Expected: SUCCESS - [ ] **Step 4: Commit** ```bash git add modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala git add modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala git commit -m "feat(api): add immutable GameContext type" ``` --- ### Task 2: Refactor RuleSet interface in modules/rule **Files:** - Modify: `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` - [ ] **Step 1: Replace RuleSet with GameContext-based interface** ```scala package de.nowchess.rules import de.nowchess.api.game.GameContext import de.nowchess.api.board.Square import de.nowchess.api.move.Move /** Extension point for chess rule variants (standard, Chess960, etc.). * All rule queries are stateless: given a GameContext, return the answer. */ trait RuleSet: /** All pseudo-legal moves for the piece on `square` (ignores check). */ 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] /** All legal moves for the side to move. */ def allLegalMoves(context: GameContext): List[Move] /** True if the side to move's king is in check. */ def isCheck(context: GameContext): Boolean /** True if the side to move is in check and has no legal moves. */ def isCheckmate(context: GameContext): Boolean /** True if the side to move is not in check and has no legal moves. */ def isStalemate(context: GameContext): Boolean /** True if neither side has enough material to checkmate. */ def isInsufficientMaterial(context: GameContext): Boolean /** True if halfMoveClock >= 100 (50-move rule). */ def isFiftyMoveRule(context: GameContext): Boolean ``` - [ ] **Step 2: Verify RuleSet compiles** Run: `./gradlew :modules:rule:compileScala` Expected: SUCCESS - [ ] **Step 3: Commit** ```bash git add modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala git commit -m "refactor(rule): update RuleSet to use GameContext" ``` --- ### Task 3: Implement StandardRules move generation engine **Files:** - Modify: `modules/rule/src/main/scala/de/nowchess/rules/StandardRules.scala` Complete rewrite of StandardRules to implement all move generation logic using GameContext and NowChess types. - [ ] **Step 1: Rewrite StandardRules with full implementation** See plan file for complete StandardRules code. Includes: - Direction vectors and helpers - Public API (all RuleSet methods) - Move generation (pawns, knights, sliding pieces, kings, castling) - Check/checkmate/stalemate detection - Insufficient material detection - [ ] **Step 2: Verify StandardRules compiles** Run: `./gradlew :modules:rule:compileScala` Expected: SUCCESS - [ ] **Step 3: Commit** ```bash git add modules/rule/src/main/scala/de/nowchess/rules/StandardRules.scala git commit -m "refactor(rule): implement StandardRules with GameContext" ``` --- ### Task 4: Configure module dependencies **Files:** - Create: `modules/rule/build.gradle.kts` - Modify: `modules/core/build.gradle.kts` - [ ] **Step 1: Create modules/rule/build.gradle.kts** See plan file for full gradle config (standard Scala module setup with api dependency). - [ ] **Step 2: Modify modules/core/build.gradle.kts** Add `implementation(project(":modules:rule"))` to dependencies. - [ ] **Step 3: Verify gradle build configuration** Run: `./gradlew :modules:rule:compileScala :modules:core:compileScala` Expected: SUCCESS - [ ] **Step 4: Commit** ```bash git add modules/rule/build.gradle.kts git add modules/core/build.gradle.kts git commit -m "build: configure rule module and add dependency" ``` --- ### Task 5: Refactor GameEngine to use RuleSet directly **Files:** - Modify: `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala` Major refactoring: remove GameController calls, use RuleSet for all validation, replace GameHistory with GameContext. - [ ] **Step 1: Update GameEngine constructor and imports** Inject RuleSet, replace GameHistory with GameContext, update field names. - [ ] **Step 2: Replace processUserInput and handleParsedMove** Use `ruleSet.legalMoves()` for validation, apply moves with RuleSet checks. - [ ] **Step 3: Update undo/redo to use GameContext** Use MoveCommand with previousContext instead of previousBoard/previousHistory/previousTurn. - [ ] **Step 4: Update reset and load methods** Replace GameHistory references with GameContext. - [ ] **Step 5: Verify GameEngine compiles** Run: `./gradlew :modules:core:compileScala` Expected: SUCCESS - [ ] **Step 6: Commit** ```bash git add modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala git commit -m "refactor(core): update GameEngine to use RuleSet, remove GameController calls" ``` --- ### Task 6: Update observer events to use GameContext **Files:** - Modify: All GameEvent files in `modules/core/src/main/scala/de/nowchess/chess/observer/` Replace (board, history, turn) parameters with GameContext in all event types. - [ ] **Step 1: Update all GameEvent case classes** For each event: - Replace: `(board: Board, history: GameHistory, turn: Color)` - With: `(context: GameContext)` Affected events: - MoveExecutedEvent - CheckDetectedEvent - CheckmateEvent - StalemateEvent - MoveUndoneEvent - MoveRedoneEvent - FiftyMoveRuleAvailableEvent - BoardResetEvent - InvalidMoveEvent - DrawClaimedEvent - Others as needed - [ ] **Step 2: Verify compilation** Run: `./gradlew :modules:core:compileScala` Expected: SUCCESS - [ ] **Step 3: Commit** ```bash git add modules/core/src/main/scala/de/nowchess/chess/observer/ git commit -m "refactor(observer): update GameEvent types to use GameContext" ``` --- ### Task 7: Delete GameController and move logic files from core **Files:** - Delete: `modules/core/src/main/scala/de/nowchess/chess/controller/GameController.scala` - Delete: Logic files from `modules/core/src/main/scala/de/nowchess/chess/logic/` - [ ] **Step 1: Delete GameController** ```bash rm modules/core/src/main/scala/de/nowchess/chess/controller/GameController.scala ``` - [ ] **Step 2: Delete logic files (moved to rule module)** ```bash rm modules/core/src/main/scala/de/nowchess/chess/logic/MoveValidator.scala rm modules/core/src/main/scala/de/nowchess/chess/logic/GameRules.scala rm modules/core/src/main/scala/de/nowchess/chess/logic/CastlingRightsCalculator.scala rm modules/core/src/main/scala/de/nowchess/chess/logic/EnPassantCalculator.scala # Delete any other logic files that are now in StandardRules ``` - [ ] **Step 3: Commit deletion** ```bash git add -u modules/core/src/main/scala/de/nowchess/chess/controller/ git add -u modules/core/src/main/scala/de/nowchess/chess/logic/ git commit -m "refactor(core): remove GameController and moved logic files" ``` --- ### Task 8: Verify full build and green state **Files:** - None (validation only) - [ ] **Step 1: Clean and build all modules** Run: `./gradlew clean build` Expected: SUCCESS - [ ] **Step 2: Run core tests** Run: `./gradlew :modules:core:test` Expected: Tests may fail (expected; tests need refactoring per spec) - [ ] **Step 3: Run rule tests** Run: `./gradlew :modules:rule:test` Expected: No tests yet (we'll write FEN/PGN-based tests separately) - [ ] **Step 4: Commit successful build state** ```bash git commit --allow-empty -m "build: full build succeeds post-refactoring" ```