CLAUDE: Add X-Check testing to resolve_with command
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>
This commit is contained in:
parent
8fb740fe3e
commit
bb78de2b84
@ -400,7 +400,7 @@ class GameEngine:
|
|||||||
logger.warning(f"Offensive decision timeout for game {state.game_id}, using default")
|
logger.warning(f"Offensive decision timeout for game {state.game_id}, using default")
|
||||||
return OffensiveDecision() # All defaults
|
return OffensiveDecision() # All defaults
|
||||||
|
|
||||||
async def resolve_play(self, game_id: UUID, forced_outcome: Optional[PlayOutcome] = None) -> PlayResult:
|
async def resolve_play(self, game_id: UUID, forced_outcome: Optional[PlayOutcome] = None, xcheck_position: Optional[str] = None) -> PlayResult:
|
||||||
"""
|
"""
|
||||||
Resolve the current play with dice roll
|
Resolve the current play with dice roll
|
||||||
|
|
||||||
@ -415,6 +415,7 @@ class GameEngine:
|
|||||||
Args:
|
Args:
|
||||||
game_id: Game to resolve
|
game_id: Game to resolve
|
||||||
forced_outcome: If provided, use this outcome instead of rolling dice (for testing)
|
forced_outcome: If provided, use this outcome instead of rolling dice (for testing)
|
||||||
|
xcheck_position: For X_CHECK outcomes, the position to check (SS, LF, etc.)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
PlayResult with complete outcome
|
PlayResult with complete outcome
|
||||||
@ -443,9 +444,12 @@ class GameEngine:
|
|||||||
"Use resolve_manual_play() for manual mode or resolve_auto_play() for auto mode."
|
"Use resolve_manual_play() for manual mode or resolve_auto_play() for auto mode."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# For X_CHECK, use xcheck_position as the hit_location parameter
|
||||||
|
hit_location = xcheck_position if forced_outcome == PlayOutcome.X_CHECK else None
|
||||||
|
|
||||||
result = resolver.resolve_outcome(
|
result = resolver.resolve_outcome(
|
||||||
outcome=forced_outcome,
|
outcome=forced_outcome,
|
||||||
hit_location=None, # Testing doesn't specify location
|
hit_location=hit_location, # For X_CHECK, this is the position being checked
|
||||||
state=state,
|
state=state,
|
||||||
defensive_decision=defensive_decision,
|
defensive_decision=defensive_decision,
|
||||||
offensive_decision=offensive_decision,
|
offensive_decision=offensive_decision,
|
||||||
|
|||||||
@ -199,22 +199,26 @@ class GameCommands:
|
|||||||
logger.exception("Offensive decision error")
|
logger.exception("Offensive decision error")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def resolve_play(self, game_id: UUID, forced_outcome: Optional[PlayOutcome] = None) -> bool:
|
async def resolve_play(self, game_id: UUID, forced_outcome: Optional[PlayOutcome] = None, xcheck_position: Optional[str] = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Resolve the current play.
|
Resolve the current play.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
game_id: Game to resolve
|
game_id: Game to resolve
|
||||||
forced_outcome: If provided, use this outcome instead of rolling dice
|
forced_outcome: If provided, use this outcome instead of rolling dice
|
||||||
|
xcheck_position: For X_CHECK outcomes, the position to check (SS, LF, etc.)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if successful, False otherwise
|
True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if forced_outcome:
|
if forced_outcome:
|
||||||
display.print_info(f"🎯 Forcing outcome: {forced_outcome.value}")
|
if xcheck_position:
|
||||||
|
display.print_info(f"🎯 Forcing X-Check to: {xcheck_position}")
|
||||||
|
else:
|
||||||
|
display.print_info(f"🎯 Forcing outcome: {forced_outcome.value}")
|
||||||
|
|
||||||
result = await game_engine.resolve_play(game_id, forced_outcome)
|
result = await game_engine.resolve_play(game_id, forced_outcome, xcheck_position)
|
||||||
state = await game_engine.get_game_state(game_id)
|
state = await game_engine.get_game_state(game_id)
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
|
|||||||
@ -268,12 +268,17 @@ HELP_DATA = {
|
|||||||
|
|
||||||
'resolve_with': {
|
'resolve_with': {
|
||||||
'summary': 'Resolve current play with a specific outcome (bypassing dice rolls)',
|
'summary': 'Resolve current play with a specific outcome (bypassing dice rolls)',
|
||||||
'usage': 'resolve_with <OUTCOME>',
|
'usage': 'resolve_with <OUTCOME>\n resolve_with x-check <POSITION>',
|
||||||
'options': [
|
'options': [
|
||||||
{
|
{
|
||||||
'name': 'OUTCOME',
|
'name': 'OUTCOME',
|
||||||
'type': 'STRING',
|
'type': 'STRING',
|
||||||
'desc': 'PlayOutcome enum value. Use list_outcomes to see all available values.'
|
'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': [
|
'examples': [
|
||||||
@ -281,9 +286,11 @@ HELP_DATA = {
|
|||||||
'resolve_with homerun',
|
'resolve_with homerun',
|
||||||
'resolve_with groundball_a',
|
'resolve_with groundball_a',
|
||||||
'resolve_with double_uncapped',
|
'resolve_with double_uncapped',
|
||||||
'resolve_with strikeout'
|
'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. Useful for testing runner advancement, scoring, and game state changes with known outcomes.'
|
'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': {
|
'quick_play': {
|
||||||
|
|||||||
@ -289,9 +289,11 @@ Press Ctrl+D or type 'quit' to exit.
|
|||||||
Resolve the current play with a specific outcome (for testing).
|
Resolve the current play with a specific outcome (for testing).
|
||||||
|
|
||||||
Usage: resolve_with <outcome>
|
Usage: resolve_with <outcome>
|
||||||
|
resolve_with x-check <position>
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
outcome PlayOutcome value (e.g., single_1, homerun, strikeout)
|
outcome PlayOutcome value (e.g., single_1, homerun, strikeout)
|
||||||
|
position For x-check: P, C, 1B, 2B, 3B, SS, LF, CF, RF
|
||||||
|
|
||||||
This command allows you to force a specific outcome instead of
|
This command allows you to force a specific outcome instead of
|
||||||
rolling dice, useful for testing runner advancement, specific
|
rolling dice, useful for testing runner advancement, specific
|
||||||
@ -304,31 +306,54 @@ Press Ctrl+D or type 'quit' to exit.
|
|||||||
resolve_with homerun
|
resolve_with homerun
|
||||||
resolve_with groundball_a
|
resolve_with groundball_a
|
||||||
resolve_with double_uncapped
|
resolve_with double_uncapped
|
||||||
|
resolve_with x-check SS # Test X-Check to shortstop
|
||||||
|
resolve_with x-check LF # Test X-Check to left field
|
||||||
"""
|
"""
|
||||||
async def _resolve_with():
|
async def _resolve_with():
|
||||||
try:
|
try:
|
||||||
gid = self._ensure_game()
|
gid = self._ensure_game()
|
||||||
await self._ensure_game_loaded(gid)
|
await self._ensure_game_loaded(gid)
|
||||||
|
|
||||||
# Parse outcome argument
|
# Parse arguments
|
||||||
outcome_str = arg.strip().lower()
|
args = arg.strip().lower().split()
|
||||||
if not outcome_str:
|
if not args:
|
||||||
display.print_error("Missing outcome argument")
|
display.print_error("Missing outcome argument")
|
||||||
display.print_info("Usage: resolve_with <outcome>")
|
display.print_info("Usage: resolve_with <outcome>")
|
||||||
|
display.print_info(" resolve_with x-check <position>")
|
||||||
display.print_info("Use 'list_outcomes' to see available values")
|
display.print_info("Use 'list_outcomes' to see available values")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check for x-check with position
|
||||||
|
outcome_str = args[0]
|
||||||
|
xcheck_position = None
|
||||||
|
|
||||||
|
if outcome_str in ['x-check', 'xcheck', 'x_check']:
|
||||||
|
if len(args) < 2:
|
||||||
|
display.print_error("Missing position for x-check")
|
||||||
|
display.print_info("Usage: resolve_with x-check <position>")
|
||||||
|
display.print_info("Valid positions: P, C, 1B, 2B, 3B, SS, LF, CF, RF")
|
||||||
|
return
|
||||||
|
outcome_str = 'x_check'
|
||||||
|
xcheck_position = args[1].upper()
|
||||||
|
|
||||||
|
# Validate position
|
||||||
|
valid_positions = ['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF']
|
||||||
|
if xcheck_position not in valid_positions:
|
||||||
|
display.print_error(f"Invalid position: {xcheck_position}")
|
||||||
|
display.print_info(f"Valid positions: {', '.join(valid_positions)}")
|
||||||
|
return
|
||||||
|
|
||||||
# Try to convert string to PlayOutcome enum
|
# Try to convert string to PlayOutcome enum
|
||||||
from app.config import PlayOutcome
|
from app.config import PlayOutcome
|
||||||
try:
|
try:
|
||||||
outcome = PlayOutcome(outcome_str)
|
outcome = PlayOutcome(outcome_str)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
display.print_error(f"Invalid outcome: {outcome_str}")
|
display.print_error(f"Invalid outcome: {outcome_str}")
|
||||||
display.print_info("Use 'list_outcomes' to see valid values")
|
display.print_info("Use 'list_outcomes' to see available values")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Use shared command with forced outcome
|
# Use shared command with forced outcome
|
||||||
await game_commands.resolve_play(gid, forced_outcome=outcome)
|
await game_commands.resolve_play(gid, forced_outcome=outcome, xcheck_position=xcheck_position)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user