════════════════════════════════════════════════════════════════════════════════ BACKEND MODEL RELATIONSHIPS ════════════════════════════════════════════════════════════════════════════════ ┌─────────────────────────────────────────────────────────────────────────────┐ │ DATABASE MODELS (db_models.py) │ │ SQLAlchemy ORM - PostgreSQL Tables │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─────────────┐ │ GAME │ (games table) │ PK: id(UUID)│ │ │ │ league_id │ ('sba' or 'pd') │ home_team_id│ │ away_team_id│ │ status │ │ game_mode │ │ inning/half │ │ scores │ │ │ │ AI Support: │ │ - home_is_ai│ │ - away_is_ai│ │ - difficulty│ └──────┬──────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ PLAY │ │ LINEUP │ │ SESSION │ │ (plays) │ │(lineups) │ │(sessions)│ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ (WebSocket state) │ │ ┌────────────┼────────────┐ │ │ │ │ │ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ batter │ │pitcher │ │catcher │ │on_first│ │ (FK) │ │ (FK) │ │ (FK) │ │ (FK) │ └────────┘ └────────┘ └────────┘ └────────┘ │ │ │ │ └────────────┴────────────┴───────┘ │ All FK to LINEUP ┌──────────────────────────────┐ │ GAME RELATIONSHIPS │ │ │ │ ┌─────────────────┐ │ │ │ GameCardsetLink │ (PD) │ │ │ - game_id (FK) │ │ │ │ - cardset_id │ │ │ │ - priority │ │ │ └─────────────────┘ │ │ │ │ ┌─────────────────┐ │ │ │ RosterLink │ │ │ │ - game_id (FK) │ │ │ │ - team_id │ │ │ │ - card_id 🅿 │(PD) │ │ │ - player_id 🆂 │(SBA) │ │ │ XOR constraint │ │ │ └─────────────────┘ │ │ │ │ ┌─────────────────┐ │ │ │ Roll │ │ │ │ - roll_id (PK) │ │ │ │ - game_id (FK) │ │ │ │ - roll_type │ │ │ │ - roll_data │ │ │ │ - context │ │ │ └─────────────────┘ │ └──────────────────────────────┘ ═══════════════════════════════════════════════════════════════════════════════ ┌─────────────────────────────────────────────────────────────────────────────┐ │ IN-MEMORY STATE MODELS (game_models.py) │ │ Pydantic v2 - Fast Validation & Serialization │ └─────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────┐ │ GameState │ (Core in-memory state) │ │ │ game_id: UUID │ │ league_id: str │ │ home_team_id │ │ away_team_id │ │ home/away_is_ai │ │ │ │ Game State: │ │ - status │ │ - inning/half │ │ - outs │ │ - scores │ │ │ │ Current Play: │ │ - batter_id │ │ - pitcher_id │ │ - catcher_id │ │ - on_base_code │ │ │ │ Decision Track: │ │ - pending_dec │ │ - decisions_dict │ │ │ │ Batters Index: │ │ - away_idx (0-8) │ │ - home_idx (0-8) │ └────────┬─────────┘ │ ┌─────────────┼─────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │ RunnerState │ │DefDecision │ │OffDecision │ │ │ │ │ │ │ │ lineup_id │ │ alignment │ │ approach │ │ card_id │ │ if_depth │ │ steal_atts │ │ on_base(1-3) │ │ of_depth │ │ hit_and_run │ └──────────────┘ │ hold_runners│ │ bunt_attempt │ └─────────────┘ └──────────────┘ ┌──────────────────┐ │ TeamLineupState │ │ │ │ team_id: int │ │ players: List[ │ │ LineupPlayer │ │ State │ │ ] │ │ │ │ Methods: │ │ - get_batting_ │ │ order() │ │ - get_pitcher() │ │ - get_batter() │ └──────────────────┘ │ │ Contains list of ▼ ┌──────────────────┐ │LineupPlayerState │ │ │ │ lineup_id: int │ │ card_id: int │ │ position: str │ │ batting_order │ │ is_active: bool │ └──────────────────┘ ═══════════════════════════════════════════════════════════════════════════════ ┌─────────────────────────────────────────────────────────────────────────────┐ │ ROSTER MODELS (roster_models.py) │ │ Pydantic - Type-safe Roster Operations │ └─────────────────────────────────────────────────────────────────────────────┘ ┌────────────────────────┐ │ BaseRosterLinkData │ (Abstract) │ │ │ id: Optional[int] │ │ game_id: UUID │ │ team_id: int │ │ │ │ Abstract Methods: │ │ - get_entity_id() │ │ - get_entity_type() │ └───────────┬────────────┘ │ ┌───────────┴───────────┐ │ │ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ PdRosterLinkData │ │ SbaRosterLinkData │ │ │ │ │ │ card_id: int 🅿 │ │ player_id: int 🆂 │ │ │ │ │ │ get_entity_id() │ │ get_entity_id() │ │ → returns card_id │ │ → returns player_id │ │ │ │ │ │ get_entity_type() │ │ get_entity_type() │ │ → returns "card" │ │ → returns "player" │ └──────────────────────┘ └──────────────────────┘ ┌────────────────────────┐ │ RosterLinkCreate │ (Request model) │ │ │ game_id: UUID │ │ team_id: int │ │ card_id: Optional │ │ player_id: Optional │ │ │ │ Validation: │ │ - XOR check (exactly │ │ one ID required) │ │ │ │ Methods: │ │ - to_pd_data() │ │ - to_sba_data() │ └────────────────────────┘ ═══════════════════════════════════════════════════════════════════════════════ KEY RELATIONSHIPS SUMMARY Database (PostgreSQL) In-Memory (Pydantic) Roster Types ═════════════════════ ═══════════════════ ════════════ Game ──┬──> Play GameState BaseRosterLinkData │ │ │ │ │ └──> Lineup ├──> RunnerState ├─> PdRosterLinkData │ (batter, │ │ │ pitcher, ├──> DefensiveDecision └─> SbaRosterLinkData │ catcher, │ │ runners) └──> OffensiveDecision │ ├──> Lineup TeamLineupState │ │ ├──> GameCardsetLink └──> LineupPlayerState │ (PD only) │ ├──> RosterLink RosterLinkCreate ─┬─> PdRosterLinkData │ - card_id (PD) │ │ - player_id (SBA) └─> SbaRosterLinkData │ ├──> GameSession │ (WebSocket state) │ └──> Roll (dice history) ═══════════════════════════════════════════════════════════════════════════════ POLYMORPHIC PATTERN SUMMARY Both `Lineup` and `RosterLink` support PD and SBA leagues: ┌──────────────────────────────────────────────────────────┐ │ Lineup (db_models.py) RosterLink (db_models.py) │ │ ══════════════════ ════════════════════ │ │ │ │ card_id (nullable) card_id (nullable) │ │ player_id (nullable) player_id (nullable) │ │ │ │ CHECK: exactly one populated CHECK: exactly one │ │ UNIQUE: (game_id, card_id) UNIQUE: (game_id, card) │ │ UNIQUE: (game_id, player_id) UNIQUE: (game_id, player)│ │ │ │ 🅿 PD: card_id NOT NULL 🅿 PD: card_id NOT NULL │ │ 🆂 SBA: player_id NOT NULL 🆂 SBA: player_id NOT │ └──────────────────────────────────────────────────────────┘ ═══════════════════════════════════════════════════════════════════════════════ DATA FLOW EXAMPLE 1. Create Game ═══════════ Game (DB) ────────────────> GameState (Memory) │ │ ├─> RosterLink (DB) │ ├─> GameCardsetLink (PD) │ └─> GameSession (DB) │ 2. Setup Lineup ════════════ Lineup (DB) ───────────────> TeamLineupState (Memory) │ │ └─> (card_id or player_id) └─> LineupPlayerState[] 3. Execute Play ════════════ GameState ──┬──> DefensiveDecision ├──> OffensiveDecision ├──> RunnerState[] └──> (resolve play) │ ▼ Play (DB) ──> Save outcome │ ├─> batter_id (FK to Lineup) ├─> pitcher_id (FK to Lineup) ├─> on_first/second/third_id └─> 25+ stat fields 4. Dice Roll Audit ═══════════════ Roll (DB) ──> Stores cryptographic roll history │ ├─> roll_data (JSONB): complete roll ├─> context (JSONB): game situation └─> Used for recovery/analytics ═══════════════════════════════════════════════════════════════════════════════