Critical bug fix for issue where Groundball A with runner on first would
fail to execute a double play after game recovery from database.
Root cause: current_on_base_code field was not recalculated during state
recovery, defaulting to 0 (empty bases) even when runners were on base.
This caused runner advancement logic to select Result 1 (batter out,
runners hold) instead of Result 2 (double play).
Changes:
- Added calculate_on_base_code() helper method to GameState model
- Updated _prepare_next_play() to use helper (eliminates duplication)
- Fixed state recovery to calculate current_on_base_code from runners
- Fixed X-Check G1 mapping (was GROUNDBALL_B, should be GROUNDBALL_A)
- Added 5 regression tests to prevent recurrence
Testing:
- All 359 unit tests passing
- New regression tests verify fix and demonstrate bug scenario
- Tested in network dev environment - double plays now work correctly
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Backend:
- Add home_team_dice_color and away_team_dice_color to GameState model
- Extract dice_color from game metadata in StateManager (default: cc0000)
- Add runners_on_base param to roll_ab for chaos check skipping
Frontend - Dice Display:
- Create DiceShapes.vue with SVG d6 (square) and d20 (hexagon) shapes
- Apply home team's dice_color to d6 dice, white for resolution d20
- Show chaos d20 in amber only when WP/PB check triggered
- Add automatic text contrast based on color luminance
- Reduce blank space and remove info bubble from dice results
Frontend - Player Cards:
- Consolidate pitcher/batter cards to single location below diamond
- Add active card highlighting based on dice roll (d6_one: 1-3=batter, 4-6=pitcher)
- New card header format: [Team] Position [Name] with full card image
- Remove redundant card displays from GameBoard and GameplayPanel
- Enlarge PlayerCardModal on desktop (max-w-3xl at 1024px+)
Tests:
- Add DiceShapes.spec.ts with 34 tests for color calculations and rendering
- Update DiceRoller.spec.ts for new DiceShapes integration
- Fix test_roll_dice_success for new runners_on_base parameter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Game Engine Improvements:
- State manager: Add game eviction with configurable max games, idle
timeout, and memory limits
- Game engine: Add resource cleanup on game completion
- Play resolver: Enhanced RunnerAdvancementData with lineup_id for
player name resolution in play-by-play
- Substitution manager: Minor improvements
Test Coverage:
- New test_game_eviction.py with 13 tests for eviction scenarios
- Updated state_manager tests for new functionality
- Updated play_resolver tests for lineup_id handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed missing PlayOutcome.HIT_BY_PITCH handling in resolve_outcome().
HBP was being submitted via WebSocket but rejected with error:
"Unhandled outcome: PlayOutcome.HIT_BY_PITCH"
Changes:
- Added HBP case to play_resolver.py (lines 430-446)
- HBP uses same forced runner advancement logic as WALK
- HBP is NOT classified as is_walk=True (correct for stats)
- Added 2 unit tests: test_hit_by_pitch, test_hit_by_pitch_bases_loaded
Test results: 981/981 passing (100%)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two related bug fixes for gameplay accuracy:
**Backend - play_resolver.py**:
- Fixed DOUBLE2 advancement: Runners now advance exactly 2 bases
* 1st → 3rd, 2nd → home, 3rd → home
* Was incorrectly advancing all runners to home
- Fixed DOUBLE3 advancement: Runners now advance exactly 3 bases
* All runners score (1st+3=4, 2nd+3=5→4, 3rd+3=6→4)
* Updated docstrings for clarity
**Frontend - ManualOutcomeEntry.vue**:
- Fixed hit location requirement logic
* Now requires hit location when runners on base (any outs)
* Was incorrectly restricting to only when outs < 2
* Hit location determines runner advancement regardless of outs
These fixes ensure accurate Strat-O-Matic gameplay simulation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend enhancements for real-time decision workflow:
**New Features**:
- decision_required event emission when game starts and after each decision
- Quick-create endpoint (/games/quick-create) for rapid testing with pre-configured lineups
- WebSocket connection manager integration in GameEngine
**Changes**:
- game_engine.py: Added _emit_decision_required() method and set_connection_manager()
- game_engine.py: Emit decision_required on game start with 5-minute timeout
- games.py: New /quick-create endpoint with Team 35 vs Team 38 lineups
- main.py: Wire connection manager to game_engine singleton
- state_manager.py: Enhanced state management for decision phases
- play_resolver.py: Improved play resolution logic
- handlers.py: Updated WebSocket handlers for new workflow
- backend/CLAUDE.md: Added WebSocket protocol spec reference
**Why**:
Eliminates polling - frontend now gets real-time notification when decisions are needed.
Quick-create saves 2 minutes of lineup setup during each test iteration.
**Testing**:
- Manual testing with terminal client
- WebSocket event flow verified with live frontend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Test Fixes (609/609 passing):
- Fixed DiceSystem API to accept team_id/player_id parameters for audit trails
- Fixed dice roll history timing issue in test
- Fixed terminal client mock to match resolve_play signature (X-Check params)
- Fixed result chart test mocks with missing pitching fields
- Fixed flaky test by using groundball_a (exists in both batting/pitching)
Documentation Updates:
- Added Testing Policy section to backend/CLAUDE.md
- Added Testing Policy section to tests/CLAUDE.md
- Documented 100% unit test requirement before commits
- Added git hook setup instructions
Git Hook System:
- Created .git-hooks/pre-commit script (enforces 100% test pass)
- Created .git-hooks/install-hooks.sh (easy installation)
- Created .git-hooks/README.md (hook documentation)
- Hook automatically runs all unit tests before each commit
- Blocks commits if any test fails
All 609 unit tests now passing (100%)
Integration tests have known asyncpg connection issues (documented)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete integration of position ratings system enabling X-Check defensive plays
to use actual player ratings from PD API with intelligent fallbacks for SBA.
**Live API Testing Verified**: ✅
- Endpoint: GET https://pd.manticorum.com/api/v2/cardpositions?player_id=8807
- Response: 200 OK, 7 positions retrieved successfully
- Cache performance: 16,601x faster (API: 0.214s, Cache: 0.000s)
- Data quality: Real defensive ratings (range 1-5, error 0-88)
**Architecture Overview**:
- League-aware: PD league fetches ratings from API, SBA uses defaults
- StateManager integration: Defenders retrieved from lineup cache
- Self-contained GameState: All data needed for X-Check in memory
- Graceful degradation: Falls back to league averages if ratings unavailable
**Files Created**:
1. app/services/pd_api_client.py (NEW)
- PdApiClient class for PD API integration
- Endpoint: GET /api/v2/cardpositions?player_id={id}&position={pos}
- Async HTTP client using httpx (already in requirements.txt)
- Optional position filtering: get_position_ratings(8807, ['SS', '2B'])
- Returns List[PositionRating] for all positions player can play
- Handles both list and dict response formats
- Comprehensive error handling with logging
2. app/services/position_rating_service.py (NEW)
- PositionRatingService with in-memory caching
- get_ratings_for_card(card_id, league_id) - All positions
- get_rating_for_position(card_id, position, league_id) - Specific position
- Cache performance: >16,000x faster on hits
- Singleton pattern: position_rating_service instance
- TODO Phase 3E-Final: Upgrade to Redis
3. app/services/__init__.py (NEW)
- Package exports for clean imports
4. test_pd_api_live.py (NEW)
- Live API integration test script
- Tests with real PD player 8807 (7 positions)
- Verifies caching, filtering, GameState integration
- Run: `python test_pd_api_live.py`
5. test_pd_api_mock.py (NEW)
- Mock integration test for CI/CD
- Demonstrates flow without API dependency
6. tests/integration/test_position_ratings_api.py (NEW)
- Pytest integration test suite
- Real API tests with player 8807
- Cache verification, SBA skip logic
- Full end-to-end GameState flow
**Files Modified**:
1. app/models/game_models.py
- LineupPlayerState: Added position_rating field (Optional[PositionRating])
- GameState: Added get_defender_for_position(position, state_manager)
- Uses StateManager's lineup cache to find active defender by position
- Iterates through lineup.players to match position + is_active
2. app/config/league_configs.py
- SbaConfig: Added supports_position_ratings() → False
- PdConfig: Added supports_position_ratings() → True
- Enables league-specific behavior without hardcoded conditionals
3. app/core/play_resolver.py
- __init__: Added state_manager parameter for X-Check defender lookup
- _resolve_x_check(): Replaced placeholder defender ratings with actual lookup
- Uses league config to check if ratings supported
- Fetches defender via state.get_defender_for_position()
- Falls back to defaults (range=3, error=15) if ratings unavailable
- Detailed logging for debugging rating lookups
4. app/core/game_engine.py
- Added _load_position_ratings_for_lineup() method
- Loads all position ratings at game start for PD league
- Skips loading for SBA (league config check)
- start_game(): Calls rating loader for both teams before marking active
- PlayResolver instantiation: Now passes state_manager parameter
- Logs: "Loaded X/9 position ratings for team Y"
**X-Check Resolution Flow**:
1. League check: config.supports_position_ratings()?
2. Get defender: state.get_defender_for_position(pos, state_manager)
3. If PD + defender.position_rating exists: Use actual range/error
4. Else if defender found: Use defaults (range=3, error=15)
5. Else: Log warning, use defaults
**Position Rating Loading (Game Start)**:
1. Check if league supports ratings (PD only)
2. Get lineup from StateManager cache
3. For each player:
- Fetch rating from position_rating_service (with caching)
- Set player.position_rating field
4. Cache API responses (16,000x faster on subsequent access)
5. Log success: "Loaded X/9 position ratings for team Y"
**Live Test Results (Player 8807)**:
```
Position Range Error Innings
CF 3 2 372
2B 3 8 212
SS 4 12 159
RF 2 2 74
LF 3 2 62
1B 4 0 46
3B 3 65 34
```
**Testing**:
- ✅ Live API: Player 8807 → 7 positions retrieved successfully
- ✅ Caching: 16,601x performance improvement
- ✅ League config: SBA=False, PD=True
- ✅ GameState integration: Defender lookup working
- ✅ Existing tests: 27/28 config tests passing (1 pre-existing URL failure)
- ✅ Syntax validation: All files compile successfully
**Benefits**:
- ✅ X-Check now uses real defensive ratings in PD league
- ✅ SBA league continues working with manual entry (uses defaults)
- ✅ No breaking changes to existing functionality
- ✅ Graceful degradation if API unavailable
- ✅ In-memory caching reduces API calls by >99%
- ✅ League-agnostic design via config system
- ✅ Production-ready with live API verification
**Phase 3E Status**: Main complete (85% → 90%)
**Next**: Phase 3E-Final (WebSocket events, Redis upgrade, full defensive lineup)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated X-Check runner advancement functions to properly delegate to
existing result handlers for non-error cases.
Changes:
- Updated x_check_g1/g2/g3 signatures to accept GameState, hit_location,
and defensive_decision parameters
- Updated x_check_f1/f2/f3 signatures to accept GameState and hit_location
- Implemented delegation logic: error cases use simple tables, non-error
cases delegate to existing tested result handlers (_execute_result,
_fb_result_*)
- Updated PlayResolver._get_x_check_advancement() to pass new parameters
- Updated all tests to provide required GameState fixtures
Benefits:
- Reuses 13 existing groundball + 4 flyball result handlers (DRY)
- No DP probability needed - X-Check d20 already tested defender
- Full game context: real lineup IDs, outs count, conditional logic
- Error cases remain simple and efficient
Test Results: 264/265 core tests passing (1 pre-existing dice failure)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical bugs in Phase 3D X-Check implementation plus
improved dice audit trail for better tracking.
BUG #1: on_base_code Mapping Error (Sequential vs Bit Field)
============================================================
The implementation incorrectly treated on_base_code as a bit field
when it is actually a sequential lookup mapping.
WRONG (bit field):
Code 3 (0b011) → R1 + R2
Code 4 (0b100) → R3 only
CORRECT (sequential):
Code 3 → R3 only
Code 4 → R1 + R2
Fixed:
- build_advancement_from_code() decoder (sequential mapping)
- build_flyball_advancement_with_error() decoder (sequential mapping)
- 13 test on_base_code values (3↔4 corrections)
- Updated documentation to clarify NOT a bit field
BUG #2: Table Data Not Matching Official Charts
================================================
7 table entries in G1_ADVANCEMENT_TABLE and G2_ADVANCEMENT_TABLE
did not match the official rulebook charts provided by user.
Fixed table entries:
- G1 Code 1, Infield In: Changed Result 3 → 2
- G1 Code 3, Normal: Changed Result 13 → 3
- G1 Code 3, Infield In: Changed Result 3 → 1
- G1 Code 4, Normal: Changed Result 3 → 13
- G1 Code 4, Infield In: Changed Result 4 → 2
- G2 Code 3, Infield In: Changed Result 3 → 1
- G2 Code 4, Normal: Changed Result 5 → 4
Also fixed 7 test expectations to match corrected tables.
IMPROVEMENT: Better Dice Audit Trail
=====================================
Updated _resolve_x_check() in PlayResolver to use proper
dice_system.roll_fielding() instead of manual die rolling.
Benefits:
- All dice tracked in audit trail (roll_id, timestamp, position)
- Automatic error_total calculation (no manual 3d6 addition)
- Consistent with codebase patterns
- Position recorded for historical analysis
Testing:
- All 59 X-Check advancement tests passing (100%)
- All 9 PlayResolver tests passing (100%)
- All table entries validated against official charts
- Complete codebase scan: no bit field operations found
Files modified:
- backend/app/core/x_check_advancement_tables.py
- backend/tests/unit/core/test_x_check_advancement_tables.py
- backend/app/core/play_resolver.py
- .claude/implementation/PHASE_3D_CRITICAL_FIX.md (documentation)
- .claude/implementation/GROUNDBALL_CHART_REFERENCE.md (new)
- .claude/implementation/XCHECK_TEST_VALIDATION.md (new)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented complete X-Check resolution system in PlayResolver with
defense range and error table lookups.
Changes:
- Added X_CHECK case to resolve_outcome() method
- Implemented _resolve_x_check() main resolution method
- Added _adjust_range_for_defensive_position() for playing in logic
- Added _lookup_defense_table() for defense range table lookups
- Added _apply_hash_conversion() for G2#/G3# to SI2 conversion
- Added _lookup_error_chart() for error determination
- Added _determine_final_x_check_outcome() for final outcome mapping
- Added XCheckResult to PlayResult dataclass
- Integrated all Phase 3B tables (defense, error, holding runners)
Features:
- Full defense table lookup (infield, outfield, catcher)
- Error chart lookup with priority ordering (RP > E3 > E2 > E1 > NO)
- Range adjustment for playing in (+1, max 5)
- Hash conversion based on playing in OR holding runner
- Error overrides outs to ERROR outcome
- Rare play handling
- Detailed X-Check audit trail in XCheckResult
Placeholders (to be completed in later phases):
- Defender retrieval from lineup (currently uses placeholder ratings)
- SPD test implementation (currently defaults to G3)
- Batter handedness from player model
- Runner advancement tables (Phase 3D)
Testing:
- All 9 PlayResolver tests passing
- All 36 X-Check table tests passing
- All 51 runner advancement tests passing
- 325/327 total tests passing (2 pre-existing failures unrelated)
- play_resolver.py compiles successfully
Phase 3C Status: 100% COMPLETE ✅
Ready for Phase 3D (Runner Advancement Tables)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Phase 2 refactoring to consolidate runner advancement logic:
**Flyball System Enhancement**:
- Add FLYOUT_BQ variant (medium-shallow depth)
- 4 flyball types with clear semantics: A (deep), B (medium), BQ (medium-shallow), C (shallow)
- Updated helper methods to include FLYOUT_BQ
**RunnerAdvancement Integration**:
- Extend runner_advancement.py to handle both groundballs AND flyballs
- advance_runners() routes to _advance_runners_groundball() or _advance_runners_flyball()
- Comprehensive flyball logic with proper DECIDE mechanics per flyball type
- No-op movements recorded for state recovery consistency
**PlayResolver Refactoring**:
- Consolidate all 4 flyball outcomes to delegate to RunnerAdvancement (DRY)
- Eliminate duplicate flyball resolution code
- Rename helpers for clarity: _advance_on_single_1/_advance_on_single_2 (was _advance_on_single)
- Fix single/double advancement logic for different hit types
**State Recovery Fix**:
- Fix state_manager.py game recovery to build LineupPlayerState objects properly
- Use get_lineup_player() helper to construct from lineup data
- Correctly track runners in on_first/on_second/on_third fields (matches Phase 2 model)
**Database Support**:
- Add runner tracking fields to play data for accurate recovery
- Include batter_id, on_first_id, on_second_id, on_third_id, and *_final fields
**Type Safety Improvements**:
- Fix lineup_id access throughout runner_advancement.py (was accessing on_first directly, now on_first.lineup_id)
- Make current_batter_lineup_id non-optional (always set by _prepare_next_play)
- Add type: ignore for known SQLAlchemy false positives
**Documentation**:
- Update CLAUDE.md with comprehensive flyball documentation
- Add flyball types table, usage examples, and test coverage notes
- Document differences between groundball and flyball mechanics
**Testing**:
- Add test_flyball_advancement.py with 21 flyball tests
- Coverage: all 4 types, DECIDE scenarios, no-op movements, edge cases
🚀 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major Refactor: Outcome-First Architecture
- PlayResolver now accepts league_id and auto_mode in constructor
- Added core resolve_outcome() method - all resolution logic in one place
- Added resolve_manual_play() wrapper for manual submissions (primary)
- Added resolve_auto_play() wrapper for PD auto mode (rare)
- Removed SimplifiedResultChart (obsolete with new architecture)
- Removed play_resolver singleton
RunnerAdvancement Integration:
- All groundball outcomes (GROUNDBALL_A/B/C) now use RunnerAdvancement
- Proper DP probability calculation with positioning modifiers
- Hit location tracked for all relevant outcomes
- 13 result types fully integrated from advancement charts
Game State Updates:
- Added auto_mode field to GameState (stored per-game)
- Updated state_manager.create_game() to accept auto_mode parameter
- GameEngine now uses state.auto_mode to create appropriate resolver
League Configuration:
- Added supports_auto_mode() to BaseGameConfig
- SbaConfig: returns False (no digitized cards)
- PdConfig: returns True (has digitized ratings)
- PlayResolver validates auto mode support and raises error for SBA
Play Results:
- Added hit_location field to PlayResult
- Groundballs include location from RunnerAdvancement
- Flyouts track hit_location for tag-up logic (future)
- Other outcomes have hit_location=None
Testing:
- Completely rewrote test_play_resolver.py for new architecture
- 9 new tests covering initialization, strikeouts, walks, groundballs, home runs
- All 9 tests passing
- All 180 core tests still passing (1 pre-existing failure unrelated)
Terminal Client:
- No changes needed - defaults to manual mode (auto_mode=False)
- Perfect for human testing of manual submissions
This completes Week 7 Task 6 - the final task of Week 7!
Week 7 is now 100% complete with all 8 tasks done.
🎯 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add ability to force specific play outcomes instead of random dice rolls,
enabling targeted testing of specific game scenarios.
Changes:
- play_resolver.resolve_play(): Add forced_outcome parameter, bypass dice
rolls when provided, create dummy AbRoll with placeholder values
- game_engine.resolve_play(): Accept and pass through forced_outcome param
- terminal_client/commands.py: Pass forced_outcome to game engine
Testing:
- Verified TRIPLE, HOMERUN, and STRIKEOUT outcomes work correctly
- Dummy AbRoll properly constructed with all required fields
- Game state updates correctly with forced outcomes
Example usage in REPL:
resolve_with triple
resolve_with homerun
Fixes terminal client testing workflow to allow controlled scenarios.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Renamed check_d20 → chaos_d20 throughout dice system
- Expanded PlayOutcome enum with granular variants (SINGLE_1/2, DOUBLE_2/3, GROUNDBALL_A/B/C, etc.)
- Integrated PlayOutcome from app.config into PlayResolver
- Added play_metadata support for uncapped hit tracking
- Updated all tests (139/140 passing)
Week 6: 100% Complete - Ready for Phase 3
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit includes cleanup from model refactoring and terminal client
modularization for better code organization and maintainability.
## Game Models Refactor
**Removed RunnerState class:**
- Eliminated separate RunnerState model (was redundant)
- Replaced runners: List[RunnerState] with direct base references:
- on_first: Optional[LineupPlayerState]
- on_second: Optional[LineupPlayerState]
- on_third: Optional[LineupPlayerState]
- Updated helper methods:
- get_runner_at_base() now returns LineupPlayerState directly
- get_all_runners() returns List[Tuple[int, LineupPlayerState]]
- is_runner_on_X() simplified to direct None checks
**Benefits:**
- Matches database structure (plays table has on_first_id, etc.)
- Simpler state management (direct references vs list management)
- Better type safety (LineupPlayerState vs generic runner)
- Easier to work with in game engine logic
**Updated files:**
- app/models/game_models.py - Removed RunnerState, updated GameState
- app/core/play_resolver.py - Use get_all_runners() instead of state.runners
- app/core/validators.py - Updated runner access patterns
- tests/unit/models/test_game_models.py - Updated test assertions
- tests/unit/core/test_play_resolver.py - Updated test data
- tests/unit/core/test_validators.py - Updated test data
## Terminal Client Refactor
**Modularization (DRY principle):**
Created separate modules for better code organization:
1. **terminal_client/commands.py** (10,243 bytes)
- Shared command functions for game operations
- Used by both CLI (main.py) and REPL (repl.py)
- Functions: submit_defensive_decision, submit_offensive_decision,
resolve_play, quick_play_sequence
- Single source of truth for command logic
2. **terminal_client/arg_parser.py** (7,280 bytes)
- Centralized argument parsing and validation
- Handles defensive/offensive decision arguments
- Validates formats (alignment, depths, hold runners, steal attempts)
3. **terminal_client/completions.py** (10,357 bytes)
- TAB completion support for REPL mode
- Command completions, option completions, dynamic completions
- Game ID completions, defensive/offensive option suggestions
4. **terminal_client/help_text.py** (10,839 bytes)
- Centralized help text and command documentation
- Detailed command descriptions
- Usage examples for all commands
**Updated main modules:**
- terminal_client/main.py - Simplified by using shared commands module
- terminal_client/repl.py - Cleaner with shared functions and completions
**Benefits:**
- DRY: Behavior consistent between CLI and REPL modes
- Maintainability: Changes in one place affect both interfaces
- Testability: Can test commands module independently
- Organization: Clear separation of concerns
## Documentation
**New files:**
- app/models/visual_model_relationships.md
- Visual documentation of model relationships
- Helps understand data flow between models
- terminal_client/update_docs/ (6 phase documentation files)
- Phased documentation for terminal client evolution
- Historical context for implementation decisions
## Tests
**New test files:**
- tests/unit/terminal_client/__init__.py
- tests/unit/terminal_client/test_arg_parser.py
- tests/unit/terminal_client/test_commands.py
- tests/unit/terminal_client/test_completions.py
- tests/unit/terminal_client/test_help_text.py
**Updated tests:**
- Integration tests updated for new runner model
- Unit tests updated for model changes
- All tests passing with new structure
## Summary
- ✅ Simplified game state model (removed RunnerState)
- ✅ Better alignment with database structure
- ✅ Modularized terminal client (DRY principle)
- ✅ Shared command logic between CLI and REPL
- ✅ Comprehensive test coverage
- ✅ Improved documentation
Total changes: 26 files modified/created
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Core Components:
✅ GameValidator (validators.py)
- Validates game state and decisions
- Rule enforcement for baseball gameplay
- Game-over and inning continuation logic
✅ PlayResolver (play_resolver.py)
- Resolves play outcomes using AbRoll system
- Simplified result charts for MVP
- Handles wild pitch/passed ball checks
- Runner advancement logic for all hit types
- PlayOutcome enum with 12 outcome types
✅ GameEngine (game_engine.py)
- Orchestrates complete game flow
- Start game, submit decisions, resolve plays
- Integrates DiceSystem with roll context
- Batch saves rolls at end of each half-inning
- Persists plays and game state to database
- Manages inning advancement and game completion
Integration Features:
- Uses advanced AbRoll system (not simplified d20)
- Roll context tracking per inning
- Batch persistence at inning boundaries
- Full audit trail with roll history
- State synchronization between memory and database
Architecture:
GameEngine → PlayResolver → DiceSystem
↓ ↓
GameValidator StateManager
↓ ↓
Database In-Memory Cache
Ready For:
✅ End-to-end at-bat testing
✅ WebSocket integration
✅ Result chart configuration
✅ Advanced decision logic (Phase 3)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>