Updated CLAUDE.md files to document recent changes including the GameState
refactoring and new X-Check testing capabilities in the terminal client.
Changes to app/models/CLAUDE.md:
- Updated GameState field documentation
- Replaced current_batter_lineup_id references with current_batter
- Documented LineupPlayerState requirement for current players
- Added comprehensive usage examples
- Added "Recent Updates" section documenting GameState refactoring
- Before/after code examples showing migration path
- Explanation of why the change was made
- Migration notes for developers
- List of all affected files (7 files updated)
Changes to terminal_client/CLAUDE.md:
- Added "2025-11-04: X-Check Testing & GameState Refactoring" section
- New feature: resolve_with x-check <position> command
- Complete X-Check resolution with defense tables and error charts
- Shows all resolution steps with audit trail
- Works with actual player ratings from PD API
- Documented 8 X-Check commands now in help system
- roll_jump / test_jump, roll_fielding / test_fielding
- test_location, rollback, force_wild_pitch, force_passed_ball
- Bug fixes documented
- GameState structure updates (display.py, repl.py)
- Game recovery fix (state_manager.py)
- DO3 advancement fix (play_resolver.py)
- Complete testing workflow examples
- List of 7 files updated
- Test coverage status (all passing)
- Updated footer: Last Updated 2025-11-04
These documentation updates provide clear migration guides for the GameState
refactoring and comprehensive examples for the new X-Check testing features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
851 lines
23 KiB
Markdown
851 lines
23 KiB
Markdown
# Terminal Client - Game Engine Testing Tool
|
||
|
||
## Overview
|
||
|
||
Interactive REPL (Read-Eval-Print Loop) terminal UI for testing the game engine directly without WebSockets or frontend dependencies. Built with Python's `cmd` module, Click, and Rich for a polished CLI experience.
|
||
|
||
**Purpose**: Rapid iteration on game engine development without needing to build/maintain a web frontend during core logic development.
|
||
|
||
**Key Feature**: Persistent in-memory state - the game engine `state_manager` stays loaded throughout your REPL session, allowing you to test gameplay flow without losing state between commands.
|
||
|
||
## Usage Modes
|
||
|
||
### Interactive REPL (Recommended)
|
||
|
||
Start the interactive shell for persistent in-memory state:
|
||
|
||
```bash
|
||
uv run python -m terminal_client
|
||
# Or: source .venv/bin/activate && python -m terminal_client
|
||
|
||
# Then type commands interactively:
|
||
⚾ > new_game
|
||
⚾ > defensive
|
||
⚾ > offensive
|
||
⚾ > resolve
|
||
⚾ > quick_play 10
|
||
⚾ > status
|
||
⚾ > quit
|
||
```
|
||
|
||
**Advantages**:
|
||
- ✅ Game state stays in memory across commands
|
||
- ✅ Fast iteration - no process startup overhead
|
||
- ✅ Natural workflow like `psql` or `redis-cli`
|
||
- ✅ Persistent event loop prevents database connection issues
|
||
|
||
### Standalone Commands (Alternative)
|
||
|
||
Run individual commands with persistent config file:
|
||
|
||
```bash
|
||
uv run python -m terminal_client new-game
|
||
uv run python -m terminal_client defensive --alignment normal
|
||
uv run python -m terminal_client offensive --approach power
|
||
uv run python -m terminal_client resolve
|
||
```
|
||
|
||
**Note**: Config file (`~/.terminal_client_config.json`) remembers current game between commands.
|
||
|
||
## Architecture
|
||
|
||
### Design Decisions
|
||
|
||
**Why Terminal UI?**
|
||
- ✅ Zero frontend overhead - test game logic immediately
|
||
- ✅ No WebSocket complexity - direct function calls to GameEngine
|
||
- ✅ Fast iteration - change code, test instantly
|
||
- ✅ Easy debugging - logs and state visible in same terminal
|
||
- ✅ CI/CD ready - can be scripted for automated testing
|
||
|
||
**Why Click over Typer?**
|
||
- ✅ Already installed (FastAPI dependency)
|
||
- ✅ Battle-tested and stable (v8.1.8)
|
||
- ✅ Python 3.13 compatible
|
||
- ✅ No version compatibility issues
|
||
|
||
**Why Rich?**
|
||
- ✅ Beautiful formatted output (colors, panels, tables)
|
||
- ✅ Clear game state visualization
|
||
- ✅ Better testing UX than plain print statements
|
||
|
||
**Why REPL (cmd module)?**
|
||
- ✅ Single persistent process - state manager stays in memory
|
||
- ✅ Persistent event loop - no database connection conflicts
|
||
- ✅ Command history and readline support
|
||
- ✅ Tab completion (future enhancement)
|
||
|
||
### Technology Stack
|
||
|
||
```python
|
||
click==8.1.8 # CLI framework (already installed via FastAPI)
|
||
rich==13.9.4 # Terminal formatting and colors
|
||
cmd (stdlib) # REPL framework (built into Python)
|
||
```
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
terminal_client/
|
||
├── __init__.py # Package marker
|
||
├── __main__.py # Entry point - routes to REPL or CLI
|
||
├── repl.py # Interactive REPL with cmd.Cmd
|
||
├── main.py # Click CLI standalone commands
|
||
├── display.py # Rich formatting for game state
|
||
├── config.py # Persistent configuration file manager
|
||
└── CLAUDE.md # This file
|
||
```
|
||
|
||
## REPL Commands Reference
|
||
|
||
All commands in interactive REPL mode. Use underscores (e.g., `new_game` not `new-game`).
|
||
|
||
### new_game
|
||
|
||
Create a new game with lineups and start it (all-in-one).
|
||
|
||
```
|
||
⚾ > new_game [--league sba|pd] [--home-team N] [--away-team N]
|
||
|
||
Examples:
|
||
new_game
|
||
new_game --league pd
|
||
new_game --home-team 5 --away-team 3
|
||
```
|
||
|
||
Automatically:
|
||
1. Creates game in database
|
||
2. Generates 9-player lineups for both teams
|
||
3. Starts the game
|
||
4. Sets as current game
|
||
|
||
### defensive
|
||
|
||
Submit defensive decision.
|
||
|
||
```
|
||
⚾ > defensive [--alignment TYPE] [--infield DEPTH] [--outfield DEPTH] [--hold BASES]
|
||
|
||
Options:
|
||
--alignment normal, shifted_left, shifted_right, extreme_shift
|
||
--infield in, normal, back, double_play
|
||
--outfield in, normal, back
|
||
--hold Comma-separated bases (e.g., 1,3)
|
||
|
||
Examples:
|
||
defensive
|
||
defensive --alignment shifted_left
|
||
defensive --infield double_play --hold 1,3
|
||
```
|
||
|
||
### offensive
|
||
|
||
Submit offensive decision.
|
||
|
||
```
|
||
⚾ > offensive [--approach TYPE] [--steal BASES] [--hit-run] [--bunt]
|
||
|
||
Options:
|
||
--approach normal, contact, power, patient
|
||
--steal Comma-separated bases (e.g., 2,3)
|
||
--hit-run Enable hit-and-run
|
||
--bunt Attempt bunt
|
||
|
||
Examples:
|
||
offensive
|
||
offensive --approach power
|
||
offensive --steal 2 --hit-run
|
||
```
|
||
|
||
### resolve
|
||
|
||
Resolve the current play.
|
||
|
||
```
|
||
⚾ > resolve
|
||
```
|
||
|
||
Both defensive and offensive decisions must be submitted first.
|
||
Displays play result with dice roll, runner advancement, and updated state.
|
||
|
||
### status
|
||
|
||
Display current game state.
|
||
|
||
```
|
||
⚾ > status
|
||
```
|
||
|
||
Shows score, inning, outs, runners, and pending decisions.
|
||
|
||
### quick_play
|
||
|
||
Auto-play multiple plays with default decisions.
|
||
|
||
```
|
||
⚾ > quick_play [COUNT]
|
||
|
||
Examples:
|
||
quick_play # Play 1 play
|
||
quick_play 10 # Play 10 plays
|
||
quick_play 27 # Play ~3 innings
|
||
```
|
||
|
||
Perfect for rapidly advancing game state for testing.
|
||
|
||
### box_score
|
||
|
||
Display box score.
|
||
|
||
```
|
||
⚾ > box_score
|
||
```
|
||
|
||
### list_games
|
||
|
||
List all games in state manager (in memory).
|
||
|
||
```
|
||
⚾ > list_games
|
||
```
|
||
|
||
Shows active games with current game marked.
|
||
|
||
### use_game
|
||
|
||
Switch to a different game.
|
||
|
||
```
|
||
⚾ > use_game <game_id>
|
||
|
||
Example:
|
||
use_game a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||
```
|
||
|
||
### config
|
||
|
||
Show configuration.
|
||
|
||
```
|
||
⚾ > config
|
||
```
|
||
|
||
Displays config file path and current game.
|
||
|
||
### clear
|
||
|
||
Clear the screen.
|
||
|
||
```
|
||
⚾ > clear
|
||
```
|
||
|
||
### quit / exit
|
||
|
||
Exit the REPL.
|
||
|
||
```
|
||
⚾ > quit
|
||
⚾ > exit
|
||
```
|
||
|
||
Or press Ctrl+D.
|
||
|
||
---
|
||
|
||
## Standalone Commands Reference
|
||
|
||
For running individual commands outside REPL mode.
|
||
|
||
### new-game
|
||
```bash
|
||
uv run python -m terminal_client new-game [OPTIONS]
|
||
|
||
Options:
|
||
--league TEXT League (sba or pd) [default: sba]
|
||
--game-id TEXT Game UUID (auto-generated if not provided)
|
||
--home-team INTEGER Home team ID [default: 1]
|
||
--away-team INTEGER Away team ID [default: 2]
|
||
|
||
Example:
|
||
uv run python -m terminal_client start-game --league sba
|
||
uv run python -m terminal_client start-game --game-id <uuid> --home-team 5 --away-team 3
|
||
```
|
||
|
||
### Defensive Decision
|
||
```bash
|
||
uv run python -m terminal_client defensive [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
--alignment TEXT Defensive alignment [default: normal]
|
||
Values: normal, shifted_left, shifted_right, extreme_shift
|
||
--infield TEXT Infield depth [default: normal]
|
||
Values: in, normal, back, double_play
|
||
--outfield TEXT Outfield depth [default: normal]
|
||
Values: in, normal, back
|
||
--hold TEXT Comma-separated bases to hold (e.g., 1,3)
|
||
|
||
Example:
|
||
uv run python -m terminal_client defensive --alignment shifted_left
|
||
uv run python -m terminal_client defensive --infield double_play --hold 1,3
|
||
```
|
||
|
||
### Offensive Decision
|
||
```bash
|
||
uv run python -m terminal_client offensive [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
--approach TEXT Batting approach [default: normal]
|
||
Values: normal, contact, power, patient
|
||
--steal TEXT Comma-separated bases to steal (e.g., 2,3)
|
||
--hit-run Hit-and-run play (flag)
|
||
--bunt Bunt attempt (flag)
|
||
|
||
Example:
|
||
uv run python -m terminal_client offensive --approach power
|
||
uv run python -m terminal_client offensive --steal 2 --hit-run
|
||
```
|
||
|
||
### Resolve Play
|
||
```bash
|
||
uv run python -m terminal_client resolve [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
uv run python -m terminal_client resolve
|
||
```
|
||
|
||
Resolves the current play using submitted defensive and offensive decisions.
|
||
Displays full play result with dice rolls, runner advancement, and updated game state.
|
||
|
||
### Game Status
|
||
```bash
|
||
uv run python -m terminal_client status [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
uv run python -m terminal_client status
|
||
```
|
||
|
||
### Box Score
|
||
```bash
|
||
uv run python -m terminal_client box-score [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
uv run python -m terminal_client box-score
|
||
```
|
||
|
||
### Quick Play
|
||
```bash
|
||
uv run python -m terminal_client quick-play [OPTIONS]
|
||
|
||
Options:
|
||
--count INTEGER Number of plays to execute [default: 1]
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
uv run python -m terminal_client quick-play --count 10
|
||
uv run python -m terminal_client quick-play --count 27 # Full inning
|
||
```
|
||
|
||
**Use Case**: Rapidly advance game state for testing specific scenarios (e.g., test 9th inning logic).
|
||
|
||
Submits default decisions (normal alignment, normal approach) and auto-resolves plays.
|
||
|
||
### List Games
|
||
```bash
|
||
uv run python -m terminal_client list-games
|
||
|
||
Example:
|
||
uv run python -m terminal_client list-games
|
||
```
|
||
|
||
Shows all active games in the state manager.
|
||
|
||
### Use Game
|
||
```bash
|
||
uv run python -m terminal_client use-game <game_id>
|
||
|
||
Example:
|
||
uv run python -m terminal_client use-game a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||
```
|
||
|
||
Switch to a different game (sets as "current" game for subsequent commands).
|
||
|
||
## Usage Patterns
|
||
|
||
### Typical REPL Testing Workflow
|
||
|
||
```bash
|
||
# Start REPL
|
||
uv run python -m terminal_client
|
||
# Or: source .venv/bin/activate && python -m terminal_client
|
||
|
||
# Create and play a game
|
||
⚾ > new_game
|
||
⚾ > defensive
|
||
⚾ > offensive
|
||
⚾ > resolve
|
||
⚾ > status
|
||
|
||
# Continue playing
|
||
⚾ > defensive --alignment shifted_left
|
||
⚾ > offensive --approach power
|
||
⚾ > resolve
|
||
|
||
# Or use quick-play to auto-advance
|
||
⚾ > quick_play 10
|
||
|
||
# Check final state
|
||
⚾ > status
|
||
⚾ > quit
|
||
```
|
||
|
||
**Advantages**: Game state stays in memory throughout the session!
|
||
|
||
### Testing Specific Scenarios
|
||
|
||
```bash
|
||
# Test defensive shifts
|
||
uv run python -m terminal_client start-game
|
||
uv run python -m terminal_client defensive --alignment shifted_left --infield double_play
|
||
uv run python -m terminal_client offensive
|
||
uv run python -m terminal_client resolve
|
||
|
||
# Test stealing
|
||
uv run python -m terminal_client start-game
|
||
uv run python -m terminal_client defensive
|
||
uv run python -m terminal_client offensive --steal 2,3 # Double steal
|
||
uv run python -m terminal_client resolve
|
||
|
||
# Test hit-and-run
|
||
uv run python -m terminal_client defensive
|
||
uv run python -m terminal_client offensive --hit-run
|
||
uv run python -m terminal_client resolve
|
||
|
||
# Advance to late game quickly
|
||
uv run python -m terminal_client start-game
|
||
uv run python -m terminal_client quick-play --count 50 # ~6 innings
|
||
uv run python -m terminal_client status
|
||
```
|
||
|
||
### Multiple Games
|
||
|
||
```bash
|
||
# Start game 1
|
||
uv run python -m terminal_client start-game
|
||
# ... play some ...
|
||
|
||
# Start game 2
|
||
uv run python -m terminal_client start-game
|
||
# ... play some ...
|
||
|
||
# List all games
|
||
uv run python -m terminal_client list-games
|
||
|
||
# Switch back to game 1
|
||
uv run python -m terminal_client use-game <game-1-uuid>
|
||
uv run python -m terminal_client status
|
||
```
|
||
|
||
## Display Features
|
||
|
||
### Game State Panel
|
||
- **Game Info**: UUID, league, status
|
||
- **Score**: Away vs Home
|
||
- **Inning**: Current inning and half
|
||
- **Outs**: Current out count
|
||
- **Runners**: Bases occupied with lineup IDs
|
||
- **Current Players**: Batter, pitcher (by lineup ID)
|
||
- **Pending Decision**: Enhanced display showing what action is needed next (Added 2025-10-28)
|
||
- Shows "⚠️ WAITING FOR ACTION" header when play is pending
|
||
- Displays specific message: "The defense needs to submit their decision" or "Ready to resolve play"
|
||
- Shows exact command to run next: `defensive [OPTIONS]`, `offensive [OPTIONS]`, or `resolve`
|
||
- Color-coded for clarity (yellow warning + cyan command hints)
|
||
- **Last Play**: Result description
|
||
|
||
### Play Result Panel
|
||
- **Outcome**: Hit type (GB, FB, LD, etc.)
|
||
- **Result Description**: Human-readable play result
|
||
- **Dice Roll**: Actual d20 roll with context
|
||
- **Outs/Runs**: Changes from play
|
||
- **Runner Movement**: Base-by-base advancement
|
||
- **Updated Score**: Current score after play
|
||
|
||
### Color Coding
|
||
- ✅ Green: Success messages, runs scored
|
||
- ❌ Red: Error messages, outs recorded
|
||
- ℹ️ Blue: Info messages
|
||
- ⚠️ Yellow: Warning messages
|
||
- Cyan: Game state highlights
|
||
|
||
## Implementation Details
|
||
|
||
### Persistent Event Loop (REPL Mode)
|
||
|
||
The REPL uses a single persistent event loop for the entire session:
|
||
|
||
```python
|
||
def __init__(self):
|
||
# Create persistent event loop
|
||
self.loop = asyncio.new_event_loop()
|
||
asyncio.set_event_loop(self.loop)
|
||
|
||
def _run_async(self, coro):
|
||
# Reuse same loop for all commands
|
||
return self.loop.run_until_complete(coro)
|
||
|
||
def do_quit(self, arg):
|
||
# Clean up on exit
|
||
self.loop.close()
|
||
```
|
||
|
||
**Why**: Prevents database connection pool conflicts that occur when creating new event loops for each command.
|
||
|
||
### Game State Management
|
||
|
||
**REPL Mode**: Game state manager stays in memory throughout the session.
|
||
- In-memory state persists across all commands
|
||
- O(1) state lookups
|
||
- Perfect for testing gameplay flow
|
||
|
||
**Standalone Mode**: Uses persistent config file (`~/.terminal_client_config.json`).
|
||
- Stores current game UUID between command invocations
|
||
- Set automatically by `new-game` or `use-game`
|
||
- Used as default if `--game-id` not specified
|
||
|
||
### Async Command Pattern
|
||
|
||
All commands use the same async pattern:
|
||
```python
|
||
@cli.command()
|
||
@click.option('--game-id', default=None)
|
||
def some_command(game_id):
|
||
async def _some_command():
|
||
gid = UUID(game_id) if game_id else get_current_game()
|
||
# ... async operations ...
|
||
state = await game_engine.some_method(gid)
|
||
display.show_something(state)
|
||
|
||
asyncio.run(_some_command())
|
||
```
|
||
|
||
This allows direct async calls to GameEngine without WebSocket overhead.
|
||
|
||
### Error Handling
|
||
|
||
- **Click Abort**: Raises `click.Abort()` on errors (clean exit)
|
||
- **Rich Display**: All errors shown with colored formatting
|
||
- **Logger**: Exceptions logged with full traceback
|
||
- **User-Friendly**: Clear error messages (not stack traces)
|
||
|
||
## Testing with Terminal Client
|
||
|
||
### Unit Test Scenarios
|
||
|
||
```bash
|
||
# Test game startup validation
|
||
uv run python -m terminal_client start-game
|
||
# Should fail: No lineups set
|
||
|
||
# Test invalid decisions
|
||
uv run python -m terminal_client defensive --alignment invalid_value
|
||
# Should fail: ValidationError
|
||
|
||
# Test resolve without decisions
|
||
uv run python -m terminal_client resolve
|
||
# Should fail: No decisions submitted
|
||
```
|
||
|
||
### Integration Test Scenarios
|
||
|
||
```bash
|
||
# Full game flow
|
||
uv run python -m terminal_client start-game
|
||
# ... set lineups via database directly ...
|
||
uv run python -m terminal_client start-game # Retry
|
||
uv run python -m terminal_client quick-play --count 100
|
||
uv run python -m terminal_client status
|
||
# Verify: Game status = "completed"
|
||
|
||
# State persistence
|
||
uv run python -m terminal_client start-game
|
||
# Note the game UUID
|
||
uv run python -m terminal_client quick-play --count 10
|
||
# Kill terminal
|
||
# Restart and use same UUID
|
||
uv run python -m terminal_client use-game <uuid>
|
||
uv run python -m terminal_client status
|
||
# Verify: State recovered from database
|
||
```
|
||
|
||
## Limitations
|
||
|
||
### Current Limitations (Phase 2)
|
||
1. **No Lineup Management**: Must set lineups via database directly
|
||
2. **No Player Data**: Shows lineup IDs only (no names/stats)
|
||
3. **Simple Box Score**: Only shows final scores (no detailed stats)
|
||
4. **No Substitutions**: Cannot make mid-game substitutions
|
||
5. **No AI Decisions**: All decisions manual (no AI opponent)
|
||
|
||
### Future Enhancements (Post-MVP)
|
||
- Interactive lineup builder
|
||
- Player name/stat display from league API
|
||
- Full box score with batting stats
|
||
- Substitution commands
|
||
- AI decision simulation
|
||
- Play-by-play export
|
||
- Replay mode for completed games
|
||
|
||
## Development Notes
|
||
|
||
### Adding New Commands
|
||
|
||
```python
|
||
# In main.py
|
||
@cli.command('new-command')
|
||
@click.option('--some-option', default='value', help='Description')
|
||
def new_command(some_option):
|
||
"""Command description for help text."""
|
||
async def _new_command():
|
||
# Implementation
|
||
pass
|
||
|
||
asyncio.run(_new_command())
|
||
```
|
||
|
||
### Adding New Display Functions
|
||
|
||
```python
|
||
# In display.py
|
||
def display_new_thing(data: SomeModel) -> None:
|
||
"""Display new thing with Rich formatting."""
|
||
panel = Panel(
|
||
Text("Content here"),
|
||
title="[bold]Title[/bold]",
|
||
border_style="color",
|
||
box=box.ROUNDED
|
||
)
|
||
console.print(panel)
|
||
```
|
||
|
||
### Logging
|
||
|
||
All commands use the module logger:
|
||
```python
|
||
logger = logging.getLogger(f'{__name__}.main')
|
||
|
||
logger.info("Game started")
|
||
logger.error("Failed to resolve play", exc_info=True)
|
||
```
|
||
|
||
Logs appear in both terminal and `backend/logs/app_YYYYMMDD.log`.
|
||
|
||
## Troubleshooting
|
||
|
||
### Command Not Found
|
||
```bash
|
||
# Wrong
|
||
python terminal_client/main.py # ❌
|
||
|
||
# Correct
|
||
uv run python -m terminal_client start-game # ✅
|
||
```
|
||
|
||
### Import Errors
|
||
```bash
|
||
# Ensure you're in backend directory
|
||
cd backend
|
||
|
||
# Recommended: Use UV (auto-activates venv)
|
||
uv run python -m terminal_client start-game
|
||
|
||
# Alternative: Manually activate venv
|
||
source .venv/bin/activate
|
||
python -m terminal_client start-game
|
||
```
|
||
|
||
### Game Not Found
|
||
```bash
|
||
# Check active games
|
||
uv run python -m terminal_client list-games
|
||
|
||
# Use specific game ID
|
||
uv run python -m terminal_client status --game-id <uuid>
|
||
```
|
||
|
||
### Validation Errors
|
||
Check logs for detailed error messages:
|
||
```bash
|
||
tail -f logs/app_$(date +%Y%m%d).log
|
||
```
|
||
|
||
## Performance
|
||
|
||
### REPL Mode
|
||
- **Startup Time**: ~500ms (one-time on launch)
|
||
- **Command Execution**: <50ms (in-memory state lookups)
|
||
- **State Display**: <50ms (Rich rendering)
|
||
- **Quick Play (10 plays)**: ~4-5 seconds (includes 0.3s sleeps per play)
|
||
- **Memory**: ~10MB for active game state
|
||
|
||
### Standalone Mode
|
||
- **Per-Command Startup**: ~500ms (process + imports)
|
||
- **Command Execution**: <100ms (direct function calls)
|
||
- **Database Queries**: 50-100ms (config file + state recovery)
|
||
|
||
**REPL is significantly faster for iterative testing!**
|
||
|
||
## Configuration File
|
||
|
||
Location: `~/.terminal_client_config.json`
|
||
|
||
```json
|
||
{
|
||
"current_game_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||
}
|
||
```
|
||
|
||
Automatically managed by:
|
||
- `new_game` command (sets current)
|
||
- `use_game` command (changes current)
|
||
- `config --clear` command (clears current)
|
||
|
||
## Related Documentation
|
||
|
||
- **Game Engine**: `../app/core/game_engine.py`
|
||
- **State Manager**: `../app/core/state_manager.py`
|
||
- **Game Models**: `../app/models/game_models.py`
|
||
- **Backend Guide**: `../CLAUDE.md`
|
||
|
||
## Recent Updates
|
||
|
||
### 2025-10-28: Enhanced Status Display
|
||
|
||
**Improvement**: Added user-friendly pending action guidance to status command.
|
||
|
||
**Changes**:
|
||
- Status display now shows prominent "⚠️ WAITING FOR ACTION" section when play is pending
|
||
- Provides specific guidance based on game state:
|
||
- "The defense needs to submit their decision" → Run `defensive [OPTIONS]`
|
||
- "The offense needs to submit their decision" → Run `offensive [OPTIONS]`
|
||
- "Ready to resolve play - both teams have decided" → Run `resolve`
|
||
- Command hints color-coded (yellow warnings + cyan command text + green command names)
|
||
- Makes testing workflow clearer by showing exactly what to do next
|
||
|
||
**Location**: `terminal_client/display.py:75-97`
|
||
|
||
**Usage Example**:
|
||
```bash
|
||
⚾ > status
|
||
|
||
╭─────────────────────────── Game State ───────────────────────────╮
|
||
│ ... │
|
||
│ │
|
||
│ ⚠️ WAITING FOR ACTION │
|
||
│ ──────────────────────────────────────── │
|
||
│ The defense needs to submit their decision. │
|
||
│ Run: defensive [OPTIONS] │
|
||
│ │
|
||
╰───────────────────────────────────────────────────────────────────╯
|
||
```
|
||
|
||
---
|
||
|
||
### 2025-11-04: X-Check Testing & GameState Refactoring
|
||
|
||
**Major Update**: Added complete X-Check defensive play testing capabilities and fixed GameState structure.
|
||
|
||
**New Features**:
|
||
|
||
1. **X-Check Resolution Testing** - `resolve_with x-check <position>`
|
||
- Test complete X-Check defensive plays with actual player ratings
|
||
- Includes defense range tables, error charts, SPD tests, G2#/G3# conversions
|
||
- Shows full XCheckResult audit trail with all resolution steps
|
||
|
||
```bash
|
||
⚾ > defensive
|
||
⚾ > offensive
|
||
⚾ > resolve_with x-check SS # Test X-Check to shortstop
|
||
⚾ > resolve_with x-check LF # Test X-Check to left field
|
||
```
|
||
|
||
**What It Shows**:
|
||
- Dice rolls (1d20 range + 3d6 error)
|
||
- Defender ratings (range/error from PD API or defaults)
|
||
- Defense table lookup (G1, G2, F3, SI2, DO3, etc.)
|
||
- SPD test results (if applicable)
|
||
- G2#/G3# conversion (playing in or holding runners)
|
||
- Error chart lookup (NO, E1, E2, E3, RP)
|
||
- Final outcome with error modifiers
|
||
- Complete runner advancement
|
||
|
||
2. **X-Check Help Documentation** (8 commands documented)
|
||
- `roll_jump` / `test_jump` - Stolen base jump dice testing
|
||
- `roll_fielding` / `test_fielding` - Fielding dice testing
|
||
- `test_location` - Hit location distribution testing
|
||
- `rollback` - Undo plays
|
||
- `force_wild_pitch` / `force_passed_ball` - Interrupt play testing
|
||
- All commands now appear in `help` output
|
||
|
||
**Bug Fixes**:
|
||
|
||
1. **GameState Structure** - Updated for LineupPlayerState refactoring
|
||
- `display.py`: Now accesses `state.current_batter.lineup_id` instead of `state.current_batter_lineup_id`
|
||
- `repl.py`: Fixed typos (self.current_game → self.current_game_id)
|
||
- All 105 terminal client tests passing
|
||
|
||
2. **Game Recovery** - Fixed validation error when loading saved games
|
||
- State manager now creates placeholder `current_batter` during recovery
|
||
- Placeholder is corrected by `_prepare_next_play()` after recovery
|
||
|
||
3. **DO3 Advancement** - Fixed incorrect batter advancement
|
||
- DO3 now correctly: Batter reaches 2B (it's a double), runners advance 3 bases
|
||
- Was incorrectly sending batter to 3B
|
||
|
||
**Testing Workflow**:
|
||
```bash
|
||
# Complete X-Check testing workflow
|
||
⚾ > new_game
|
||
⚾ > defensive --infield in # Set defensive positioning
|
||
⚾ > offensive --approach power # Set offensive approach
|
||
⚾ > resolve_with x-check 2B # Test X-Check to second base
|
||
|
||
# See complete resolution:
|
||
# - Defense table: d20=15 + range=4 → G2
|
||
# - Error chart: 3d6=8 vs error=12 → NO
|
||
# - Final: G2 + NO = groundout to 2B
|
||
# - Runner advancement based on G2 tables
|
||
```
|
||
|
||
**Files Updated**:
|
||
- `terminal_client/repl.py` - Added x-check parsing to resolve_with
|
||
- `terminal_client/commands.py` - Added xcheck_position parameter
|
||
- `terminal_client/help_text.py` - Documented 8 X-Check commands
|
||
- `terminal_client/display.py` - Fixed GameState references
|
||
- `app/core/game_engine.py` - Added xcheck_position support
|
||
- `app/core/play_resolver.py` - Fixed DO3 batter advancement
|
||
- `app/core/runner_advancement.py` - Updated all 17 GameState references
|
||
|
||
**Test Coverage**:
|
||
- ✅ All 34 runner_advancement tests passing
|
||
- ✅ All 105 terminal_client tests passing
|
||
- ✅ All 26 state_manager tests passing
|
||
|
||
---
|
||
|
||
**Created**: 2025-10-26
|
||
**Author**: Claude
|
||
**Purpose**: Testing tool for Phase 2 game engine development
|
||
**Status**: ✅ Complete - Interactive REPL with persistent state and full X-Check testing!
|
||
**Last Updated**: 2025-11-04
|