Architecture
Last Updated: 2026-04-08T19:48:08.000Z
Overview
| Attribute |
Value |
| Primary language |
TypeScript |
| Secondary language |
Java |
| Project type |
Phaser browser game packaged for Android with Capacitor |
| 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 implementation of Scopone Scientifico. Gameplay rules, scoring, inference, and AI live in framework-independent modules under src/game/. Phaser scenes under src/scenes/ own rendering, input, UI, animation, and scene transitions. The android/ tree is a Capacitor wrapper with a small custom MainActivity for immersive full-screen behavior.
Project Structure
Key Directories
| Directory |
Purpose |
src/game/ |
Rules engine, score calculation, imperfect-information tracking, AI heuristics, and master search |
src/scenes/ |
Phaser scene lifecycle, menus, board rendering, interaction, HUD, audio, and FX |
public/ |
Atlas metadata and other 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 via Phaser scene registration |
| 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 error shapes |
| Imperfect-information search |
CardTracker plus determinization sampling support the master AI tier |
Key Components
src/main.ts
- Creates the
Phaser.Game instance.
- Registers
BootScene, MenuScene, and GameScene.
- Installs a one-shot fullscreen request on first user input when supported.
src/game/types.ts
- Defines the core game model:
Card, Capture, Player, GameState, TeamScore, and ScoreBreakdown.
- Models constrained domains with unions such as
PlayerIndex and Difficulty.
- 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.
- Implements capture rules where direct value matches take priority over subset-sum captures.
- Applies moves immutably, awards scope, assigns leftover table cards, 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 value and suit residue helpers used by AI inference and probability estimates.
src/game/ai.ts
- Exposes
chooseMove() as the async AI entry point.
- Implements three difficulty tiers:
beginner, advanced, and master.
- Uses table-driven search profiles, role-aware heuristics, tracker-based inference, and determinization plus alpha-beta search.
- Configures the current master profile with a 4600 ms budget, 10 samples, depth 6, and batch size 2.
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, messaging, 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/scenes/BootScene.ts
- Loads the card atlas and card back.
- Displays a simple loading bar.
- Transitions into
MenuScene after asset load.
- Renders the title, rules summary, and difficulty selection.
- Starts
GameScene with the chosen difficulty.
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.
- 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 |
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
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
main.ts creates the Phaser app and registers all scenes.
BootScene loads textures and starts MenuScene.
MenuScene passes the chosen difficulty to GameScene.
GameScene.create() creates a fresh CardTracker, constructs a new GameState, and starts the opening deal.
- Human turns use pointer-driven card selection and
findCaptures() output to choose legal captures.
- AI turns call
AIWorkerClient.chooseMove(state, playerIdx, difficulty, tracker, onProgress).
AIWorkerClient posts a typed request to ai.worker.ts; if workers are unavailable, it reruns the same request in-thread.
chooseMove() returns a heuristic move for lower tiers or performs batched master search while emitting AIDecisionProgress.
GameScene updates the think bar from progress callbacks, executes the returned move, records tracker state, and advances turn order.
- When every hand is empty,
engine.ts finalizes scoring and GameScene presents the round or match outcome.
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 |
npx tsc --noEmit |
user-provided test command |
Type-checks the TypeScript codebase without emitting files |