5.7 KiB
5.7 KiB
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 --noEmitfor 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()andopponentThreatScore()inai.tsiterateopp.handdirectly — 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 1–10
- 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
- If the played card's value matches a table card, the table card must be captured (single card, not a sum)
- If multiple table cards match the played value, exactly one is captured (player chooses)
- If no direct match, the player may capture a subset of table cards summing to the played value
- If the played card matches both a single card and a sum, the single card must be captured (not the sum)
- 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.
- 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 (4–6 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
- 10–20 samples × 625 nodes = ~6,000–12,500 evaluations — runs in <100ms on modern hardware