# Config Module - League Configuration & Play Outcomes ## Purpose League-specific configuration system and play outcome definitions. Provides immutable config objects for game rules, API endpoints, and result chart behaviors. ## Structure ``` app/config/ ├── __init__.py # Public exports ├── base_config.py # Abstract BaseConfig ├── league_configs.py # SbaConfig, PdConfig implementations ├── result_charts.py # PlayOutcome enum └── common_x_check_tables.py # X-Check defense/error tables ``` ## League Configuration ### Usage ```python from app.config import get_league_config config = get_league_config("sba") # or "pd" api_url = config.get_api_base_url() supports_ratings = config.supports_position_ratings() ``` ### BaseConfig (Abstract) Common interface for league configs: - `league_id`, `innings`, `dh_rule`, `mercy_rule` - `get_api_base_url()`, `get_result_chart_name()` - `supports_position_ratings()`, `supports_auto_mode()` ### SbaConfig - Manual outcome selection only - No position ratings support - Simple player data ### PdConfig - Manual or auto resolution - Position ratings from API - Cardset validation - Detailed scouting data ### Immutability Configs use Pydantic `frozen=True`: ```python config.innings = 7 # Raises ValidationError ``` ## PlayOutcome Enum Universal enum for all play outcomes (both leagues). ### Categories ```python from app.config import PlayOutcome # Standard outcomes PlayOutcome.STRIKEOUT PlayOutcome.SINGLE PlayOutcome.HOMERUN # Uncapped (requires advancement decision) PlayOutcome.SINGLE_UNCAPPED PlayOutcome.DOUBLE_UNCAPPED # Interrupts (logged with pa=0) PlayOutcome.WILD_PITCH PlayOutcome.STOLEN_BASE ``` ### Helper Methods ```python outcome.is_hit() # True for hits outcome.is_out() # True for outs outcome.is_uncapped() # Requires decision tree outcome.get_bases_advanced() # Base advancement ``` ## X-Check Tables Defense range tables and error charts for defensive play resolution. ### Defense Tables - `INFIELD_DEFENSE_TABLE` - 20×5 (d20 × range) - `OUTFIELD_DEFENSE_TABLE` - 20×5 - `CATCHER_DEFENSE_TABLE` - 20×5 ### Error Charts - 3d6 (3-18) by error rating (0-25) - Results: RP (rare play), E1-E3, NO ### Helpers ```python from app.config import get_fielders_holding_runners, get_error_chart_for_position holders = get_fielders_holding_runners([1, 2], 'R') # Runners on 1st & 2nd, RHB chart = get_error_chart_for_position('SS') ``` ## Common Tasks ### Get League-Specific Behavior ```python config = get_league_config(state.league_id) if config.supports_position_ratings(): ratings = await fetch_ratings(player_id) ``` ### Validate Outcome ```python outcome = PlayOutcome.GROUNDBALL_C if outcome.requires_location(): assert hit_location is not None ``` ## References - **League Differences**: See root `CLAUDE.md` - **Play Resolution**: See `../core/play_resolver.py` --- **Tests**: `tests/unit/config/` | **Updated**: 2025-01-19