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

5.7 KiB
Raw Blame History

Findings

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

Summary

Initial analysis of the Scopone Scientifico Phaser 3 codebase. This document is populated by the Planner agent as research is performed.

Codebase Observations

  • Total source files: 9 TypeScript (6 in src/), 3 Java (Capacitor boilerplate)
  • Largest file: GameScene.ts (~1340 lines) — rendering, input, effects, audio, UI
  • Game logic is framework-independent: game/ modules have zero Phaser imports
  • No test framework: only tsc --noEmit for type-checking
  • No linter/formatter: code style enforced manually
  • AI plays all 3 non-human seats using the same heuristic
  • Procedural audio: all sound is Web Audio oscillators — no audio asset files

Potential Improvement Areas

  • AI cheats with perfect information: scoreDump() and opponentThreatScore() in ai.ts iterate opp.hand directly — bots can see all opponent cards. Must be replaced with imperfect-information card tracking.
  • No mastery/difficulty levels: All 3 AI seats use the same heuristic at the same strength.
  • No card tracking: No module tracks which cards have been played or remains in the deck.
  • No minimax: Pure heuristic scoring, no look-ahead or game tree search.
  • Allied bot is selfish: Compagno (player 2) plays identically to opponents — no cooperative strategy.

Research Performed

Web Research: Scopone Scientifico Rules (2026-03-31)

Sources: Wikipedia (Scopa article, Scopone section), Pagat.com (Scopone page by John McLeod)

Core Rules (Scopone Scientifico variant)

  • 4 players, 2 fixed teams of 2 (sit opposite): Team A = players 0+2, Team B = players 1+3
  • 40-card Napoletane deck: 4 suits (bastoni, coppe, denara, spade), values 110
  • All 40 cards dealt (10 each), no initial table cards — the "scientifico" variant
  • Play passes around the table (counter-clockwise in Italian tradition; this game uses 0→1→2→3)
  • Each turn: play one card face-up to the table

Capture Rules

  1. If the played card's value matches a table card, the table card must be captured (single card, not a sum)
  2. If multiple table cards match the played value, exactly one is captured (player chooses)
  3. If no direct match, the player may capture a subset of table cards summing to the played value
  4. If the played card matches both a single card and a sum, the single card must be captured (not the sum)
  5. There is no obligation to play a capturing card — a player may choose to play a non-capturing card instead. But if the played card CAN capture, it MUST capture.
  6. Scopa: capturing ALL remaining table cards awards +1 point (except on the very last card of the round)

Scoring (per round, 4 fixed points + scope)

Category Rule
Carte Team with majority of captured cards (20+ of 40). Tie = no point.
Denari Team with majority of coins/denara suit cards (6+ of 10). Tie = no point.
Settebello Team capturing the 7 of denara. Always awarded.
Primiera Team with highest prime value. Prime = best card per suit using special scale. Tie = no point. Must have all 4 suits.
Scope +1 per scopa achieved during play.

Primiera Values (confirmed matching codebase)

Card value Primiera value
7 21
6 18
1 (Ace) 16
5 15
4 14
3 13
2 12
8,9,10 10

A team missing an entire suit cannot win primiera (even 3×21=63 loses to 21+16+16+16=69 with all 4 suits).

Winning

  • First team to 11+ points at the end of a round wins
  • If both reach 11 in the same round, higher total wins; if tied, play continues

Strategy Notes (from Pagat.com)

  • 7 of coins (settebello) is the single most valuable card — contributes to all 4 fixed scoring categories
  • Avoid giving scope: leave table total ≥ 11 when possible
  • Anchor strategy: leave a card on table that your team controls (you hold duplicates of that value)
  • Whirlwind: consecutive scope — clearing the table forces opponent to play, partner captures, repeat
  • Sevens > sixes > aces in priority for primiera control
  • Paired/unpaired tracking: if all captures are single-card matches, the last card matches the last table card. Sum captures disrupt this pattern, important for end-game planning.

Codebase Capture Rule Validation

The findCaptures() in engine.ts correctly implements:

  • Direct match priority over sum captures ✓
  • Multiple direct matches: takes ALL matching cards (slight deviation — pagat.com says choose ONE, but Wikipedia says take all. The codebase takes all direct matches. This is the existing behavior and must not be altered per success criteria.)
  • Sum subsets via power set enumeration ✓
  • applyMove() auto-captures when possible ✓

Minimax Feasibility Analysis

  • 10 cards per player × 4 players = 40 total moves per round
  • Full game tree: ~10^12 nodes — infeasible for exhaustive search
  • Approach: Depth-limited alpha-beta with determinization for imperfect information
    • Sample N possible opponent hand assignments consistent with card tracking
    • Run minimax on each sample to limited depth (46 plies)
    • Average/vote across samples for best move
    • Alpha-beta pruning reduces effective branching factor significantly
    • Depth 4 (one full rotation) with ~5 moves per player = ~625 nodes per sample — very manageable
    • 1020 samples × 625 nodes = ~6,00012,500 evaluations — runs in <100ms on modern hardware