CLAUDE: Implement forced outcome feature for terminal client testing
Add ability to force specific play outcomes instead of random dice rolls, enabling targeted testing of specific game scenarios. Changes: - play_resolver.resolve_play(): Add forced_outcome parameter, bypass dice rolls when provided, create dummy AbRoll with placeholder values - game_engine.resolve_play(): Accept and pass through forced_outcome param - terminal_client/commands.py: Pass forced_outcome to game engine Testing: - Verified TRIPLE, HOMERUN, and STRIKEOUT outcomes work correctly - Dummy AbRoll properly constructed with all required fields - Game state updates correctly with forced outcomes Example usage in REPL: resolve_with triple resolve_with homerun Fixes terminal client testing workflow to allow controlled scenarios. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
16ba30b351
commit
8ecce0f5ad
@ -324,7 +324,7 @@ class GameEngine:
|
||||
logger.warning(f"Offensive decision timeout for game {state.game_id}, using default")
|
||||
return OffensiveDecision() # All defaults
|
||||
|
||||
async def resolve_play(self, game_id: UUID) -> PlayResult:
|
||||
async def resolve_play(self, game_id: UUID, forced_outcome: Optional[PlayOutcome] = None) -> PlayResult:
|
||||
"""
|
||||
Resolve the current play with dice roll
|
||||
|
||||
@ -335,6 +335,13 @@ class GameEngine:
|
||||
4. Update game state in DB
|
||||
5. Check for inning change (outs >= 3)
|
||||
6. Prepare next play (always last step)
|
||||
|
||||
Args:
|
||||
game_id: Game to resolve
|
||||
forced_outcome: If provided, use this outcome instead of rolling dice (for testing)
|
||||
|
||||
Returns:
|
||||
PlayResult with complete outcome
|
||||
"""
|
||||
state = state_manager.get_state(game_id)
|
||||
if not state:
|
||||
@ -347,7 +354,7 @@ class GameEngine:
|
||||
offensive_decision = OffensiveDecision(**state.decisions_this_play.get('offensive', {}))
|
||||
|
||||
# STEP 1: Resolve play (this internally calls dice_system.roll_ab)
|
||||
result = play_resolver.resolve_play(state, defensive_decision, offensive_decision)
|
||||
result = play_resolver.resolve_play(state, defensive_decision, offensive_decision, forced_outcome)
|
||||
|
||||
# Track roll for batch saving at end of inning
|
||||
if game_id not in self._rolls_this_inning:
|
||||
|
||||
@ -11,9 +11,10 @@ Updated: 2025-10-29 - Integrated universal PlayOutcome enum
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, List
|
||||
import pendulum
|
||||
|
||||
from app.core.dice import dice_system
|
||||
from app.core.roll_types import AbRoll
|
||||
from app.core.roll_types import AbRoll, RollType
|
||||
from app.models.game_models import GameState, DefensiveDecision, OffensiveDecision
|
||||
from app.config import PlayOutcome
|
||||
|
||||
@ -135,7 +136,8 @@ class PlayResolver:
|
||||
self,
|
||||
state: GameState,
|
||||
defensive_decision: DefensiveDecision,
|
||||
offensive_decision: OffensiveDecision
|
||||
offensive_decision: OffensiveDecision,
|
||||
forced_outcome: Optional[PlayOutcome] = None
|
||||
) -> PlayResult:
|
||||
"""
|
||||
Resolve a complete play
|
||||
@ -144,22 +146,41 @@ class PlayResolver:
|
||||
state: Current game state
|
||||
defensive_decision: Defensive team's choices
|
||||
offensive_decision: Offensive team's choices
|
||||
forced_outcome: If provided, use this outcome instead of rolling dice (for testing)
|
||||
|
||||
Returns:
|
||||
PlayResult with complete outcome
|
||||
"""
|
||||
logger.info(f"Resolving play - Inning {state.inning} {state.half}, {state.outs} outs")
|
||||
|
||||
# Roll dice using our advanced AbRoll system
|
||||
ab_roll = dice_system.roll_ab(
|
||||
league_id=state.league_id,
|
||||
game_id=state.game_id
|
||||
)
|
||||
logger.info(f"AB Roll: {ab_roll}")
|
||||
if forced_outcome:
|
||||
# Use forced outcome for testing (no dice roll)
|
||||
logger.info(f"Using forced outcome: {forced_outcome.value}")
|
||||
outcome = forced_outcome
|
||||
# Create a dummy AbRoll for the forced outcome
|
||||
ab_roll = AbRoll(
|
||||
roll_id=f"forced_{state.game_id}_{state.play_count}",
|
||||
roll_type=RollType.AB,
|
||||
league_id=state.league_id,
|
||||
timestamp=pendulum.now('UTC'),
|
||||
game_id=state.game_id,
|
||||
d6_one=1, # Dummy values - not used for forced outcomes
|
||||
d6_two_a=3,
|
||||
d6_two_b=4,
|
||||
chaos_d20=10,
|
||||
resolution_d20=10
|
||||
)
|
||||
else:
|
||||
# Roll dice using our advanced AbRoll system
|
||||
ab_roll = dice_system.roll_ab(
|
||||
league_id=state.league_id,
|
||||
game_id=state.game_id
|
||||
)
|
||||
logger.info(f"AB Roll: {ab_roll}")
|
||||
|
||||
# Get base outcome from chart
|
||||
outcome = self.result_chart.get_outcome(ab_roll)
|
||||
logger.info(f"Base outcome: {outcome}")
|
||||
# Get base outcome from chart
|
||||
outcome = self.result_chart.get_outcome(ab_roll)
|
||||
logger.info(f"Base outcome: {outcome}")
|
||||
|
||||
# Apply decisions (simplified for Phase 2)
|
||||
# TODO: Implement full decision logic in Phase 3
|
||||
|
||||
@ -211,11 +211,8 @@ class GameCommands:
|
||||
try:
|
||||
if forced_outcome:
|
||||
display.print_info(f"🎯 Forcing outcome: {forced_outcome.value}")
|
||||
# TODO: Integrate with game_engine to properly apply forced outcomes
|
||||
display.print_warning("⚠️ Manual outcome selection is experimental")
|
||||
display.print_warning(" Using regular resolution for now (forced outcome noted)")
|
||||
|
||||
result = await game_engine.resolve_play(game_id)
|
||||
result = await game_engine.resolve_play(game_id, forced_outcome)
|
||||
state = await game_engine.get_game_state(game_id)
|
||||
|
||||
if state:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user