275 lines
7.4 KiB
Markdown
275 lines
7.4 KiB
Markdown
# Manual Outcome Testing Feature
|
||
|
||
**Date**: 2025-10-29
|
||
**Status**: Implemented (Experimental)
|
||
**Purpose**: Allow manual specification of play outcomes for testing specific scenarios
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
Added two new terminal client commands to support testing specific game scenarios without random dice rolls:
|
||
|
||
1. **`list_outcomes`** - Display all available PlayOutcome values
|
||
2. **`resolve_with <outcome>`** - Resolve play with a specific outcome
|
||
|
||
## Commands
|
||
|
||
### list_outcomes
|
||
|
||
Displays a beautiful categorized table of all available PlayOutcome enum values.
|
||
|
||
**Usage:**
|
||
```bash
|
||
⚾ > list_outcomes
|
||
```
|
||
|
||
**Output:**
|
||
- Categorized table with:
|
||
- **Outs**: strikeout, groundball variants, flyout variants, lineouts, popouts
|
||
- **Hits**: single variants, double variants, triple, homerun
|
||
- **Walks/HBP**: walk, hbp, intentional_walk
|
||
- **Errors**: error
|
||
- **Interrupts**: wild_pitch, passed_ball, stolen_base, caught_stealing, balk, pick_off
|
||
- **Ballpark**: bp_homerun, bp_single, bp_flyout, bp_lineout
|
||
|
||
### resolve_with <outcome>
|
||
|
||
Resolves the current play with a specific outcome instead of rolling dice.
|
||
|
||
**Usage:**
|
||
```bash
|
||
⚾ > resolve_with single_1
|
||
⚾ > resolve_with homerun
|
||
⚾ > resolve_with groundball_a
|
||
⚾ > resolve_with double_uncapped
|
||
```
|
||
|
||
**Features:**
|
||
- Validates outcome string against PlayOutcome enum
|
||
- Provides clear error messages for invalid outcomes
|
||
- TAB completion for all outcome names
|
||
- Detailed help text with examples
|
||
|
||
**Current Status:**
|
||
⚠️ **Experimental** - Currently shows warning and uses regular dice resolution. Full forced outcome integration will be added in Week 7 as part of the strategic decision system implementation.
|
||
|
||
The command infrastructure is in place and ready for integration when the game engine supports forced outcomes.
|
||
|
||
## Testing Use Cases
|
||
|
||
These commands will be useful for testing:
|
||
|
||
1. **Runner Advancement Rules**
|
||
```bash
|
||
# Set up bases loaded
|
||
# Then force specific outcomes to test advancement
|
||
resolve_with single_1 # Test standard single advancement
|
||
resolve_with single_2 # Test enhanced advancement
|
||
resolve_with double_2 # Runners to 2nd and home
|
||
resolve_with double_3 # Runners to 3rd and home
|
||
```
|
||
|
||
2. **Double Play Mechanics**
|
||
```bash
|
||
# Set up runner on first, <2 outs
|
||
resolve_with groundball_a # Should check for DP opportunity
|
||
resolve_with groundball_b # Standard groundout
|
||
```
|
||
|
||
3. **Uncapped Hit Decision Trees**
|
||
```bash
|
||
# Test uncapped outcomes
|
||
resolve_with single_uncapped
|
||
resolve_with double_uncapped
|
||
# Should trigger advancement decision workflows
|
||
```
|
||
|
||
4. **Scoring Scenarios**
|
||
```bash
|
||
# Runner on third, test different outcomes
|
||
resolve_with groundball_b # Does runner score?
|
||
resolve_with flyout_b # Tag up from third?
|
||
resolve_with single_1 # Should score easily
|
||
```
|
||
|
||
5. **Game State Edge Cases**
|
||
```bash
|
||
# Test bases loaded, 2 outs
|
||
resolve_with strikeout # Inning over
|
||
resolve_with homerun # Grand slam
|
||
resolve_with walk # Force in run
|
||
```
|
||
|
||
## Implementation Details
|
||
|
||
### Files Modified
|
||
|
||
1. **`terminal_client/commands.py`**
|
||
- Added `PlayOutcome` import
|
||
- Enhanced `resolve_play()` with `forced_outcome` parameter
|
||
- Added `list_outcomes()` method with Rich table display
|
||
|
||
2. **`terminal_client/repl.py`**
|
||
- Added `do_list_outcomes()` command
|
||
- Added `do_resolve_with()` command with outcome parsing
|
||
|
||
3. **`terminal_client/completions.py`**
|
||
- Added `VALID_OUTCOMES` constant with all PlayOutcome values
|
||
- Added `complete_resolve_with()` method for TAB completion
|
||
|
||
4. **`terminal_client/help_text.py`**
|
||
- Added commands to "Testing & Development" section
|
||
- Added `list_outcomes` help entry
|
||
- Added `resolve_with` help entry with examples
|
||
|
||
### TAB Completion
|
||
|
||
The `resolve_with` command has full TAB completion support:
|
||
|
||
```bash
|
||
⚾ > resolve_with <TAB>
|
||
strikeout groundball_a groundball_b groundball_c
|
||
flyout_a flyout_b flyout_c lineout
|
||
single_1 single_2 single_uncapped double_2
|
||
double_3 double_uncapped triple homerun
|
||
walk hbp ...
|
||
|
||
⚾ > resolve_with single_<TAB>
|
||
single_1 single_2 single_uncapped
|
||
|
||
⚾ > resolve_with single_1
|
||
```
|
||
|
||
### Error Handling
|
||
|
||
Clear error messages for invalid input:
|
||
|
||
```bash
|
||
⚾ > resolve_with
|
||
❌ Missing outcome argument
|
||
ℹ Usage: resolve_with <outcome>
|
||
ℹ Use 'list_outcomes' to see available values
|
||
|
||
⚾ > resolve_with invalid_outcome
|
||
❌ Invalid outcome: invalid_outcome
|
||
ℹ Use 'list_outcomes' to see valid values
|
||
```
|
||
|
||
## Future Enhancements (Week 7)
|
||
|
||
When implementing Week 7 strategic decisions, integrate forced outcomes:
|
||
|
||
1. **Add `resolve_play_with_outcome()` to GameEngine**
|
||
```python
|
||
async def resolve_play_with_outcome(
|
||
self,
|
||
game_id: UUID,
|
||
forced_outcome: PlayOutcome
|
||
) -> PlayResult:
|
||
"""
|
||
Resolve play with a specific outcome (testing only).
|
||
|
||
Bypasses dice rolling and uses provided outcome directly.
|
||
All other game logic (runner advancement, scoring, etc.) still applies.
|
||
"""
|
||
# Get current state
|
||
state = state_manager.get_state(game_id)
|
||
|
||
# Create PlayResult with forced outcome
|
||
result = PlayResult(
|
||
outcome=forced_outcome,
|
||
description=f"Forced outcome: {forced_outcome.value}",
|
||
outs_recorded=1 if forced_outcome.is_out() else 0,
|
||
runs_scored=0, # Calculated by runner advancement
|
||
hit_location=self._get_default_hit_location(forced_outcome),
|
||
runner_movements=[]
|
||
)
|
||
|
||
# Apply result using normal resolution logic
|
||
await self._apply_play_result(state, result)
|
||
return result
|
||
```
|
||
|
||
2. **Update `commands.py` to use new method**
|
||
```python
|
||
if forced_outcome:
|
||
result = await game_engine.resolve_play_with_outcome(game_id, forced_outcome)
|
||
else:
|
||
result = await game_engine.resolve_play(game_id)
|
||
```
|
||
|
||
3. **Add runner advancement for forced outcomes**
|
||
- Use RunnerAdvancer class (from Week 7 plan)
|
||
- Calculate movements based on outcome type
|
||
- Apply scoring logic
|
||
|
||
## Testing
|
||
|
||
Manual testing performed:
|
||
- ✅ `list_outcomes` displays all outcomes correctly
|
||
- ✅ TAB completion works for outcome names
|
||
- ✅ Invalid outcomes show clear error messages
|
||
- ✅ Help text displays correctly
|
||
- ✅ Commands integrate with existing REPL flow
|
||
|
||
---
|
||
|
||
## Usage Examples
|
||
|
||
### Basic Workflow
|
||
|
||
```bash
|
||
# Start terminal client
|
||
python -m terminal_client
|
||
|
||
# Create a game
|
||
⚾ > new_game
|
||
|
||
# Submit decisions
|
||
⚾ > defensive
|
||
⚾ > offensive
|
||
|
||
# See all available outcomes
|
||
⚾ > list_outcomes
|
||
|
||
# Try forcing a specific outcome
|
||
⚾ > resolve_with single_1
|
||
⚠️ Manual outcome selection is experimental
|
||
Using regular resolution for now (forced outcome noted)
|
||
|
||
# [Regular resolution happens]
|
||
# This will be fully functional in Week 7
|
||
|
||
# Continue testing
|
||
⚾ > status
|
||
⚾ > resolve_with homerun
|
||
```
|
||
|
||
### Testing Runner Advancement
|
||
|
||
```bash
|
||
# Set up specific scenario
|
||
⚾ > quick_play 10 # Advance game state
|
||
|
||
# Check current state
|
||
⚾ > status
|
||
# Inning: 3 Top, Outs: 1, Runners: [1st, 3rd]
|
||
|
||
# Force single to test advancement
|
||
⚾ > defensive
|
||
⚾ > offensive
|
||
⚾ > resolve_with single_2
|
||
|
||
# Verify runner advancement logic
|
||
⚾ > status
|
||
# Runner from 3rd should score
|
||
# Runner from 1st should advance to 3rd (enhanced)
|
||
```
|
||
|
||
---
|
||
|
||
**Next Steps**: Week 7 implementation will add full forced outcome support to the game engine.
|
||
|
||
**Documentation**: See `terminal_client/CLAUDE.md` for full terminal client guide.
|