major-domo-v2/utils/scorebug_helpers.py
Cal Corum 87fb4491a9 CLAUDE: Fix scorebug win probability display logic and enhance percentage positioning
Fixed critical bug where win probability progress bar displayed backwards:
- Away team with 75% win probability was showing as losing
- Home team with 25% win probability was showing as winning

Changes:
- Corrected comparison operators in create_team_progress_bar() function
- Enhanced UX by positioning percentage next to winning team:
  * Home winning (>50%): Percentage on right (e.g., "POR ░▓▓▓▓▓▓▓▓▓► WV  95.0%")
  * Away winning (<50%): Percentage on left (e.g., "75.0% POR ◄▓▓▓▓▓▓▓▓░░ WV")
  * Even game (=50%): Percentage on both sides
- Added comprehensive test suite with 10 test cases covering all scenarios
- Updated docstring examples to reflect new format

All tests passing (10/10) 

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 00:39:41 -05:00

210 lines
7.4 KiB
Python

"""
Scorebug Display Helpers
Utility functions for formatting and displaying scorebug information.
"""
import discord
from typing import Optional
from views.embeds import EmbedColors
def create_scorebug_embed(
scorebug_data,
away_team,
home_team,
full_length: bool = True
) -> discord.Embed:
"""
Create a rich embed from scorebug data.
Args:
scorebug_data: ScorebugData object with game information
away_team: Away team object (optional)
home_team: Home team object (optional)
full_length: If True, includes pitcher/batter/runners/summary; if False, compact view
Returns:
Discord embed with scorebug information
"""
# Determine embed color based on win probability (not score!)
# This creates a fun twist where the favored team's color shows,
# even if they're currently losing
if scorebug_data.win_percentage > 50 and home_team:
embed_color = home_team.get_color_int() # Home team favored
elif scorebug_data.win_percentage < 50 and away_team:
embed_color = away_team.get_color_int() # Away team favored
else:
embed_color = EmbedColors.INFO # Even game (50/50)
# Create embed with header as title
embed = discord.Embed(
title=scorebug_data.header,
color=embed_color
)
# Get team abbreviations for use throughout
away_abbrev = away_team.abbrev if away_team else "AWAY"
home_abbrev = home_team.abbrev if home_team else "HOME"
# Create ASCII scorebug with bases visualization
occupied = ''
unoccupied = ''
# runners[0]=Catcher, [1]=On First, [2]=On Second, [3]=On Third
first_base = unoccupied if not scorebug_data.runners[1] or not scorebug_data.runners[1][0] else occupied
second_base = unoccupied if not scorebug_data.runners[2] or not scorebug_data.runners[2][0] else occupied
third_base = unoccupied if not scorebug_data.runners[3] or not scorebug_data.runners[3][0] else occupied
half = '' if scorebug_data.which_half == 'Top' else ''
if not scorebug_data.is_final:
inning = f'{half} {scorebug_data.inning}'
outs = f'{scorebug_data.outs} Out{"s" if scorebug_data.outs != 1 else ""}'
else:
# Final inning display
final_inning = scorebug_data.inning if scorebug_data.which_half == "Bot" else scorebug_data.inning - 1
inning = f'F/{final_inning}'
outs = ''
game_state_text = (
f'```\n'
f'{away_abbrev: ^4}{scorebug_data.away_score: ^3} {second_base}{inning: >10}\n'
f'{home_abbrev: ^4}{scorebug_data.home_score: ^3} {third_base} {first_base}{outs: >8}\n'
f'```'
)
# Add win probability bar
embed.add_field(
name='Win Probability',
value=create_team_progress_bar(
scorebug_data.win_percentage,
away_abbrev,
home_abbrev
),
inline=False
)
# Add game state
embed.add_field(
name='Game State',
value=game_state_text,
inline=False
)
# If not full_length, return compact version
if not full_length:
return embed
# Full length - add pitcher and batter info
if scorebug_data.pitcher_name:
embed.add_field(
name='Pitcher',
value=f'[{scorebug_data.pitcher_name}]({scorebug_data.pitcher_url})\n{scorebug_data.pitcher_stats}',
inline=True
)
if scorebug_data.batter_name:
embed.add_field(
name='Batter',
value=f'[{scorebug_data.batter_name}]({scorebug_data.batter_url})\n{scorebug_data.batter_stats}',
inline=True
)
# Add baserunners if present
on_first = scorebug_data.runners[1] if scorebug_data.runners[1] else ''
on_second = scorebug_data.runners[2] if scorebug_data.runners[2] else ''
on_third = scorebug_data.runners[3] if scorebug_data.runners[3] else ''
have_baserunners = len(on_first[0]) + len(on_second[0]) + len(on_third[0]) > 0
if have_baserunners > 0:
br_string = ''
if len(on_first) > 0:
br_string += f'On First: [{on_first[0]}]({on_first[1]})\n'
if len(on_second) > 0:
br_string += f'On Second: [{on_second[0]}]({on_second[1]})\n'
if len(on_third) > 0:
br_string += f'On Third: [{on_third[0]}]({on_third[1]})\n'
embed.add_field(name=' ', value=' ', inline=False) # Spacer
embed.add_field(
name='Baserunners',
value=br_string,
inline=True
)
# Add catcher
if scorebug_data.runners[0] and scorebug_data.runners[0][0]:
embed.add_field(
name='Catcher',
value=f'[{scorebug_data.runners[0][0]}]({scorebug_data.runners[0][1]})',
inline=True
)
# Add inning summary if not final
if not scorebug_data.is_final and scorebug_data.summary:
i_string = ''
for line in scorebug_data.summary:
if line and len(line) >= 2 and line[0]:
i_string += f'- Play {line[0]}: {line[1]}\n'
if i_string and "IFERROR" not in i_string:
embed.add_field(
name='Inning Summary',
value=i_string,
inline=False
)
return embed
def create_team_progress_bar(
win_percentage: float,
away_abbrev: str,
home_abbrev: str,
length: int = 10
) -> str:
"""
Create a proportional progress bar showing each team's win probability.
Args:
win_percentage: Home team's win percentage (0-100)
away_abbrev: Away team abbreviation (e.g., "POR")
home_abbrev: Home team abbreviation (e.g., "WV")
length: Total bar length in blocks (default 10)
Returns:
Formatted bar with dark blocks (▓) weighted toward winning team.
Arrow extends from the side with the advantage.
Percentage displayed on winning team's side (or both sides if even).
Examples:
Home winning: "POR ░▓▓▓▓▓▓▓▓▓► WV 95.0%"
Away winning: "70.0% POR ◄▓▓▓▓▓▓▓░░░ WV"
Even game: "50.0% POR =▓▓▓▓▓▓▓▓▓▓= WV 50.0%"
"""
# Calculate blocks for each team (home team's percentage)
home_blocks = int((win_percentage / 100) * length)
away_blocks = length - home_blocks
if win_percentage > 50:
# Home team (right side) is winning
away_char = '' # Light blocks for losing team
home_char = '' # Dark blocks for winning team
bar = away_char * away_blocks + home_char * home_blocks
# Arrow extends from right side, percentage on right
return f'{away_abbrev} {bar}{home_abbrev} {win_percentage:.1f}%'
elif win_percentage < 50:
# Away team (left side) is winning
away_char = '' # Dark blocks for winning team
home_char = '' # Light blocks for losing team
bar = away_char * away_blocks + home_char * home_blocks
# Arrow extends from left side, percentage on left (showing away team's win %)
away_win_pct = 100 - win_percentage
return f'{away_win_pct:.1f}% {away_abbrev}{bar} {home_abbrev}'
else:
# Even game (50/50)
away_char = ''
home_char = ''
bar = away_char * away_blocks + home_char * home_blocks
# Arrows on both sides for balanced display, percentage on both sides
return f'{win_percentage:.1f}% {away_abbrev} ={bar}= {home_abbrev} {win_percentage:.1f}%'