10 KiB
10 KiB
Findings
Last Updated: 2026-04-09T20:59:51.000Z
Summary
Initializer refresh for SCOPONE-0010. The cache was invalid because docs/FINDINGS.md no longer matched its recorded hash, and the architecture document no longer matched the live source layout after settings, preferences, and benchmark changes. The observations below reflect the current repository state.
Codebase Observations
- Primary gameplay code currently lives in 15 TypeScript source files under
src/; the Android wrapper adds 3 Java files. - The project is structurally split between framework-free gameplay modules in
src/game/and Phaser scene code insrc/scenes/. src/scenes/GameScene.tsandsrc/game/ai.tsremain the two largest concentrations of application logic.- A dedicated audio preference path now exists:
src/game/preferences.ts,src/scenes/MenuScene.ts, andsrc/scenes/SettingsScene.ts. main.tsstill contains a localSettingsSceneplaceholder class, whileMenuScene.ensureSettingsSceneAvailable()swaps in the concrete imported scene before navigation.- The AI transport layer is a stable three-file path:
ai-worker-protocol.ts,ai-worker-client.ts, andai.worker.ts. - The AI exposes three difficulty levels:
beginner,advanced, andmaster. advancedandmasterboth useCardTrackerto reason about unseen cards without directly reading hidden hands.- The base
mastersearch profile is4300 ms / 8 samples / depth 5 / batch 2, with tighter endgame branches down to3200 ms / 4 samples / exact remaining depth / batch 1when 4 cards remain. GameSceneconsumes AI progress callbacks to update an on-screen think bar while a worker request is running.GameScenenow enforcesAI_MIN_THINK_MS = 1000andMOVE_OUTCOME_STATUS_MS = 2000through timer-backed scene logic.AIWorkerClientfails over pending work to in-threadchooseMove()if worker creation, posting, or deserialization fails.- The AI benchmark harness is now in source under
src/game/ai-benchmark.tsandsrc/game/ai-benchmark-fixtures.ts, andpackage.jsonexposes it asnpm run benchmark:ai-quality. - The current benchmark contract is iteration 5: 13 fixed fixtures, 6 critical concepts, and 48 self-play matches.
- The Android wrapper targets SDK 36 with
minSdkVersion24 and applies immersive mode from the native activity. - Audio remains procedural via Web Audio; there is still no dedicated audio asset pipeline in the source tree.
- No ESLint or Prettier configuration is present.
- The only repository-wide verification command supplied is
npx tsc --noEmit.
Potential Improvement Areas
GameScene.tsstill centralizes layout, turn flow, HUD updates, effects, audio, status messaging, and AI orchestration in one scene class.ai.tsstill combines heuristic tiers, inference helpers, determinization, move ordering, and alpha-beta evaluation in one module.- The current settings flow works, but the dual registration pattern for
SettingsSceneinmain.tsplus dynamic replacement inMenuSceneis fragile and worth simplifying later. - Worker transport is isolated cleanly, but progress rendering and fallback behavior remain coupled to scene-level UI concerns.
- A 3.2 to 4.35 second master search window may still be noticeable on slower mobile devices even with yielding and the minimum-think pacing already in place.
- There is no dedicated automated rules test suite beyond type-checking and the AI benchmark harness.
- Formatting and style are enforced socially rather than by automated linting or formatting tools.
Current Rule / Implementation Notes
Capture behavior in engine.ts
- Direct-match capture has priority over subset-sum capture.
- When multiple direct matches exist,
findCaptures()returns one single-card option per matching card. - Subset-sum captures are considered only when no direct match exists.
applyMove()defaults to the first legal capture if no explicit capture choice is supplied.- Scopa is awarded only when a capture clears the table before the final play of the round.
AI implementation snapshot
beginneruses a simpler heuristic with noise to remain beatable.advancedadds race awareness, anti-scopa logic, partner setup, denari pressure, and tracker-based probability estimates.masterorders legal moves with a quick evaluator, samples hidden hands, and scores them with alpha-beta search under a dynamic deadline.- Progress is reported through
AIDecisionProgressso the scene can keep the think bar responsive. CardTrackernow exposes same-rank residue summaries throughgetValueRankResidue()andgetValueRankResidueSummary(), and those semantics are the live inference surface for unseen-value reasoning.
Worker execution snapshot
GameScenecreatesAIWorkerClientduringcreate()and disposes it on bothshutdownanddestroy.AIWorkerClientserializesCardTrackerstate throughtoSnapshot()instead of attempting to transfer the class instance.ai.worker.tsrebuilds tracker state withCardTracker.fromSnapshot()before callingchooseMove().- Progress, result, and serialized error payloads all travel through
ai-worker-protocol.ts. - If worker execution becomes unavailable, pending requests are rerun with the in-thread AI path rather than being dropped.
Scene / UI implementation snapshot
BootSceneloads atlas assets and presents a simple loading bar.MenuScenenow exposes both difficulty selection and a dedicated entry point intoSettingsScene.SettingsScenepersists music and effects toggles immediately throughsaveAudioPreferences().GameScenereads normalized audio preferences from scene data or persisted storage before match start.GameScenetracks played and captured cards inCardTrackeras the round evolves.- The scene owns score HUD rendering, player labels, status text, think-bar rendering, procedural audio, and particle effects.
- Round-end and match-end flows remain managed inside the scene instead of separate overlay components.
Benchmark snapshot
ai-benchmark.tsnow uses a simulated timing source for fixture and self-play evaluation instead of depending only on wall-clock timing.- The benchmark summary records per-seed aggregates, dual-loss seeds, and a regression watchlist intersection.
- The harness remains source-local under
src/, so it is covered by the defaultnpx tsc --noEmitinclude set.
Research Performed
Web Research: Scopone Scientifico Rules (2026-03-31)
Sources: Wikipedia (Scopa article, Scopone section), Pagat.com (Scopone page by John McLeod)
Core Rules (Scopone Scientifico variant)
- 4 players, 2 fixed teams of 2 (sit opposite): Team A = players 0+2, Team B = players 1+3.
- 40-card Napoletane deck: 4 suits (
bastoni,coppe,denara,spade), values 1-10. - All 40 cards are dealt at the start of the round; the table begins empty.
- Turns advance in the implementation as
0 -> 1 -> 2 -> 3.
Capture Rules
- If the played card matches one or more table cards by value, a direct match must be taken.
- If multiple direct matches exist, one matching table card is chosen.
- If no direct match exists, a subset of table cards may be captured when their values sum to the played value.
- A direct match has priority over any possible sum capture.
- Scopa awards a point only when the table is cleared before the final play of the round.
Scoring
| Category | Rule |
|---|---|
| Carte | Majority of captured cards |
| Denari | Majority of denara suit cards |
| Settebello | Team that captures the 7 of denara |
| Primiera | Highest best-of-each-suit prime value |
| Scope | One point per scopa |
Primiera values used in code
| Card value | Primiera value |
|---|---|
| 7 | 21 |
| 6 | 18 |
| 1 | 16 |
| 5 | 15 |
| 4 | 14 |
| 3 | 13 |
| 2 | 12 |
| 8, 9, 10 | 10 |
SCOPONE-0008: AI progress rendering notes (2026-04-02)
- The current implementation does not use Phaser
TimerEventprogress helpers. - Instead,
chooseMove()emits its own normalized progress payload throughAIDecisionProgress. GameScene.updateThinkBar()renders remaining time from that callback.- The yielding behavior in the master search path is necessary so the browser can repaint while search batches continue.
SCOPONE-0009: Phaser scene lifecycle notes (2026-04-08)
- Source: Context7
/websites/phaser_io_api-documentation, queryPhaser 3.87 Scene lifecycle create restart shutdown destroy event listeners scene restart preserving external state. - Phaser dispatches
shutdownwhen a scene stops being active but may be re-used later; resource cleanup that should also cover final teardown can additionally listen todestroy. - The current
GameScenepattern of registering one-shot shutdown and destroy handlers is aligned with Phaser guidance for worker disposal and UI cleanup. - Dealer rotation and next-round state changes can stay inside the existing in-scene orchestration without requiring a different Phaser lifecycle primitive.
SCOPONE-0010: UI, settings, and benchmark refresh notes (2026-04-09)
src/game/preferences.tsis now the authoritative audio preference seam. It normalizes stored values and shields scenes from malformed storage state.src/scenes/MenuScene.tsnow reads persisted audio preferences and exposes a dedicated settings entry point instead of keeping audio options implicit.src/scenes/SettingsScene.tsexists as a real scene and persists music and effects toggles independently throughsaveAudioPreferences().src/scenes/GameScene.tsalready contains the previously planned pacing and status work:AI_MIN_THINK_MS = 1000,MOVE_OUTCOME_STATUS_MS = 2000, timer-backedsetStatus(...), andhandleSceneShutdown()timer cleanup are all present in source and should be treated as current behavior, not future work.src/game/ai-benchmark.tsnow enforces an iteration 5 contract with simulated timing, cross-seed aggregation, dual-loss reporting, and a regression watchlist intersection. Older findings that described iteration 4 targets or wall-clock-only timing are stale.main.tsstill registers a localSettingsScenestub whileMenuScenedynamically installs the concrete scene implementation before use. This works today but is an architectural wrinkle worth remembering in later planning.