Files
scopone/docs/ARCHITECTURE.md
2026-04-09 23:00:59 +02:00

12 KiB

Architecture

Last Updated: 2026-04-09T20:59:51.000Z

Overview

Attribute Value
Primary language TypeScript
Secondary language Java
Source counts 15 TypeScript files under src/, 3 Java files under android/
Project type Phaser browser game packaged for Android with Capacitor
Framework Phaser 3.87.0
Tooling Vite 5, TypeScript 5.x, Capacitor 8.3, tsx 4.19
Runtime layout 1280 x 720, Phaser.Scale.FIT, centered in #game
Build command npm run build
Test command npx tsc --noEmit

The repository is a TypeScript-first implementation of Scopone Scientifico. Pure game rules, scoring, imperfect-information tracking, AI heuristics, worker transport, and benchmark code live under src/game/. Phaser scenes under src/scenes/ own rendering, input, animation, menu flow, status messaging, and procedural audio. The android/ tree is a Capacitor wrapper with a small custom MainActivity that forces immersive full-screen behavior.

Project Structure

scopone-phaser/
|- src/
|  |- main.ts
|  |- game/
|  |  |- types.ts
|  |  |- engine.ts
|  |  |- card-tracker.ts
|  |  |- preferences.ts
|  |  |- ai.ts
|  |  |- ai-worker-protocol.ts
|  |  |- ai-worker-client.ts
|  |  |- ai.worker.ts
|  |  |- ai-benchmark.ts
|  |  `- ai-benchmark-fixtures.ts
|  `- scenes/
|     |- BootScene.ts
|     |- MenuScene.ts
|     |- SettingsScene.ts
|     `- GameScene.ts
|- public/
|- android/
|  |- app/
|  `- variables.gradle
|- docs/
|- prompts/
|- package.json
|- tsconfig.json
|- vite.config.ts
`- capacitor.config.ts

Key Directories

Directory Purpose
src/game/ Rules engine, score calculation, imperfect-information tracking, audio preference persistence, AI heuristics, worker transport, and benchmark harnesses
src/scenes/ Phaser scene lifecycle, menus, settings UI, board rendering, interaction, HUD, audio, and FX
public/ Atlas metadata and static assets loaded by Phaser
android/ Capacitor Android project, Gradle configuration, generated wrapper assets, and the native activity
docs/ Architecture, code style, findings, and cache metadata
prompts/ JIRA workflow artifacts and iteration state

Design Patterns

No explicit GoF patterns were detected in the source or by semantic search.

Observed architectural patterns:

Pattern Where it appears
Scene-based flow BootScene -> MenuScene -> GameScene, with SettingsScene opened from the menu and returning to it
Functional core / imperative shell src/game/ avoids Phaser imports while src/scenes/ owns runtime side effects
Immutable state transitions applyMove() clones GameState before mutating round state
Worker offload with fallback AIWorkerClient uses ai.worker.ts when available and falls back to direct chooseMove() otherwise
Typed message protocol ai-worker-protocol.ts defines worker request, progress, result, and serialized error shapes
Persistence adapter preferences.ts normalizes and stores audio settings through a storage boundary instead of scene-local flags
Deterministic benchmark harness ai-benchmark.ts uses fixtures, seeded self-play, and simulated timing sources to evaluate AI quality

Key Components

src/main.ts

  • Creates the Phaser.Game instance.
  • Installs a one-shot fullscreen request on first user input when supported.
  • Registers BootScene, MenuScene, GameScene, and a local SettingsScene placeholder; MenuScene replaces that placeholder with the concrete src/scenes/SettingsScene.ts class before navigation.

src/game/types.ts

  • Defines the core game model: Card, Capture, Player, GameState, TeamScore, and ScoreBreakdown.
  • Models constrained domains with unions such as PlayerIndex, Difficulty, and DealerRelativeRole.
  • Stores PRIMIERA_VALUES for end-of-round scoring.

src/game/engine.ts

  • Builds and shuffles the 40-card deck.
  • Creates a round state for four players with dealer-relative opening order and stable player labels.
  • Implements Scopone capture rules where direct value matches take priority over subset-sum captures.
  • Applies moves immutably, awards scopa only before the final play, assigns leftover table cards to the last capturing team, and computes round and match scoring.

src/game/card-tracker.ts

  • Tracks cards visible through play and capture events without exposing hidden hands.
  • Reconstructs unseen cards from played + myHand + table.
  • Supplies suit counts, same-rank residue summaries, and hand-value probabilities used by the AI tiers.

src/game/preferences.ts

  • Defines the persisted audio preference model.
  • Normalizes possibly invalid storage payloads back to safe defaults.
  • Loads and saves preferences through localStorage when available, with browser-safe fallbacks.

src/game/ai.ts

  • Exposes chooseMove() as the async AI entry point.
  • Implements three difficulty tiers: beginner, advanced, and master.
  • Uses role-aware heuristics, tracker-based inference, tactical priority scoring, determinization sampling, and alpha-beta search.
  • Applies dynamic master search profiles based on total cards remaining, ranging from the base profile 4300 ms / 8 samples / depth 5 / batch 2 down to 3200 ms / 4 samples / exact endgame depth / batch 1 in the last four cards.

src/game/ai-worker-protocol.ts

  • Defines a single choose-move worker request type.
  • Defines typed progress, result, and serialized error responses.
  • Keeps worker communication schema isolated from UI code.

src/game/ai-worker-client.ts

  • Wraps worker lifecycle and pending-request tracking behind the same chooseMove() API that scenes consume.
  • Creates the worker as an ES module with new Worker(new URL('./ai.worker.ts', import.meta.url), { type: 'module' }).
  • Fails over pending requests to in-thread AI execution if worker creation, posting, or deserialization fails.

src/game/ai.worker.ts

  • Rehydrates CardTracker from a serialized snapshot.
  • Delegates move selection to chooseMove().
  • Posts progress, result, or serialized error messages back to the main thread.

src/game/ai-benchmark.ts and src/game/ai-benchmark-fixtures.ts

  • Define the AI quality harness invoked by npm run benchmark:ai-quality.
  • Combine fixed fixtures, critical-concept checks, seeded self-play, regression watchlists, and simulated timing sources.
  • Encode the current iteration 5 benchmark contract: 13 fixed fixtures, 6 critical concepts, and 48 self-play matches.

src/scenes/BootScene.ts

  • Loads the card atlas and card back.
  • Displays a simple loading bar.
  • Transitions into MenuScene after asset load.

src/scenes/MenuScene.ts

  • Renders the title, compact rules summary, difficulty selection, and audio-settings entry point.
  • Reads persisted audio preferences to describe current state before match start.
  • Ensures the concrete SettingsScene class is registered before opening it.

src/scenes/SettingsScene.ts

  • Provides a dedicated audio settings surface.
  • Toggles music and effects independently.
  • Saves each change immediately and returns control to the menu scene.

src/scenes/GameScene.ts

  • Owns match flow, dealing, selection, capture resolution, AI turn orchestration, score HUD, status UI, think bar, particles, and procedural audio.
  • Instantiates and disposes AIWorkerClient on scene lifecycle events.
  • Enforces a minimum AI think display time and timer-based move outcome status messages.
  • Reads normalized audio preferences from scene data or persisted storage.
  • Updates CardTracker after play and capture events so AI inference remains derived from visible information.

android/app/src/main/java/com/phaser/scopa/MainActivity.java

  • Extends BridgeActivity.
  • Applies immersive mode during onCreate() and whenever window focus returns.
  • Hides status and navigation bars with transient swipe behavior.

Dependencies

JavaScript production dependencies

Package Version Purpose
phaser ^3.87.0 Game engine runtime
@capacitor/core ^8.3.0 Capacitor runtime bridge
@capacitor/cli ^8.3.0 Capacitor project tooling
@capacitor/android ^8.3.0 Android platform integration

JavaScript development dependencies

Package Version Purpose
tsx ^4.19.2 TypeScript execution for benchmark and prompt-local tooling
typescript ^5.0.0 Static type checking and TS compilation step
vite ^5.0.0 Dev server and production bundler

Android / Gradle dependencies

Dependency Version Source Purpose
com.android.tools.build:gradle 8.13.0 android/build.gradle Android Gradle plugin
com.google.gms:google-services 4.4.4 android/build.gradle Optional Google services integration
androidx.appcompat:appcompat 1.7.1 android/variables.gradle Android app compatibility
androidx.coordinatorlayout:coordinatorlayout 1.3.0 android/variables.gradle Layout coordination helpers
androidx.core:core-splashscreen 1.2.0 android/variables.gradle Splash screen support
junit:junit 4.13.2 android/variables.gradle JVM Android tests
androidx.test.ext:junit 1.3.0 android/variables.gradle Instrumented test runner
androidx.test.espresso:espresso-core 3.7.0 android/variables.gradle Instrumented UI testing

Platform configuration

  • compileSdkVersion: 36
  • targetSdkVersion: 36
  • minSdkVersion: 24

Module Organization

main.ts
  -> BootScene
  -> MenuScene
       -> SettingsScene (opened on demand after dynamic registration)
       -> GameScene
            -> engine.ts
            -> types.ts
            -> preferences.ts
            -> card-tracker.ts
            -> ai-worker-client.ts
                 -> ai-worker-protocol.ts
                 -> ai.worker.ts
                      -> ai.ts
ai-benchmark.ts
  -> ai.ts
  -> ai-benchmark-fixtures.ts
  -> engine.ts
  -> card-tracker.ts

Application-level dependency direction is one-way:

  • src/game/ imports only from sibling game modules.
  • src/scenes/ imports from src/game/ and Phaser.
  • src/game/ never imports Phaser.

Data Flow

  1. main.ts creates the Phaser app, installs fullscreen-on-first-input, and registers the scene list.
  2. BootScene loads atlas assets and starts MenuScene.
  3. MenuScene reads persisted audio preferences, lets the player choose difficulty, and can open SettingsScene for audio toggles.
  4. SettingsScene writes audio preferences immediately and returns to MenuScene.
  5. GameScene.create() normalizes incoming scene data, creates a fresh CardTracker, constructs a new GameState, and starts the opening deal.
  6. Human turns use pointer-driven card selection and findCaptures() output to choose legal captures.
  7. AI turns call AIWorkerClient.chooseMove(state, playerIdx, difficulty, tracker, onProgress).
  8. AIWorkerClient posts a typed request to ai.worker.ts; if workers are unavailable, it reruns the same request in-thread.
  9. chooseMove() returns a heuristic move for lower tiers or performs batched master search while emitting AIDecisionProgress.
  10. GameScene updates the think bar from progress callbacks, enforces a minimum visible think time, executes the returned move, records tracker state, and advances turn order.
  11. When every hand is empty, engine.ts finalizes scoring and GameScene presents the round or match outcome.
  12. Separately, ai-benchmark.ts exercises the same game and AI modules through fixed fixtures, seeded self-play, and simulated timing to produce quality summaries.

Build System

Command Source Purpose
npm run dev package.json Starts the Vite dev server
npm run build user-provided build command Runs tsc && vite build and writes web output to dist/
npm run preview package.json Serves the built app with Vite preview
npm run benchmark:ai-quality package.json Runs the AI benchmark harness through tsx
npx tsc --noEmit user-provided test command Type-checks the TypeScript codebase without emitting files