CLAUDE: Add HBP (Hit By Pitch) handler to PlayResolver
Fixed missing PlayOutcome.HIT_BY_PITCH handling in resolve_outcome(). HBP was being submitted via WebSocket but rejected with error: "Unhandled outcome: PlayOutcome.HIT_BY_PITCH" Changes: - Added HBP case to play_resolver.py (lines 430-446) - HBP uses same forced runner advancement logic as WALK - HBP is NOT classified as is_walk=True (correct for stats) - Added 2 unit tests: test_hit_by_pitch, test_hit_by_pitch_bases_loaded Test results: 981/981 passing (100%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bda98b1efe
commit
b12905a71b
@ -427,6 +427,24 @@ class PlayResolver:
|
||||
is_walk=True,
|
||||
)
|
||||
|
||||
if outcome == PlayOutcome.HIT_BY_PITCH:
|
||||
# HBP - identical to walk: batter to first, runners advance if forced
|
||||
runners_advanced = self._advance_on_walk(state)
|
||||
runs_scored = sum(
|
||||
1 for (from_base, to_base) in runners_advanced if to_base == 4
|
||||
)
|
||||
|
||||
return PlayResult(
|
||||
outcome=outcome,
|
||||
outs_recorded=0,
|
||||
runs_scored=runs_scored,
|
||||
batter_result=1,
|
||||
runners_advanced=runners_advanced,
|
||||
description="Hit by pitch",
|
||||
ab_roll=ab_roll,
|
||||
# Note: HBP is NOT classified as a walk for statistics purposes
|
||||
)
|
||||
|
||||
# ==================== Singles ====================
|
||||
if outcome == PlayOutcome.SINGLE_1:
|
||||
# Single with standard advancement
|
||||
|
||||
@ -148,6 +148,71 @@ class TestResolveOutcome:
|
||||
assert result.runs_scored == 1 # Runner on 3rd forced home
|
||||
assert (3, 4) in result.runners_advanced
|
||||
|
||||
def test_hit_by_pitch(self):
|
||||
"""
|
||||
Test HBP (Hit By Pitch) resolution.
|
||||
|
||||
HBP works identically to walk for runner advancement, but is NOT
|
||||
classified as a walk for baseball statistics purposes.
|
||||
"""
|
||||
resolver = PlayResolver(league_id="sba", auto_mode=False)
|
||||
state = GameState(
|
||||
game_id=uuid4(),
|
||||
league_id="sba",
|
||||
home_team_id=1,
|
||||
away_team_id=2,
|
||||
current_batter=LineupPlayerState(lineup_id=1, card_id=100, position="CF", batting_order=1)
|
||||
)
|
||||
ab_roll = create_mock_ab_roll(state.game_id)
|
||||
|
||||
result = resolver.resolve_outcome(
|
||||
outcome=PlayOutcome.HIT_BY_PITCH,
|
||||
hit_location=None,
|
||||
state=state,
|
||||
defensive_decision=DefensiveDecision(),
|
||||
offensive_decision=OffensiveDecision(),
|
||||
ab_roll=ab_roll
|
||||
)
|
||||
|
||||
assert result.outcome == PlayOutcome.HIT_BY_PITCH
|
||||
assert result.outs_recorded == 0
|
||||
assert result.runs_scored == 0
|
||||
assert result.batter_result == 1 # Batter to first
|
||||
assert result.is_walk is False # HBP is NOT classified as walk
|
||||
assert result.is_hit is False
|
||||
assert result.description == "Hit by pitch"
|
||||
|
||||
def test_hit_by_pitch_bases_loaded(self):
|
||||
"""
|
||||
Test HBP with bases loaded forces run home.
|
||||
|
||||
Same forced runner advancement logic as walk.
|
||||
"""
|
||||
resolver = PlayResolver(league_id="sba", auto_mode=False)
|
||||
state = GameState(
|
||||
game_id=uuid4(),
|
||||
league_id="sba",
|
||||
home_team_id=1,
|
||||
away_team_id=2,
|
||||
current_batter=LineupPlayerState(lineup_id=1, card_id=100, position="CF", batting_order=1),
|
||||
on_first=LineupPlayerState(lineup_id=2, card_id=101, position="CF", batting_order=2),
|
||||
on_second=LineupPlayerState(lineup_id=3, card_id=102, position="RF", batting_order=3),
|
||||
on_third=LineupPlayerState(lineup_id=4, card_id=103, position="SS", batting_order=4)
|
||||
)
|
||||
ab_roll = create_mock_ab_roll(state.game_id)
|
||||
|
||||
result = resolver.resolve_outcome(
|
||||
outcome=PlayOutcome.HIT_BY_PITCH,
|
||||
hit_location=None,
|
||||
state=state,
|
||||
defensive_decision=DefensiveDecision(),
|
||||
offensive_decision=OffensiveDecision(),
|
||||
ab_roll=ab_roll
|
||||
)
|
||||
|
||||
assert result.runs_scored == 1 # Runner on 3rd forced home
|
||||
assert (3, 4) in result.runners_advanced
|
||||
|
||||
def test_groundball_uses_runner_advancement(self):
|
||||
"""Test that groundballs delegate to RunnerAdvancement"""
|
||||
resolver = PlayResolver(league_id="sba", auto_mode=False)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user