CLAUDE: Fix batting order recovery after inning changes

When recovering game state after reconnect/refresh, the old code:
1. Only looked at the last play overall (regardless of team)
2. Used that play's batting order for the currently batting team
3. Reset the non-batting team's index to 0

This caused the wrong batter to appear after inning changes - e.g.,
batter #1 showing instead of #5 in top of 2nd after bottom of 1st ended.

Fix: Now recovers each team's batter index separately by filtering
plays by half ("top" = away team, "bottom" = home team) and finding
each team's last at-bat independently.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-11-28 23:38:54 -06:00
parent 4d2c905a1c
commit dc9f98ad88

View File

@ -435,65 +435,81 @@ class StateManager:
else: else:
state.outs = outs_after state.outs = outs_after
# Recover batter indices - find which batter is next based on last play # Recover batter indices - find last play for EACH team separately
last_batter_order = last_play.get("batting_order") # This is critical after inning changes - we need both teams' positions
logger.info(f"Recovery: last_batter_order from play #{last_play['play_number']} = {last_batter_order}") away_plays = [p for p in completed_plays if p.get("half") == "top"]
home_plays = [p for p in completed_plays if p.get("half") == "bottom"]
if last_batter_order: # Away team's last at-bat
# Next batter is last_batter + 1, wrapping at 9 if away_plays:
next_batter_order = (last_batter_order % 9) + 1 last_away_play = max(away_plays, key=lambda p: p["play_number"])
away_last_order = last_away_play.get("batting_order")
# Set the correct index for the batting team if away_last_order:
# Index is order - 1 (0-indexed) # Next batter is last_batter + 1, wrapping at 9
next_batter_idx = next_batter_order - 1 state.away_team_batter_idx = away_last_order % 9 # Already 0-indexed after mod
logger.info(f"Recovery: Away team last batter was #{away_last_order}, next is #{state.away_team_batter_idx + 1}")
if batting_team_id == away_team_id:
state.away_team_batter_idx = next_batter_idx
state.home_team_batter_idx = 0
else: else:
state.home_team_batter_idx = next_batter_idx
state.away_team_batter_idx = 0 state.away_team_batter_idx = 0
logger.info(f"Recovery: Set batter indices - next_order={next_batter_order}, next_idx={next_batter_idx}, batting_team={batting_team_id}")
# Update current_batter to match the recovered batter index
# Get batting lineup sorted by batting_order
batting_lineup = [
get_lineup_player(lineup["id"])
for lineup in lineups
if lineup.get("team_id") == batting_team_id
and lineup.get("batting_order") is not None
and lineup.get("is_active")
]
logger.info(f"Recovery: Found {len(batting_lineup)} batters (before None filter)")
# Filter out None values (if any)
batting_lineup = [b for b in batting_lineup if b is not None]
logger.info(f"Recovery: {len(batting_lineup)} batters after None filter")
batting_lineup_sorted = sorted(
batting_lineup, key=lambda x: x.batting_order or 0
)
logger.info(f"Recovery: Sorted lineup has {len(batting_lineup_sorted)} batters")
# Set current_batter to the batter at next_batter_idx
if next_batter_idx < len(batting_lineup_sorted):
state.current_batter = batting_lineup_sorted[next_batter_idx]
logger.info(
f"Recovery: ✓ Set current_batter to order={next_batter_order}, idx={next_batter_idx}, "
f"card_id={state.current_batter.card_id}, batting_order={state.current_batter.batting_order}"
)
else:
logger.warning(
f"Recovery: ✗ Batter index {next_batter_idx} out of range for batting order "
f"(lineup size: {len(batting_lineup_sorted)})"
)
logger.info(f"Recovery: Calculated next_order={next_batter_order}, next_idx={next_batter_idx}")
else: else:
logger.info("Recovery: No last_batter_order found - using defaults") # Away team hasn't batted yet (game just started or first pitch)
# Fallback if no batting order in last play
state.away_team_batter_idx = 0 state.away_team_batter_idx = 0
logger.info("Recovery: Away team has no plays yet, starting at batter #1")
# Home team's last at-bat
if home_plays:
last_home_play = max(home_plays, key=lambda p: p["play_number"])
home_last_order = last_home_play.get("batting_order")
if home_last_order:
# Next batter is last_batter + 1, wrapping at 9
state.home_team_batter_idx = home_last_order % 9 # Already 0-indexed after mod
logger.info(f"Recovery: Home team last batter was #{home_last_order}, next is #{state.home_team_batter_idx + 1}")
else:
state.home_team_batter_idx = 0
else:
# Home team hasn't batted yet
state.home_team_batter_idx = 0 state.home_team_batter_idx = 0
logger.info("Recovery: Home team has no plays yet, starting at batter #1")
# Determine which index to use for current_batter
if batting_team_id == away_team_id:
next_batter_idx = state.away_team_batter_idx
else:
next_batter_idx = state.home_team_batter_idx
logger.info(f"Recovery: Set batter indices - away={state.away_team_batter_idx}, home={state.home_team_batter_idx}, current batting team uses idx={next_batter_idx}")
# Update current_batter to match the recovered batter index
# Get batting lineup sorted by batting_order
batting_lineup = [
get_lineup_player(lineup["id"])
for lineup in lineups
if lineup.get("team_id") == batting_team_id
and lineup.get("batting_order") is not None
and lineup.get("is_active")
]
logger.info(f"Recovery: Found {len(batting_lineup)} batters (before None filter)")
# Filter out None values (if any)
batting_lineup = [b for b in batting_lineup if b is not None]
logger.info(f"Recovery: {len(batting_lineup)} batters after None filter")
batting_lineup_sorted = sorted(
batting_lineup, key=lambda x: x.batting_order or 0
)
logger.info(f"Recovery: Sorted lineup has {len(batting_lineup_sorted)} batters")
# Set current_batter to the batter at next_batter_idx
if next_batter_idx < len(batting_lineup_sorted):
state.current_batter = batting_lineup_sorted[next_batter_idx]
logger.info(
f"Recovery: ✓ Set current_batter to idx={next_batter_idx}, "
f"card_id={state.current_batter.card_id}, batting_order={state.current_batter.batting_order}"
)
else:
logger.warning(
f"Recovery: ✗ Batter index {next_batter_idx} out of range for batting order "
f"(lineup size: {len(batting_lineup_sorted)})"
)
# Always start at awaiting_defensive on recovery # Always start at awaiting_defensive on recovery
# (Users can re-submit decisions if they refreshed mid-workflow) # (Users can re-submit decisions if they refreshed mid-workflow)