refactor(game): land decomp phases 2-9 on development (recovery for #132/#134/#96)#145
Merged
Merged
Conversation
Move undoLastInActivePlay and canUndoActivePlay into src/Game/undo.ts as free functions. Now that types promotes undo.frames to a typed BackgammonGameMoving[] on BasePlay, the functions read game.activePlay.undo.frames directly with no 'as any' casts (removes 5 casts from the undo slice of #96). The public Game.* statics remain as thin delegating wrappers; canUndoActivePlay (deferred from guards) lands here with its concern. Depends on backgammon-types refactor/undo-frames-typed. Characterization tests (10) added in the prior commit stay green. No behavior change. Core: 458 passed / 12 skipped, tsc -b clean. Refs #132, #96
Move the cube/doubling and resign logic into src/Game/cube.ts as free functions: canOfferDouble, canAcceptDouble, acceptDouble, canRefuseDouble, refuseDouble, resign, double. The public Game.* statics remain thin delegating wrappers so external callers (ai/api/client) are unchanged. Internal cross-calls go module-local (Rule A): acceptDouble->canAcceptDouble, canRefuseDouble->canAcceptDouble, refuseDouble->canRefuseDouble, double->canOfferDouble. The cube-slice predicates (canOfferDouble/canAcceptDouble/canRefuseDouble) land here with their concern, as deferred from the guards phase. Faithful move: pre-existing casts preserved verbatim (cube-slice #96 cast removal is out of scope for this phase). Characterization tests (10) added in the prior commit stay green. No behavior change. Core: 468 passed / 12 skipped, tsc -b clean. Refs #132, #134
#132) Move the standalone lifecycle transitions rollForStart and restoreState into src/Game/lifecycle.ts as free functions. Public Game.* statics remain thin delegating wrappers (external ai/api/client callers unchanged). rollForStart self-recurses on a tie via the module-local function (Rule A). Removed two now-dead imports from index.ts (RESTORABLE_GAME_STATE_KINDS, BackgammonPlayerRollingForStart). Game.initialize and Game.createNewGame stay on the class: they drive the new Game() constructor path (PositionReconstructor, gnuPositionId getter) and the overloaded initialize signature. Faithful move; pre-existing casts preserved. Characterization tests for restoreState (7) added in the prior commit stay green. No behavior change. Core: 475 passed / 12 skipped, tsc -b clean. Refs #132, #134
Move the turn-progression logic into src/Game/turnFlow.ts as free functions: roll, switchDice, move, moveAndFinalize, toMoved, executeAndRecalculate, checkAndCompleteTurn, confirmTurn, startMove, and getPlayersForColor (used by roll). The public Game.* statics now delegate via 'public static X = X' to the imported free functions, so external callers (ai/api/client) and their type signatures are unchanged. Internal cross-calls go module-local (Rule A): roll->getPlayersForColor, moveAndFinalize->move/checkAndCompleteTurn, executeAndRecalculate->move/checkAndCompleteTurn/confirmTurn, checkAndCompleteTurn->toMoved. The player accessors activePlayer/inactivePlayer and the robot helpers (handleRobotMovedState, executeRobotTurn) stay on the class; handleRobotMovedState still reaches confirmTurn through the facade. Byte-exact relocation (mechanical extraction script) preserves behavior; the test suite is identical before and after. Removed now-dead value imports (Dice, Play, MoveExecutionOptions) from index.ts. index.ts drops ~1500 lines (~2050 -> 586). Note: switchDice coverage (weak, ~#134) is left as a follow-up coverage pass; this phase is a behavior-preserving move. No behavior change. Core: 475 passed / 12 skipped, tsc -b clean. Refs #132
Move handleRobotMovedState and confirmTurnWithRobotAutomation into src/Game/robot.ts as free functions. Both call confirmTurn imported from turnFlow (Rule A: no facade round-trip). Public Game.* statics delegate via 'public static X = X'. executeRobotTurn stays as its existing re-export from ./executeRobotTurn. Removed now-dead imports from index.ts (debug, BackgammonGameMoved). Faithful move; behavior unchanged. Core: 475 passed / 12 skipped, tsc -b clean. Refs #132
…132) After the module extractions, index.ts is a thin facade over shared/guards/undo/cube/lifecycle/turnFlow/robot. Remove type imports left dead by the moves: BackgammonCubeValue, BackgammonDieValue, BackgammonMoveSkeleton, BackgammonPlayerDoubled, BackgammonPlayerWinner, BackgammonRoll, BackgammonPlayersMovingTuple, BackgammonPlayersRollingTuple. Downstream guard: rebuilt core + typechecked consumers against local core -- ai (tsc -b) clean, api (tsc --noEmit) clean; client does not consume core at the type level. Game.* public API + signatures preserved throughout the epic. No behavior change. Core: 475 passed / 12 skipped, tsc -b clean. index.ts: 2491 -> ~530 lines across the epic. Refs #132, #133
Add targeted tests for the decomposed modules and a COVERAGE=1-gated threshold scoped to src/Game/: - guards.ts -> 100% (canRoll/canRollForStart/canPlayerRoll/canGetPossibleMoves) - robot.ts -> 88% (handleRobotMovedState, confirmTurnWithRobotAutomation) - game-accessors: activePlayer/inactivePlayer/getPlayersForColor/findChecker + createNewGame rules merge - turnFlow: roll (rolled-for-start/rolling/doubled), switchDice, executeAndRecalculate, checkAndCompleteTurn, toMoved, moveAndFinalize - cube: acceptDouble-at-64 (maxxed) and refuseDouble non-first-double Game/ aggregate coverage: ~59% -> 80.5% stmts, 71.8% branch, 89% funcs, 82.5% lines. Gate uses a directory-path key (aggregate, not per-file) at 75/70/75/75; branch floor ratchets toward 75 as turnFlow no-move/blocked paths and the gnuPositionId getter gain coverage. Enforced only under COVERAGE=1, so normal jest runs are unaffected. Core: 518 passed / 12 skipped; COVERAGE=1 exit 0; normal exit 0. Refs #134
… (Phase 9 of #96) cube.ts: drop all 5 'as any' casts. - offeredThisTurnBy is on BaseCube, so the cast was unnecessary. - rollForStartValue is on BasePlayerProps (optional); the two player-copy casts become documented non-null assertions (rolling/inactive player types require the field). - the acceptDouble return 'as any' narrows to a commented 'as unknown as BackgammonGameRolling' (spread yields a generic players array TS can't narrow to the tuple). turnFlow.ts: remove the undo-snapshot casts in move() and executeAndRecalculate() -- game.activePlay and undo.frames are typed now (since the Phase 2 types fix), so 'const ap: any = (game as any)...' plus the JSON fallback 'as any' are gone. index.ts: comment the gnuPositionId getter's 'this as any' (Game class vs BackgammonGame union). Deferred: turnFlow still has ~30 '(move as any).x =' casts in the sanitization/recalculation loops. Removing those requires rebuilding move objects immutably instead of mutating in place -- a behavior-sensitive change left for a dedicated pass now that turnFlow has ~76% coverage. No behavior change. Core: 518 passed / 12 skipped, tsc -b clean. Refs #96
test(game): coverage gate for decomposed Game modules (Phase 8 of #134)
refactor(game): remove as-any casts in cube + snapshot paths (Phase 9 of #96)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What happened
The Game/index.ts decomposition (#132) landed as a stacked PR chain #135-#144. All ten show MERGED, but only #135 (phase 0) and #136 (phase 1) had base =
development. PRs #137-#144 had base = the branch below them in the stack, so merging them only advanced the intermediate feature branches. Phases 2-9 never reacheddevelopment.State before this PR on
development:Game/index.tsstill 2448 lines; onlyshared.ts+guards.tsextracted.What this PR does
Lands the remaining commits (phases 2-9) onto
development. This is not new work; it is the already-reviewed content of the merged-but-stranded PRs #137-#144. This branch's tree is identical to the intended final decomposition tip.Net effect:
Game/index.ts: 2448 -> 533 linescube.ts,lifecycle.ts,robot.ts,turnFlow.ts,undo.tsas anycasts in Game/index.ts with proper type definitions #96 partial)Recovers stranded PRs
#137 phase 2 undo, #138 phase 3 cube, #139 phase 4 lifecycle, #140 phase 5 turnFlow, #141 phase 6 robot, #142 phase 7 facade, #143 phase 8 coverage, #144 phase 9 casts.
Merge instructions
Use a merge commit (do NOT squash) to preserve the individual phase commits.