Added ability to test X-Check defensive plays directly in the terminal client using the resolve_with command. This allows testing the complete X-Check resolution system (defense tables, error charts, runner advancement) with actual player ratings. Changes: - repl.py: Updated do_resolve_with() to parse "x-check <position>" syntax - Accepts "x-check", "xcheck", or "x_check" followed by position - Validates position (P, C, 1B, 2B, 3B, SS, LF, CF, RF) - Passes xcheck_position parameter through to commands - commands.py: Updated resolve_play() to accept xcheck_position parameter - Passes xcheck_position to game_engine.resolve_play() - Shows "🎯 Forcing X-Check to: <position>" message - game_engine.py: Updated resolve_play() to accept xcheck_position parameter - For X_CHECK outcomes, uses xcheck_position as hit_location - Enables full X-Check resolution with defense tables and error charts - help_text.py: Updated resolve_with help documentation - Added x-check usage syntax and examples - Documented position parameter requirement - Added note about using actual player ratings Usage: ⚾ > defensive ⚾ > offensive ⚾ > resolve_with x-check SS # Test X-Check to shortstop ⚾ > resolve_with x-check LF # Test X-Check to left field This integrates the existing play_resolver._resolve_x_check() logic, providing a way to test the complete X-Check system including defense range adjustments, table lookups, SPD tests, G2#/G3# conversion, error charts, and runner advancement. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
564 lines
18 KiB
Python
564 lines
18 KiB
Python
"""
|
|
Help text and documentation for terminal client commands.
|
|
|
|
Provides detailed, formatted help text for all REPL commands
|
|
with usage examples and option descriptions.
|
|
|
|
Author: Claude
|
|
Date: 2025-10-27
|
|
"""
|
|
from typing import Dict
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
from rich.panel import Panel
|
|
from rich.markdown import Markdown
|
|
from rich import box
|
|
|
|
console = Console()
|
|
|
|
|
|
class HelpFormatter:
|
|
"""Format and display help text for commands."""
|
|
|
|
@staticmethod
|
|
def show_command_help(command_name: str, help_data: Dict) -> None:
|
|
"""
|
|
Display detailed help for a specific command.
|
|
|
|
Args:
|
|
command_name: Name of the command
|
|
help_data: Dictionary with help information
|
|
{
|
|
'summary': 'Brief description',
|
|
'usage': 'command [OPTIONS]',
|
|
'options': [
|
|
{'name': '--option', 'type': 'TYPE', 'desc': 'Description'}
|
|
],
|
|
'examples': ['example 1', 'example 2']
|
|
}
|
|
"""
|
|
# Build help text
|
|
help_text = []
|
|
|
|
# Summary
|
|
help_text.append(f"**{command_name}** - {help_data.get('summary', 'No description')}")
|
|
help_text.append("")
|
|
|
|
# Usage
|
|
if 'usage' in help_data:
|
|
help_text.append("**USAGE:**")
|
|
help_text.append(f" {help_data['usage']}")
|
|
help_text.append("")
|
|
|
|
# Display in panel
|
|
if help_text:
|
|
md = Markdown("\n".join(help_text[:3])) # Just summary and usage
|
|
panel = Panel(
|
|
md,
|
|
title=f"[bold cyan]Help: {command_name}[/bold cyan]",
|
|
border_style="cyan",
|
|
box=box.ROUNDED
|
|
)
|
|
console.print(panel)
|
|
console.print()
|
|
|
|
# Options
|
|
if 'options' in help_data and help_data['options']:
|
|
console.print("[bold cyan]OPTIONS:[/bold cyan]")
|
|
|
|
# Create options table
|
|
table = Table(box=box.SIMPLE, show_header=False, padding=(0, 2))
|
|
table.add_column("Option", style="cyan", no_wrap=True)
|
|
table.add_column("Type", style="yellow")
|
|
table.add_column("Description", style="white")
|
|
|
|
for opt in help_data['options']:
|
|
table.add_row(
|
|
opt['name'],
|
|
opt.get('type', ''),
|
|
opt.get('desc', '')
|
|
)
|
|
|
|
console.print(table)
|
|
console.print()
|
|
|
|
# Examples
|
|
if 'examples' in help_data and help_data['examples']:
|
|
console.print("[bold cyan]EXAMPLES:[/bold cyan]")
|
|
for example in help_data['examples']:
|
|
console.print(f" {example}")
|
|
console.print()
|
|
|
|
# Notes
|
|
if 'notes' in help_data:
|
|
console.print(f"[dim]{help_data['notes']}[/dim]")
|
|
console.print()
|
|
|
|
@staticmethod
|
|
def show_command_list() -> None:
|
|
"""Display list of all available commands."""
|
|
console.print("\n[bold cyan]Available Commands:[/bold cyan]\n")
|
|
|
|
# Game Management
|
|
console.print("[bold yellow]Game Management:[/bold yellow]")
|
|
console.print(" new_game Create a new game with test lineups and start it")
|
|
console.print(" list_games List all games in state manager")
|
|
console.print(" use_game Switch to a different game")
|
|
console.print(" status Display current game state")
|
|
console.print(" box_score Display box score")
|
|
console.print()
|
|
|
|
# Gameplay
|
|
console.print("[bold yellow]Gameplay:[/bold yellow]")
|
|
console.print(" defensive Submit defensive decision")
|
|
console.print(" offensive Submit offensive decision")
|
|
console.print(" resolve Resolve the current play")
|
|
console.print(" resolve_with Resolve with a specific outcome (testing)")
|
|
console.print(" quick_play Auto-play multiple plays")
|
|
console.print()
|
|
|
|
# Testing
|
|
console.print("[bold yellow]Testing & Development:[/bold yellow]")
|
|
console.print(" list_outcomes Show all available PlayOutcome values")
|
|
console.print(" test_location Test hit location distribution")
|
|
console.print(" rollback Roll back the last N plays")
|
|
console.print()
|
|
|
|
# X-Check Testing (Dice Systems)
|
|
console.print("[bold yellow]X-Check Testing:[/bold yellow]")
|
|
console.print(" roll_jump Roll jump dice for stolen base testing")
|
|
console.print(" test_jump Test jump roll distribution")
|
|
console.print(" roll_fielding Roll fielding check dice")
|
|
console.print(" test_fielding Test fielding roll distribution")
|
|
console.print()
|
|
|
|
# Interrupt Plays
|
|
console.print("[bold yellow]Interrupt Plays:[/bold yellow]")
|
|
console.print(" force_wild_pitch Force a wild pitch interrupt play")
|
|
console.print(" force_passed_ball Force a passed ball interrupt play")
|
|
console.print()
|
|
|
|
# Utilities
|
|
console.print("[bold yellow]Utilities:[/bold yellow]")
|
|
console.print(" config Show configuration")
|
|
console.print(" clear Clear the screen")
|
|
console.print(" help Show help for commands")
|
|
console.print(" quit/exit Exit the REPL")
|
|
console.print()
|
|
|
|
console.print("[dim]Type 'help <command>' for detailed information.[/dim]")
|
|
console.print("[dim]Use TAB for auto-completion of commands and options.[/dim]\n")
|
|
|
|
|
|
# Detailed help data for each command
|
|
HELP_DATA = {
|
|
'new_game': {
|
|
'summary': 'Create a new game with test lineups and start it immediately',
|
|
'usage': 'new_game [--league LEAGUE] [--home-team ID] [--away-team ID]',
|
|
'options': [
|
|
{
|
|
'name': '--league',
|
|
'type': 'sba|pd',
|
|
'desc': 'League type (default: sba)'
|
|
},
|
|
{
|
|
'name': '--home-team',
|
|
'type': 'INT',
|
|
'desc': 'Home team ID (default: 1)'
|
|
},
|
|
{
|
|
'name': '--away-team',
|
|
'type': 'INT',
|
|
'desc': 'Away team ID (default: 2)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'new_game',
|
|
'new_game --league pd',
|
|
'new_game --league sba --home-team 5 --away-team 3'
|
|
]
|
|
},
|
|
|
|
'defensive': {
|
|
'summary': 'Submit defensive decision for the current play',
|
|
'usage': 'defensive [--alignment TYPE] [--infield DEPTH] [--outfield DEPTH] [--hold BASES]',
|
|
'options': [
|
|
{
|
|
'name': '--alignment',
|
|
'type': 'STRING',
|
|
'desc': 'Defensive alignment: normal, shifted_left, shifted_right, extreme_shift (default: normal)'
|
|
},
|
|
{
|
|
'name': '--infield',
|
|
'type': 'STRING',
|
|
'desc': 'Infield depth: in, normal, back, double_play (default: normal)'
|
|
},
|
|
{
|
|
'name': '--outfield',
|
|
'type': 'STRING',
|
|
'desc': 'Outfield depth: in, normal, back (default: normal)'
|
|
},
|
|
{
|
|
'name': '--hold',
|
|
'type': 'LIST',
|
|
'desc': 'Comma-separated bases to hold runners: 1,2,3 (default: none)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'defensive',
|
|
'defensive --alignment shifted_left',
|
|
'defensive --infield double_play --hold 1,3',
|
|
'defensive --alignment extreme_shift --infield back --outfield back'
|
|
]
|
|
},
|
|
|
|
'offensive': {
|
|
'summary': 'Submit offensive decision for the current play',
|
|
'usage': 'offensive [--approach TYPE] [--steal BASES] [--hit-run] [--bunt]',
|
|
'options': [
|
|
{
|
|
'name': '--approach',
|
|
'type': 'STRING',
|
|
'desc': 'Batting approach: normal, contact, power, patient (default: normal)'
|
|
},
|
|
{
|
|
'name': '--steal',
|
|
'type': 'LIST',
|
|
'desc': 'Comma-separated bases to steal: 2,3 (default: none)'
|
|
},
|
|
{
|
|
'name': '--hit-run',
|
|
'type': 'FLAG',
|
|
'desc': 'Execute hit-and-run play (default: false)'
|
|
},
|
|
{
|
|
'name': '--bunt',
|
|
'type': 'FLAG',
|
|
'desc': 'Attempt bunt (default: false)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'offensive',
|
|
'offensive --approach power',
|
|
'offensive --steal 2',
|
|
'offensive --steal 2,3 --hit-run',
|
|
'offensive --approach contact --bunt'
|
|
]
|
|
},
|
|
|
|
'resolve': {
|
|
'summary': 'Resolve the current play using submitted decisions',
|
|
'usage': 'resolve',
|
|
'options': [],
|
|
'examples': [
|
|
'resolve'
|
|
],
|
|
'notes': 'Both defensive and offensive decisions must be submitted before resolving.'
|
|
},
|
|
|
|
'list_outcomes': {
|
|
'summary': 'Display all available PlayOutcome values for manual outcome testing',
|
|
'usage': 'list_outcomes',
|
|
'options': [],
|
|
'examples': [
|
|
'list_outcomes'
|
|
],
|
|
'notes': 'Shows a categorized table of all play outcomes. Use these values with resolve_with command.'
|
|
},
|
|
|
|
'resolve_with': {
|
|
'summary': 'Resolve current play with a specific outcome (bypassing dice rolls)',
|
|
'usage': 'resolve_with <OUTCOME>\n resolve_with x-check <POSITION>',
|
|
'options': [
|
|
{
|
|
'name': 'OUTCOME',
|
|
'type': 'STRING',
|
|
'desc': 'PlayOutcome enum value. Use list_outcomes to see all available values.'
|
|
},
|
|
{
|
|
'name': 'POSITION',
|
|
'type': 'P|C|1B|2B|3B|SS|LF|CF|RF',
|
|
'desc': 'For x-check: defensive position to test (required)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'resolve_with single_1',
|
|
'resolve_with homerun',
|
|
'resolve_with groundball_a',
|
|
'resolve_with double_uncapped',
|
|
'resolve_with strikeout',
|
|
'resolve_with x-check SS # Test X-Check to shortstop',
|
|
'resolve_with x-check LF # Test X-Check to left field'
|
|
],
|
|
'notes': 'Experimental feature for testing specific scenarios without random dice rolls. X-Check mode uses full defense tables and error charts with actual player ratings.'
|
|
},
|
|
|
|
'quick_play': {
|
|
'summary': 'Auto-play multiple plays with default decisions',
|
|
'usage': 'quick_play [COUNT]',
|
|
'options': [
|
|
{
|
|
'name': 'COUNT',
|
|
'type': 'INT',
|
|
'desc': 'Number of plays to execute (default: 1). Positional argument.'
|
|
}
|
|
],
|
|
'examples': [
|
|
'quick_play',
|
|
'quick_play 10',
|
|
'quick_play 27 # Play roughly 3 innings',
|
|
'quick_play 100 # Play full game quickly'
|
|
]
|
|
},
|
|
|
|
'status': {
|
|
'summary': 'Display current game state',
|
|
'usage': 'status',
|
|
'options': [],
|
|
'examples': [
|
|
'status'
|
|
]
|
|
},
|
|
|
|
'box_score': {
|
|
'summary': 'Display box score for the current game',
|
|
'usage': 'box_score',
|
|
'options': [],
|
|
'examples': [
|
|
'box_score'
|
|
]
|
|
},
|
|
|
|
'list_games': {
|
|
'summary': 'List all games currently loaded in state manager',
|
|
'usage': 'list_games',
|
|
'options': [],
|
|
'examples': [
|
|
'list_games'
|
|
]
|
|
},
|
|
|
|
'use_game': {
|
|
'summary': 'Switch to a different game',
|
|
'usage': 'use_game <GAME_ID>',
|
|
'options': [
|
|
{
|
|
'name': 'GAME_ID',
|
|
'type': 'UUID',
|
|
'desc': 'UUID of the game to switch to. Positional argument.'
|
|
}
|
|
],
|
|
'examples': [
|
|
'use_game a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
'use_game <TAB> # Use tab completion to see available games'
|
|
]
|
|
},
|
|
|
|
'config': {
|
|
'summary': 'Show terminal client configuration',
|
|
'usage': 'config',
|
|
'options': [],
|
|
'examples': [
|
|
'config'
|
|
]
|
|
},
|
|
|
|
'clear': {
|
|
'summary': 'Clear the screen',
|
|
'usage': 'clear',
|
|
'options': [],
|
|
'examples': [
|
|
'clear'
|
|
]
|
|
},
|
|
|
|
# X-Check Testing Commands
|
|
'roll_jump': {
|
|
'summary': 'Roll jump dice for stolen base testing',
|
|
'usage': 'roll_jump [LEAGUE]',
|
|
'options': [
|
|
{
|
|
'name': 'LEAGUE',
|
|
'type': 'sba|pd',
|
|
'desc': 'League type (default: sba)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'roll_jump # Roll for SBA league',
|
|
'roll_jump pd # Roll for PD league'
|
|
],
|
|
'notes': 'Components: 1d20 check (1=pickoff, 2=balk, 3+=normal), 2d6 jump total'
|
|
},
|
|
|
|
'test_jump': {
|
|
'summary': 'Test jump roll distribution statistics',
|
|
'usage': 'test_jump [COUNT] [LEAGUE]',
|
|
'options': [
|
|
{
|
|
'name': 'COUNT',
|
|
'type': 'integer',
|
|
'desc': 'Number of rolls to test (default: 10)'
|
|
},
|
|
{
|
|
'name': 'LEAGUE',
|
|
'type': 'sba|pd',
|
|
'desc': 'League type (default: sba)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'test_jump # 10 rolls for SBA',
|
|
'test_jump 100 # 100 rolls for SBA',
|
|
'test_jump 50 pd # 50 rolls for PD'
|
|
],
|
|
'notes': 'Shows pickoff/balk/normal frequency and 2d6 distribution'
|
|
},
|
|
|
|
'roll_fielding': {
|
|
'summary': 'Roll fielding check dice for X-Check defensive plays',
|
|
'usage': 'roll_fielding <POSITION> [LEAGUE]',
|
|
'options': [
|
|
{
|
|
'name': 'POSITION',
|
|
'type': 'P|C|1B|2B|3B|SS|LF|CF|RF',
|
|
'desc': 'Fielding position (required)'
|
|
},
|
|
{
|
|
'name': 'LEAGUE',
|
|
'type': 'sba|pd',
|
|
'desc': 'League type (default: sba)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'roll_fielding SS # Roll for shortstop (SBA)',
|
|
'roll_fielding P pd # Roll for pitcher (PD)',
|
|
'roll_fielding CF # Roll for center field'
|
|
],
|
|
'notes': 'Components: 1d20 range, 3d6 error total, d100/special rare play check'
|
|
},
|
|
|
|
'test_fielding': {
|
|
'summary': 'Test fielding roll distribution for a position',
|
|
'usage': 'test_fielding <POSITION> [COUNT] [LEAGUE]',
|
|
'options': [
|
|
{
|
|
'name': 'POSITION',
|
|
'type': 'P|C|1B|2B|3B|SS|LF|CF|RF',
|
|
'desc': 'Fielding position (required)'
|
|
},
|
|
{
|
|
'name': 'COUNT',
|
|
'type': 'integer',
|
|
'desc': 'Number of rolls (default: 10)'
|
|
},
|
|
{
|
|
'name': 'LEAGUE',
|
|
'type': 'sba|pd',
|
|
'desc': 'League type (default: sba)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'test_fielding SS # 10 rolls for shortstop',
|
|
'test_fielding 2B 100 # 100 rolls for second base',
|
|
'test_fielding CF 50 pd # 50 rolls for center field (PD)'
|
|
],
|
|
'notes': 'Shows rare play frequency, average range/error, and distribution stats'
|
|
},
|
|
|
|
'test_location': {
|
|
'summary': 'Test hit location distribution for an outcome',
|
|
'usage': 'test_location <OUTCOME> [HANDEDNESS] [COUNT]',
|
|
'options': [
|
|
{
|
|
'name': 'OUTCOME',
|
|
'type': 'PlayOutcome',
|
|
'desc': 'PlayOutcome enum value (e.g., groundball_c)'
|
|
},
|
|
{
|
|
'name': 'HANDEDNESS',
|
|
'type': 'L|R',
|
|
'desc': 'Batter handedness (default: R)'
|
|
},
|
|
{
|
|
'name': 'COUNT',
|
|
'type': 'integer',
|
|
'desc': 'Number of samples (default: 100)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'test_location groundball_c',
|
|
'test_location groundball_c L',
|
|
'test_location flyout_b R 200'
|
|
],
|
|
'notes': 'Shows distribution of hit locations based on pull rates'
|
|
},
|
|
|
|
'rollback': {
|
|
'summary': 'Roll back the last N plays',
|
|
'usage': 'rollback <NUM_PLAYS>',
|
|
'options': [
|
|
{
|
|
'name': 'NUM_PLAYS',
|
|
'type': 'integer',
|
|
'desc': 'Number of plays to roll back (must be > 0)'
|
|
}
|
|
],
|
|
'examples': [
|
|
'rollback 1 # Undo the last play',
|
|
'rollback 3 # Undo the last 3 plays',
|
|
'rollback 5 # Undo the last 5 plays'
|
|
],
|
|
'notes': 'Deletes plays from database and reconstructs state by replaying'
|
|
},
|
|
|
|
'force_wild_pitch': {
|
|
'summary': 'Force a wild pitch interrupt play',
|
|
'usage': 'force_wild_pitch',
|
|
'options': [],
|
|
'examples': [
|
|
'force_wild_pitch # Runner on 2nd advances to 3rd'
|
|
],
|
|
'notes': 'Advances all runners one base. This is an interrupt play (pa=0)'
|
|
},
|
|
|
|
'force_passed_ball': {
|
|
'summary': 'Force a passed ball interrupt play',
|
|
'usage': 'force_passed_ball',
|
|
'options': [],
|
|
'examples': [
|
|
'force_passed_ball # Runner on 1st advances to 2nd'
|
|
],
|
|
'notes': 'Advances all runners one base. This is an interrupt play (pa=0)'
|
|
}
|
|
}
|
|
|
|
|
|
def get_help_text(command: str) -> Dict:
|
|
"""
|
|
Get help data for a command.
|
|
|
|
Args:
|
|
command: Command name
|
|
|
|
Returns:
|
|
Help data dictionary or empty dict if not found
|
|
"""
|
|
return HELP_DATA.get(command, {})
|
|
|
|
|
|
def show_help(command: str = None) -> None:
|
|
"""
|
|
Show help for a command or list all commands.
|
|
|
|
Args:
|
|
command: Command name or None for command list
|
|
"""
|
|
if command:
|
|
help_data = get_help_text(command)
|
|
if help_data:
|
|
HelpFormatter.show_command_help(command, help_data)
|
|
else:
|
|
console.print(f"[yellow]No help available for '{command}'[/yellow]")
|
|
console.print("[dim]Type 'help' to see all available commands.[/dim]")
|
|
else:
|
|
HelpFormatter.show_command_list()
|