Files
scopone/docs/ARCHITECTURE.md
2026-04-02 20:55:49 +02:00

9.3 KiB

Architecture

Last Updated: 2026-04-02T19:05:00.000Z

Overview

Attribute Value
Primary language TypeScript
Secondary language Java (Capacitor Android shell)
Project type Browser card game with Android packaging
Framework Phaser 3.87.0
Tooling Vite 5, TypeScript 5.x, Capacitor 8.3
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 Scopone Scientifico implementation. The web client contains the real game logic and presentation. The Android tree is a Capacitor wrapper with a custom immersive MainActivity.

Project Structure

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

Key Directories

Directory Purpose
src/game/ Framework-independent rules, scoring, imperfect-information tracking, and AI search
src/scenes/ Phaser scene lifecycle, UI, animation, effects, and round orchestration
public/ Static web assets consumed by Phaser loaders
android/ Capacitor Android project, Gradle config, and immersive activity wrapper
docs/ Architecture, code style, findings, and cache metadata
prompts/ JIRA pipeline artifacts and iteration state

Design Patterns

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

Observed architectural patterns in the current codebase:

Pattern Where it appears
Scene-based flow BootScene -> MenuScene -> GameScene via Phaser scene registration
Functional core / imperative shell src/game/ stays free of Phaser imports, while src/scenes/ owns rendering and input
Clone-before-mutate state transitions applyMove() clones GameState before applying move effects
Worker offload with fallback AIWorkerClient runs heavy AI inside ai.worker.ts and falls back to in-thread chooseMove() if worker startup or messaging fails
Determinization search Master AI samples hidden hands before alpha-beta evaluation
Message-based progress reporting Worker and main thread exchange typed request/result/progress messages through ai-worker-protocol.ts

Key Components

src/main.ts

  • Bootstraps Phaser.Game.
  • Registers BootScene, MenuScene, and GameScene.
  • Installs a one-shot fullscreen request handler on first user interaction.

src/game/types.ts

  • Defines the game model: Card, Capture, Player, GameState, TeamScore, ScoreBreakdown.
  • Encodes difficulty tiers as 'beginner' | 'advanced' | 'master'.
  • Stores the PRIMIERA_VALUES lookup table.

src/game/engine.ts (371 lines)

  • Builds and shuffles the 40-card deck.
  • Creates the initial round state for four players.
  • Implements capture selection rules: single direct matches take priority; subset sums are considered only when no direct match exists.
  • Applies moves immutably, detects scopas, assigns leftover table cards, and computes round scores.

src/game/card-tracker.ts (89 lines)

  • Tracks seen cards across a round without exposing hidden hands directly.
  • Computes unseen cards from played + myHand + table.
  • Supplies probability helpers used by the AI for value-based inference.

src/game/ai.ts

  • Exposes chooseMove() as an async entry point.
  • Implements three difficulty levels:
    • beginner: noisy heuristic play.
    • advanced: stronger heuristics with race awareness, partner setup, and card-tracker inference.
    • master: determinization plus alpha-beta search with dynamic time budgets, batching, and progress callbacks.
  • Uses yieldToBrowser() between master-search batches so Phaser can repaint the think bar.

src/game/ai-worker-protocol.ts

  • Defines the typed message contract between the main thread and the worker.
  • Serializes requests around GameState, Difficulty, PlayerIndex, tracker snapshots, progress, results, and worker-safe errors.

src/game/ai-worker-client.ts

  • Wraps the worker lifecycle behind the same chooseMove() API the scene needs.
  • Creates module workers with new Worker(new URL('./ai.worker.ts', import.meta.url), { type: 'module' }).
  • Streams progress callbacks back into GameScene and degrades to direct chooseMove() execution when workers are unavailable.

src/game/ai.worker.ts

  • Rehydrates CardTracker from a snapshot, delegates move selection to chooseMove(), and posts progress/result/error messages back to the scene thread.
  • Keeps the expensive master search off the main rendering thread when worker support is available.

src/scenes/BootScene.ts

  • Loads the card atlas and card-back texture.
  • Shows a simple progress bar and transitions into the menu.

src/scenes/MenuScene.ts

  • Renders the title screen and rules summary.
  • Lets the player choose beginner, advanced, or master difficulty.
  • Starts GameScene with the selected difficulty in scene data.

src/scenes/GameScene.ts

  • Owns the match loop, HUD, think bar, card interaction, animation, FX, audio, and round transitions.
  • Uses CardTracker to record played and captured cards after each move.
  • Instantiates AIWorkerClient, bridges async AI progress into a visible top-of-screen think bar, and disposes worker resources on scene shutdown.
  • Handles end-of-round overlays and full-match restart flow.

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

  • Extends BridgeActivity.
  • Forces immersive mode by hiding status and navigation bars whenever the window gains focus.

Dependencies

JavaScript production dependencies

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

JavaScript development dependencies

Package Version Purpose
typescript ^5.0.0 Type-checking and TS compilation for builds
vite ^5.0.0 Dev server and bundling

Android / Gradle dependencies

Dependency Source Purpose
com.android.tools.build:gradle:8.13.0 android/build.gradle Android build plugin
com.google.gms:google-services:4.4.4 android/build.gradle Optional Google services integration
androidx.appcompat:appcompat android/app/build.gradle Android UI compatibility
androidx.coordinatorlayout:coordinatorlayout android/app/build.gradle Android layout support
androidx.core:core-splashscreen android/app/build.gradle Splash screen support
junit:junit android/app/build.gradle JVM-side Android tests
androidx.test.ext:junit android/app/build.gradle Instrumented Android testing
androidx.test.espresso:espresso-core android/app/build.gradle Android UI testing

Module Organization

main.ts
    -> BootScene
    -> MenuScene
    -> GameScene
             -> engine.ts
             -> ai.ts
           -> ai-worker-client.ts
               -> ai-worker-protocol.ts
               -> ai.worker.ts
                   -> ai.ts
             -> card-tracker.ts
             -> types.ts

Dependencies are one-directional at the application level:

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

Data Flow

  1. main.ts creates the Phaser app and registers all scenes.
  2. BootScene loads assets, then starts MenuScene.
  3. MenuScene passes the chosen difficulty into GameScene.
  4. GameScene.create() initializes a new CardTracker, creates the initial GameState, and animates the opening deal.
  5. On each turn:
    • Human turns use click-driven selection and capture highlighting.
    • AI turns call AIWorkerClient.chooseMove(state, playerIdx, difficulty, tracker, onProgress).
  6. AIWorkerClient posts a typed request into ai.worker.ts; if worker setup fails, it falls back to in-thread chooseMove().
  7. chooseMove() either returns immediately for heuristic tiers or performs batched master search while reporting AIDecisionProgress.
  8. Worker progress messages drive GameScene.updateThinkBar() until a result is posted back.
  9. GameScene.executeMove() applies the move, updates the tracker, animates the result, refreshes the HUD, and advances the round.
  10. When all hands are empty, engine.ts finalizes scoring and GameScene displays the round summary or final match screen.

Build System

Command Source Purpose
npm run dev package.json Starts the Vite development server on port 3000 and opens the browser
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 locally via Vite preview
npx tsc --noEmit user-provided test command Type-checks the TypeScript codebase without emitting files