This commit includes Week 6 player models implementation and critical performance optimizations discovered during testing. ## Player Models (Week 6 - 50% Complete) **New Files:** - app/models/player_models.py (516 lines) - BasePlayer abstract class with polymorphic interface - SbaPlayer with API parsing factory method - PdPlayer with batting/pitching scouting data support - Supporting models: PdCardset, PdRarity, PdBattingCard, PdPitchingCard - tests/unit/models/test_player_models.py (692 lines) - 32 comprehensive unit tests, all passing - Tests for BasePlayer, SbaPlayer, PdPlayer, polymorphism **Architecture:** - Simplified single-layer approach vs planned two-layer - Factory methods handle API → Game transformation directly - SbaPlayer.from_api_response(data) - parses SBA API inline - PdPlayer.from_api_response(player_data, batting_data, pitching_data) - Full Pydantic validation, type safety, and polymorphism ## Performance Optimizations **Database Query Reduction (60% fewer queries per play):** - Before: 5 queries per play (INSERT play, SELECT play with JOINs, SELECT games, 2x SELECT lineups) - After: 2 queries per play (INSERT play, UPDATE games conditionally) Changes: 1. Lineup caching (game_engine.py:384-425) - Check state_manager.get_lineup() cache before DB fetch - Eliminates 2 SELECT queries per play 2. Remove unnecessary refresh (operations.py:281-302) - Removed session.refresh(play) after INSERT - Eliminates 1 SELECT with 3 expensive LEFT JOINs 3. Direct UPDATE statement (operations.py:109-165) - Changed update_game_state() to use direct UPDATE - No longer does SELECT + modify + commit 4. Conditional game state updates (game_engine.py:200-217) - Only UPDATE games table when score/inning/status changes - Captures state before/after and compares - ~40-60% fewer updates (many plays don't score) ## Bug Fixes 1. Fixed outs_before tracking (game_engine.py:551) - Was incorrectly calculating: state.outs - result.outs_recorded - Now correctly captures: state.outs (before applying result) - All play records now have accurate out counts 2. Fixed game recovery (state_manager.py:312-314) - AttributeError when recovering: 'GameState' has no attribute 'runners' - Changed to use state.get_all_runners() method - Games can now be properly recovered from database ## Enhanced Terminal Client **Status Display Improvements (terminal_client/display.py:75-97):** - Added "⚠️ WAITING FOR ACTION" section when play is pending - Shows specific guidance: - "The defense needs to submit their decision" → Run defensive [OPTIONS] - "The offense needs to submit their decision" → Run offensive [OPTIONS] - "Ready to resolve play" → Run resolve - Color-coded command hints for better UX ## Documentation Updates **backend/CLAUDE.md:** - Added comprehensive Player Models section (204 lines) - Updated Current Phase status to Week 6 (~50% complete) - Documented all optimizations and bug fixes - Added integration examples and usage patterns **New Files:** - .claude/implementation/week6-status-assessment.md - Comprehensive Week 6 progress review - Architecture decision rationale (single-layer vs two-layer) - Completion status and next priorities - Updated roadmap for remaining Week 6 work ## Test Results - Player models: 32/32 tests passing - All existing tests continue to pass - Performance improvements verified with terminal client ## Next Steps (Week 6 Remaining) 1. Configuration system (BaseConfig, SbaConfig, PdConfig) 2. Result charts & PD play resolution with ratings 3. API client for live roster data (deferred) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
762 lines
19 KiB
Markdown
762 lines
19 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
|
||
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
|
||
python -m terminal_client new-game
|
||
python -m terminal_client defensive --alignment normal
|
||
python -m terminal_client offensive --approach power
|
||
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
|
||
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:
|
||
python -m terminal_client start-game --league sba
|
||
python -m terminal_client start-game --game-id <uuid> --home-team 5 --away-team 3
|
||
```
|
||
|
||
### Defensive Decision
|
||
```bash
|
||
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:
|
||
python -m terminal_client defensive --alignment shifted_left
|
||
python -m terminal_client defensive --infield double_play --hold 1,3
|
||
```
|
||
|
||
### Offensive Decision
|
||
```bash
|
||
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:
|
||
python -m terminal_client offensive --approach power
|
||
python -m terminal_client offensive --steal 2 --hit-run
|
||
```
|
||
|
||
### Resolve Play
|
||
```bash
|
||
python -m terminal_client resolve [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
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
|
||
python -m terminal_client status [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
python -m terminal_client status
|
||
```
|
||
|
||
### Box Score
|
||
```bash
|
||
python -m terminal_client box-score [OPTIONS]
|
||
|
||
Options:
|
||
--game-id TEXT Game UUID (uses current if not provided)
|
||
|
||
Example:
|
||
python -m terminal_client box-score
|
||
```
|
||
|
||
### Quick Play
|
||
```bash
|
||
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:
|
||
python -m terminal_client quick-play --count 10
|
||
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
|
||
python -m terminal_client list-games
|
||
|
||
Example:
|
||
python -m terminal_client list-games
|
||
```
|
||
|
||
Shows all active games in the state manager.
|
||
|
||
### Use Game
|
||
```bash
|
||
python -m terminal_client use-game <game_id>
|
||
|
||
Example:
|
||
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
|
||
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
|
||
python -m terminal_client start-game
|
||
python -m terminal_client defensive --alignment shifted_left --infield double_play
|
||
python -m terminal_client offensive
|
||
python -m terminal_client resolve
|
||
|
||
# Test stealing
|
||
python -m terminal_client start-game
|
||
python -m terminal_client defensive
|
||
python -m terminal_client offensive --steal 2,3 # Double steal
|
||
python -m terminal_client resolve
|
||
|
||
# Test hit-and-run
|
||
python -m terminal_client defensive
|
||
python -m terminal_client offensive --hit-run
|
||
python -m terminal_client resolve
|
||
|
||
# Advance to late game quickly
|
||
python -m terminal_client start-game
|
||
python -m terminal_client quick-play --count 50 # ~6 innings
|
||
python -m terminal_client status
|
||
```
|
||
|
||
### Multiple Games
|
||
|
||
```bash
|
||
# Start game 1
|
||
python -m terminal_client start-game
|
||
# ... play some ...
|
||
|
||
# Start game 2
|
||
python -m terminal_client start-game
|
||
# ... play some ...
|
||
|
||
# List all games
|
||
python -m terminal_client list-games
|
||
|
||
# Switch back to game 1
|
||
python -m terminal_client use-game <game-1-uuid>
|
||
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
|
||
python -m terminal_client start-game
|
||
# Should fail: No lineups set
|
||
|
||
# Test invalid decisions
|
||
python -m terminal_client defensive --alignment invalid_value
|
||
# Should fail: ValidationError
|
||
|
||
# Test resolve without decisions
|
||
python -m terminal_client resolve
|
||
# Should fail: No decisions submitted
|
||
```
|
||
|
||
### Integration Test Scenarios
|
||
|
||
```bash
|
||
# Full game flow
|
||
python -m terminal_client start-game
|
||
# ... set lineups via database directly ...
|
||
python -m terminal_client start-game # Retry
|
||
python -m terminal_client quick-play --count 100
|
||
python -m terminal_client status
|
||
# Verify: Game status = "completed"
|
||
|
||
# State persistence
|
||
python -m terminal_client start-game
|
||
# Note the game UUID
|
||
python -m terminal_client quick-play --count 10
|
||
# Kill terminal
|
||
# Restart and use same UUID
|
||
python -m terminal_client use-game <uuid>
|
||
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
|
||
python -m terminal_client start-game # ✅
|
||
```
|
||
|
||
### Import Errors
|
||
```bash
|
||
# Ensure you're in backend directory
|
||
cd backend
|
||
source venv/bin/activate
|
||
python -m terminal_client start-game
|
||
```
|
||
|
||
### Game Not Found
|
||
```bash
|
||
# Check active games
|
||
python -m terminal_client list-games
|
||
|
||
# Use specific game ID
|
||
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] │
|
||
│ │
|
||
╰───────────────────────────────────────────────────────────────────╯
|
||
```
|
||
|
||
---
|
||
|
||
**Created**: 2025-10-26
|
||
**Author**: Claude
|
||
**Purpose**: Testing tool for Phase 2 game engine development
|
||
**Status**: ✅ Complete - Interactive REPL with persistent state working perfectly!
|
||
**Last Updated**: 2025-10-28
|