Files
scopone/docs/ARCHITECTURE.md
Giancarmine Salucci 3d1f3e5eb4 chore: initial commit
2026-03-31 18:38:34 +02:00

7.5 KiB
Raw Blame History

Architecture

Last Updated: 2026-03-31T00:00:00.000Z

Overview

Attribute Value
Language TypeScript (ES2020 target, strict mode)
Type 2D card game — Scopone Scientifico
Framework Phaser 3.87+ (scene-based game engine)
Bundler Vite 5
Native Capacitor 8.3 (Android)
Resolution 1280 × 720, FIT scaling with auto-center

Project Structure

scopone-phaser/
├── src/
│   ├── main.ts               # Phaser.Game bootstrap + config
│   ├── game/
│   │   ├── types.ts           # Card, Suit, Player, GameState, TeamScore, ScoreBreakdown, PRIMIERA_VALUES
│   │   ├── engine.ts          # Deck build, shuffle, capture logic, applyMove, scoring, primiera
│   │   └── ai.ts              # Heuristic AI: chooseMove, scoreCapture, scoreDump
│   └── scenes/
│       ├── BootScene.ts       # Asset loading (atlas, card back image)
│       ├── MenuScene.ts       # Start menu with rules summary
│       └── GameScene.ts       # Main game: rendering, turn management, effects, audio
├── index.html                 # Entry point, Italian locale, green felt background
├── public/                    # Static assets (atlas.json, atlas.png, retro.png)
├── android/                   # Capacitor Android native shell
├── package.json
├── tsconfig.json
├── vite.config.ts
└── capacitor.config.ts

Key Directories

Directory Purpose
src/game/ Domain logic — types, game engine, AI (no Phaser dependency)
src/scenes/ Phaser scenes — rendering, input, effects, audio
public/ Static assets served by Vite (card atlas, card back)
android/ Capacitor-generated Android project (Gradle, Java)
prompts/ JIRA agent pipeline artifacts

Design Patterns

Pattern Where
Scene lifecycle BootScene → MenuScene → GameScene (Phaser scene graph)
Immutable state updates applyMove() deep-clones GameState before mutation
Heuristic scoring AI evaluates all legal moves with weighted feature scores
Separation of concerns game/ has no Phaser imports; scenes/ bridges game ↔ rendering
Procedural audio Web Audio API oscillators + delay reverb — no audio files

No explicit GoF patterns (singleton, factory, observer, DI) detected.

Key Components

types.ts — Domain Types

  • Card { suit, value, id } — 40-card Napoletane deck (suits: bastoni, coppe, denara, spade; values 1-10)
  • GameState — full round state: 4 players, table, current player, team scores, round tracking
  • TeamScore — per-team stats: cards, scope, denari, settebello, primiera, round/total points
  • ScoreBreakdown — which team won each scoring category
  • PRIMIERA_VALUES — lookup table for primiera card values

engine.ts — Game Logic

  • buildDeck() / shuffle() — Fisher-Yates 40-card deck
  • createInitialState() — deals 10 cards per player, empty table (Scopone Scientifico rules)
  • findCaptures(played, table) — direct value match (mandatory) or subset-sum combinations
  • applyMove(state, player, card, captureChoice) — immutable state transition, scopa detection, end-of-round scoring
  • calculateScores() / scoreRound() — carte, denari, settebello, primiera, scope points
  • calcPrimiera(pile) — best card per suit using PRIMIERA_VALUES
  • teamOf(playerIdx) — team assignment: 0+2 = Team A, 1+3 = Team B

ai.ts — Heuristic AI

  • chooseMove(state, playerIdx) — evaluates all legal moves (captures + dumps)
  • scoreCapture() — weighted: scopa (+500), settebello (+300), denari (+50 each), card count, primiera value, opponent threat
  • scoreDump() — avoids giving opponents scopa (-400), prefers low-value non-denari, penalises dumping 7s and aces

GameScene.ts — Main Scene (~1340 lines)

  • Four-player layout: South (human), West/East (AI, rotated ±90°), North (AI partner)
  • Deal animation with staggered tweens
  • Card selection with postFX glow pulse
  • Capture highlighting with multiple-choice UI
  • Particle effects: capture burst, scopa explosion, settebello flash, denari shimmer, primiera glow, card trails, victory confetti
  • Camera shake + flash on scopa and settebello
  • Live score bar with animated counter updates
  • Think bar progress indicator during AI turns
  • Procedural background music (oscillator drone + triangle melody + chord stabs)
  • Round-end summary panel and game-over screen

Dependencies

Production

Package Version Purpose
phaser ^3.87.0 2D game engine
@capacitor/core ^8.3.0 Capacitor runtime
@capacitor/cli ^8.3.0 Capacitor CLI
@capacitor/android ^8.3.0 Android platform plugin

Development

Package Version Purpose
typescript ^5.0.0 TypeScript compiler
vite ^5.0.0 Dev server and bundler

Module Organisation

main.ts ──→ BootScene ──→ MenuScene ──→ GameScene
                                           │
                                           ├── game/engine (createInitialState, applyMove, findCaptures, ...)
                                           ├── game/ai     (chooseMove)
                                           └── game/types  (Card, GameState, ...)

game/ modules are pure logic with no framework coupling. scenes/ imports from game/ but never vice versa.

Data Flow

  1. createInitialState() builds shuffled deck, deals 10 cards each, empty table
  2. GameScene.nextTurn() detects current player: human → enable input; AI → delay + chooseMove()
  3. applyMove() returns new GameState + capture result + scopa flag
  4. GameScene.executeMove() animates: card flight → capture burst → pile collection
  5. updateScoreBar() reflects live team stats with animated counter tweens
  6. When all hands empty → calculateScores() → round-end overlay
  7. First team to 11 points → game-over screen → optional restart

Build System

Command Action
npm run dev vite — dev server on port 3000
npm run build tsc && vite build — compile + bundle to dist/
npm run preview vite preview — preview production build
tsc --noEmit Type-check only (no test framework)