Skip to content

Commit

Permalink
Display live baseball
Browse files Browse the repository at this point in the history
  • Loading branch information
ty-porter committed Mar 18, 2024
1 parent 22c7859 commit 8ef96bf
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 31 deletions.
22 changes: 22 additions & 0 deletions rewrite/data/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,28 @@ def man_on(self, base_number):

return value_at_keypath(self.data, f"liveData.linescore.offense.{base}").get("id", None)

def batter(self):
return self.__fetch_player("offense", "batter")

def in_hole(self):
return self.__fetch_player("offense", "inHole")

def on_deck(self):
return self.__fetch_player("offense", "onDeck")

def pitcher(self):
return self.__fetch_player("defense", "pitcher")

def boxscore_name(self, ID):
ID = format_id(ID)

return value_at_keypath(self.data, f"gameData.players.{ID}.boxscoreName")

def __fetch_player(self, team, position):
ID = value_at_keypath(self.data, f"liveData.linescore.{team}.{position}").get("id", None)

return self.boxscore_name(ID)

def pitches(self):
return Pitches(self)

Expand Down
30 changes: 15 additions & 15 deletions rewrite/data/pitches.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from data.team import TeamType

from utils import format_id, value_at_keypath

class Pitches:
Expand Down Expand Up @@ -98,21 +100,19 @@ def last_pitch(self):
return None

def current_pitcher_pitch_count(self):
# TODO: Clean this up
try:
pitcher_id = self.game.data["liveData"]["linescore"]["defense"]["pitcher"]["id"]

# TODO: ID formatting probably doesn't belong on Game object if it's being used here
ID = format_id(pitcher_id)
try:
return self.game.data["liveData"]["boxscore"]["teams"]["away"]["players"][ID]["stats"]["pitching"][
"numberOfPitches"
]
except:
return self.game.data["liveData"]["boxscore"]["teams"]["home"]["players"][ID]["stats"]["pitching"][
"numberOfPitches"
]
except:
pitcher_id = value_at_keypath(self.game.data, "liveData.linescore.defense.pitcher").get("id", None)

if pitcher_id is None:
return 0

ID = format_id(pitcher_id)

for team in [TeamType.HOME, TeamType.AWAY]:
pitches = value_at_keypath(self.game.data, f"liveData.boxscore.teams.{team}.players.{ID}.stats.pitching").get("numberOfPitches", 0)

if pitches > 0:
return pitches

return 0

def __fetch_count_part(self, part):
Expand Down
31 changes: 29 additions & 2 deletions rewrite/presenters/live_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,32 @@ def __init__(self, game, config):
self.game = game
self.config = config

def batter_count_text(self):
return "{}-{}".format(self.game.pitches().balls, self.game.pitches().strikes)
def count_text(self):
return "{}-{}".format(self.game.pitches().balls, self.game.pitches().strikes)

def pitcher_text(self):
pitcher = self.game.pitcher()
pitch_count = self.config.layout.coords("atbat.pitch_count")

if pitch_count.enabled and pitch_count.append_pitcher_name:
pitches = self.game.pitches().curren_pitcher_pitch_count()
pitcher = f"{pitcher} ({pitches})"

return pitcher

def pitch_text(self):
coords = self.config.layout.coords("atbat.pitch")
pitches = self.game.pitches()

if int(pitches.last_pitch_speed) and coords.enabled:
mph = " "
if coords.mph:
mph = "mph "
if coords.desc_length.lower() == "long":
pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type_long
elif coords.desc_length.lower() == "short":
pitch_text = str(pitches.last_pitch_speed) + mph + pitches.last_pitch_type
else:
pitch_text = ""

return pitch_text
92 changes: 91 additions & 1 deletion rewrite/screens/games/live_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from screens.components.out import Out
from presenters.live_game import LiveGamePresenter

from utils.text import ScrollingText

class LiveGameScreen(GameScreen):
MAX_DURATION_SECONDS = 5
Expand All @@ -27,6 +28,9 @@ def __init__(self, *args, **kwargs):
def render(self):
presenter = self.create_cached_object("live_game_presenter", LiveGamePresenter, self.game, self.config)

# Scrollers should render first to avoid masking each other.
self.__render_at_bat(presenter)

self.__render_bases()
self.__render_outs()
self.__render_count(presenter)
Expand All @@ -44,9 +48,95 @@ def __render_outs(self):
out.render()

def __render_count(self, presenter):
text = presenter.batter_count_text()
text = presenter.count_text()
font, font_size = self.layout.font_for("batter_count")
coords = self.layout.coords("batter_count")
color = self.colors.graphics_color("batter_count")

graphics.DrawText(self.canvas, font, coords.x, coords.y, color, text)

def __render_at_bat(self, presenter):
# Pitcher
self.__render_pitcher_text(presenter)
self.__render_pitch_text(presenter)
self.__render_pitch_count()

# Batter
self.__render_batter_text()

def __render_pitcher_text(self, presenter):
text = presenter.pitcher_text()
coords = self.layout.coords("atbat.pitcher")
color = self.colors.graphics_color("atbat.pitcher")
font, font_size = self.layout.font_for("atbat.pitcher")
bgcolor = self.colors.graphics_color("default.background")

label = "P:"

scroller = self.create_cached_object(
"pitcher_text_scroller",
ScrollingText,
self.canvas,
coords.x + font_size[0] * len(label),
coords.y,
coords.width,
font,
font_size,
color,
bgcolor,
text,
center=False
)
scroller.render_text()

# Pitcher label
graphics.DrawText(self.canvas, font, coords.x, coords.y, color, label)

def __render_pitch_text(self, presenter):
text = presenter.pitch_text()

if text is None:
return

coords = self.layout.coords("atbat.pitch")
color = self.colors.graphics_color("atbat.pitch")
font, font_size = self.layout.font("atbat.pitch")

graphics.DrawText(self.canvas, font, coords.x, coords.y, color, text)

def __render_pitch_count(self):
coords = self.layout.coords("atbat.pitch_count")
color = self.colors.graphics_color("atbat.pitch_count")
font, font_size = self.layout.font_for("atbat.pitch_count")

if coords.enabled and not coords.append_pitcher_name:
pitch_count = f"{self.game.pitches().pitch_count}P"
graphics.DrawText(self.canvas, font, coords.x, coords.y, color, pitch_count)

def __render_batter_text(self):
coords = self.layout.coords("atbat.batter")
color = self.colors.graphics_color("atbat.batter")
font, font_size = self.layout.font_for("atbat.batter")
bgcolor = self.colors.graphics_color("default.background")
# TODO: There's an offset to the starting position here for some reason in the old code, but only on certain layouts?
# offset = coords._asdict().get("offset", 0)

label = "AB:"

scroller = self.create_cached_object(
"batter_text_scroller",
ScrollingText,
self.canvas,
coords.x + font_size[0] * len(label),
coords.y,
coords.width,
font,
font_size,
color,
bgcolor,
self.game.batter(),
center=False
)
scroller.render_text()

graphics.DrawText(self.canvas, font, coords.x, coords.y, color, label)
1 change: 0 additions & 1 deletion rewrite/screens/games/postgame.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ def __render_decision_scroll(self, presenter):
coords.x,
coords.y,
coords.width,
coords.width,
font,
font_size,
color,
Expand Down
1 change: 0 additions & 1 deletion rewrite/screens/games/pregame.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ def __render_info(self, presenter):
coords.x,
coords.y,
coords.width,
coords.width,
font,
font_size,
color,
Expand Down
53 changes: 42 additions & 11 deletions rewrite/utils/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from dataclasses import dataclass

from utils.graphics import DrawRect


@dataclass
class TextPosition:
Expand All @@ -10,12 +12,16 @@ class TextPosition:


class ScrollingText:
def __init__(self, canvas, x, y, width, start_x, font, font_size, text_color, bg_color, text, center=True):
def __init__(self, canvas, x, y, width, font, font_size, text_color, bg_color, text, start_x = None, center=True):
# Matrix
self.canvas = canvas

# Start X and Y
if start_x is None:
start_x = x + width

self.end_position = TextPosition(x=x, y=y)
self.start_position = TextPosition(x=start_x, y=y)

# Font options
self.font = font
Expand All @@ -34,19 +40,18 @@ def __init__(self, canvas, x, y, width, start_x, font, font_size, text_color, bg
# Current position
self.position = TextPosition(x=start_x, y=y)
self.finished = False

def __should_scroll(self):
return len(self.text) * self.font_size[0] > self.width
self.static = len(self.text) * self.font_size[0] <= self.width

def render_text(self):
if self.finished:
return

if self.__should_scroll():
self.__render_scroll_text()
else:
if self.static:
self.__render_static_text(self.center)
self.finished = True
else:
if not self.finished:
self.__render_scroll_text()

# Render the background color over excess text
self.__apply_mask()

def __render_scroll_text(self):
# This is done so that we don't need to draw a huge bar to the left of the text.
Expand Down Expand Up @@ -79,6 +84,7 @@ def __render_scroll_text(self):
def __render_static_text(self, center):
if center:
if self._centered_text is None:
# BUG: This does not correctly calculate the center position
self._centered_text = CenteredText(
self.canvas,
self.position.x,
Expand All @@ -92,7 +98,7 @@ def __render_static_text(self, center):

self._centered_text.render_text()
else:
graphics.DrawText(self.canvas, self.font, self.position.x, self.position.y, self.text_color, self.text)
graphics.DrawText(self.canvas, self.font, self.end_position.x, self.end_position.y, self.text_color, self.text)

def __truncate_text(self, text, font_w, font_h):
text = self.text
Expand All @@ -116,6 +122,31 @@ def __perform_scroll(self, text_width):

self.position.x = next_x

def __apply_mask(self):
'''
Applies a mask to the matrix such that text is only visible if it is within the window configured by the scroller.
'''

# Left side
DrawRect(
self.canvas,
0,
self.end_position.y - self.font_size[1],
self.end_position.x - 1,
self.font_size[1] + 1,
self.bg_color
)

# Right side
DrawRect(
self.canvas,
self.start_position.x + 1,
self.start_position.y - self.font_size[1],
self.canvas.width - self.start_position.x + 1,
self.font_size[1] + 1,
self.bg_color
)


class CenteredText:
def __init__(self, canvas, x, y, font, font_size, text_color, text, bg_color=None):
Expand Down

0 comments on commit 8ef96bf

Please sign in to comment.