fix: preserve batter at plate when half-inning ends on caught stealing
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m5s
When a half-inning ended with a CS (or pickoff), the batter who was at the plate was incorrectly skipped in the next inning. The side-switch code unconditionally advanced the batting order by 1 without checking whether the last play was a plate appearance. Now checks opponent_play.pa before incrementing, matching the existing non-side-switch logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a9fa2abbd1
commit
046b35e5a5
@ -753,7 +753,7 @@ def complete_play(session: Session, this_play: Play):
|
||||
opponent_play = get_last_team_play(
|
||||
session, this_play.game, this_play.pitcher.team
|
||||
)
|
||||
nbo = opponent_play.batting_order + 1
|
||||
nbo = opponent_play.batting_order + 1 if opponent_play.pa == 1 else opponent_play.batting_order
|
||||
except PlayNotFoundException as e:
|
||||
logger.info(
|
||||
f"logic_gameplay - complete_play - No last play found for {this_play.pitcher.team.sname}, setting upcoming batting order to 1"
|
||||
|
||||
@ -569,3 +569,95 @@ def test_pinch_runner_entry_and_scoring(session: Session):
|
||||
|
||||
# Entry play should NOT be in this list (PA=0)
|
||||
assert entry_play not in pitcher_plays
|
||||
|
||||
|
||||
async def test_cs_end_of_inning_preserves_batter(session: Session):
|
||||
"""
|
||||
Test that when a half-inning ends on a Caught Stealing, the batter who was
|
||||
at the plate (but did not complete a plate appearance) leads off the next
|
||||
time their team bats.
|
||||
|
||||
Scenario:
|
||||
Top 1 - Away team batting:
|
||||
Batter 1 (bo=1): strikeout → 1 out
|
||||
Batter 2 (bo=2): strikeout → 2 outs
|
||||
Batter 3 (bo=3): single, reaches first
|
||||
Batter 4 (bo=4): at the plate, runner caught stealing → 3 outs, side switch
|
||||
|
||||
Bot 1 - Home team batting:
|
||||
Batter 1 (bo=1): strikeout → 1 out
|
||||
Batter 2 (bo=2): strikeout → 2 outs
|
||||
Batter 3 (bo=3): strikeout → 3 outs, side switch
|
||||
|
||||
Top 2 - Away team batting:
|
||||
The next batter should be Batter 4 (bo=4), NOT Batter 5 (bo=5),
|
||||
because Batter 4 never completed a plate appearance.
|
||||
"""
|
||||
this_game = session.get(Game, 3)
|
||||
play = this_game.initialize_play(session)
|
||||
|
||||
# --- TOP 1 ---
|
||||
|
||||
# Batter 1 (bo=1): strikeout → 1 out
|
||||
assert play.batting_order == 1
|
||||
assert play.inning_half == 'top'
|
||||
play = await strikeouts(session, None, play)
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Batter 2 (bo=2): strikeout → 2 outs
|
||||
assert play.batting_order == 2
|
||||
assert play.starting_outs == 1
|
||||
play = await strikeouts(session, None, play)
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Batter 3 (bo=3): single → runner on first
|
||||
assert play.batting_order == 3
|
||||
assert play.starting_outs == 2
|
||||
play = await singles(session, None, play, '*')
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Batter 4 (bo=4) is at the plate with runner on first
|
||||
assert play.batting_order == 4
|
||||
assert play.starting_outs == 2
|
||||
assert play.on_first is not None
|
||||
assert play.inning_half == 'top'
|
||||
|
||||
# Runner on first is caught stealing → 3rd out, side switch
|
||||
play = await steals(session, None, play, 'caught-stealing', to_base=2)
|
||||
assert play.pa == 0 # CS is not a plate appearance
|
||||
assert play.outs == 1 # CS records one out
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Should now be bottom of inning 1
|
||||
assert play.inning_half == 'bot'
|
||||
assert play.inning_num == 1
|
||||
assert play.is_new_inning is True
|
||||
|
||||
# --- BOT 1 ---
|
||||
|
||||
# Home batter 1 (bo=1): strikeout → 1 out
|
||||
assert play.batting_order == 1
|
||||
play = await strikeouts(session, None, play)
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Home batter 2 (bo=2): strikeout → 2 outs
|
||||
assert play.batting_order == 2
|
||||
play = await strikeouts(session, None, play)
|
||||
play = complete_play(session, play)
|
||||
|
||||
# Home batter 3 (bo=3): strikeout → 3 outs, side switch
|
||||
assert play.batting_order == 3
|
||||
play = await strikeouts(session, None, play)
|
||||
play = complete_play(session, play)
|
||||
|
||||
# --- TOP 2 ---
|
||||
|
||||
# Should be top of inning 2, and batter 4 (bo=4) should lead off
|
||||
# because they never completed a PA in the previous half-inning
|
||||
assert play.inning_half == 'top'
|
||||
assert play.inning_num == 2
|
||||
assert play.is_new_inning is True
|
||||
assert play.batting_order == 4, (
|
||||
f"Expected batter 4 to lead off (never completed PA), "
|
||||
f"but got batter {play.batting_order}"
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user