Architecture
Last Updated: 2026-04-02T18:12:18.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
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 |
| Determinization search |
Master AI samples hidden hands before alpha-beta evaluation |
| Callback-driven progress reporting |
chooseMove() reports AIDecisionProgress to GameScene for the think bar |
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 (1210 lines)
- 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/scenes/BootScene.ts (47 lines)
- Loads the card atlas and card-back texture.
- Shows a simple progress bar and transitions into the menu.
- 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 (1446 lines)
- 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.
- Bridges async AI progress into a visible top-of-screen think bar.
- 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
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
main.ts creates the Phaser app and registers all scenes.
BootScene loads assets, then starts MenuScene.
MenuScene passes the chosen difficulty into GameScene.
GameScene.create() initializes a new CardTracker, creates the initial GameState, and animates the opening deal.
- On each turn:
- Human turns use click-driven selection and capture highlighting.
- AI turns call
chooseMove(state, playerIdx, difficulty, tracker, onProgress).
chooseMove() either returns immediately for heuristic tiers or performs batched master search while reporting AIDecisionProgress.
GameScene.executeMove() applies the move, updates the tracker, animates the result, refreshes the HUD, and advances the round.
- 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 |