# Models - Data Models for Game Engine ## Purpose Data models split into two systems: - **Pydantic**: In-memory game state, API contracts, WebSocket serialization - **SQLAlchemy**: Database persistence, relationships, audit trail ## Directory Structure ``` models/ ├── __init__.py # Central exports - import from here ├── game_models.py # Pydantic in-memory state (GameState, decisions) ├── player_models.py # Polymorphic players (BasePlayer → SbaPlayer, PdPlayer) ├── db_models.py # SQLAlchemy ORM (Game, Play, Lineup) └── roster_models.py # Roster link models ``` ## Key Models ### GameState (game_models.py) Core in-memory state for active games. **Critical Fields**: - Identity: `game_id`, `league_id` - Teams: `home_team_id`, `away_team_id`, `*_is_ai` - Game state: `inning`, `half`, `outs`, `*_score` - Runners: `on_first`, `on_second`, `on_third` (LineupPlayerState) - Current: `current_batter`, `current_pitcher`, `current_catcher` - Decisions: `pending_defensive_decision`, `pending_offensive_decision` **Helper Methods**: 20+ including `get_batting_team_id()`, `add_runner()`, `is_game_over()` ### LineupPlayerState Lightweight player reference in lineup. - `lineup_id`, `card_id`, `position`, `batting_order`, `is_active` - `position_rating` (Optional - PD league only) ### TeamLineupState Team's active lineup with helpers: `get_batting_order()`, `get_pitcher()`, `get_batter()` ### Decision Models - **DefensiveDecision**: `alignment`, `infield_depth`, `outfield_depth`, `hold_runners` - **OffensiveDecision**: `action`, `steal_attempts`, `hit_and_run`, `bunt_attempt` ### Player Models (player_models.py) Polymorphic architecture for league-agnostic game engine. ``` BasePlayer (ABC) ├── SbaPlayer - Simple (id, name, image, positions) └── PdPlayer - Complex (scouting data, ratings) ``` Factory: `Player.from_api_data(config, data)` ### Database Models (db_models.py) | Model | Purpose | |-------|---------| | Game | Game container with status, scores, AI flags | | Play | At-bat record with 25+ stat fields | | Lineup | Player assignments and substitutions | | GameSession | WebSocket state tracking | | RosterLink | Eligible cards/players (polymorphic) | ## Common Patterns ### Import from Package ```python from app.models import GameState, Game, SbaPlayer # ✅ from app.models.game_models import GameState # ❌ ``` ### Data Resolution GameState uses minimal LineupPlayerState refs. Get full player data from StateManager: ```python lineup_state = state_manager.get_lineup(game_id, team_id) player = lineup_state.get_player_by_lineup_id(lineup_id) ``` ### Pydantic Validation All models use field validators for data integrity: ```python @field_validator('position') @classmethod def validate_position(cls, v): if v not in VALID_POSITIONS: raise ValueError(...) return v ``` ## References - **Game Recovery**: See `app/core/state_manager.py` for state rebuild - **Database Schema**: See `app/database/CLAUDE.md` for table details - **Type Checking**: See `backend/.claude/type-checking-guide.md` --- **Tests**: `tests/unit/models/` | **Updated**: 2025-01-19