Skip to content

Commit

Permalink
Wrap up most postgame screen work, text utils and object caching on s…
Browse files Browse the repository at this point in the history
…creens
  • Loading branch information
ty-porter committed Mar 12, 2024
1 parent b749c45 commit 803eea9
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 24 deletions.
18 changes: 17 additions & 1 deletion rewrite/config/colors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os, sys

from utils import logger as ScoreboardLogger
from utils import deep_update, read_json
from utils import value_at_keypath, deep_update, read_json


class Colors:
Expand All @@ -10,6 +10,8 @@ class Colors:
TEAM_COLORS_REFERENCE_FILENAME = "teams.json.example"
SCOREBOARD_COLORS_REFERENCE_FILENAME = "scoreboard.json.example"

DEFAULT_COLOR = (0, 0, 0)

def __init__(self):
self._team_json = self.__fetch_colors(Colors.TEAM_COLORS_REFERENCE_FILENAME)
self._scoreboard_json = self.__fetch_colors(Colors.SCOREBOARD_COLORS_REFERENCE_FILENAME)
Expand All @@ -31,3 +33,17 @@ def __fetch_colors(self, reference_filename):
return colors

return reference_colors

def team_graphics_color(self, keypath):
return self.__fetch_color(self._team_json, keypath)

def graphics_color(self, keypath):
return self.__fetch_color(self._scoreboard_json, keypath)

def __fetch_color(self, config, keypath):
color = value_at_keypath(config, keypath)

if color:
return (color["r"], color["g"], color["b"])

return Colors.DEFAULT_COLOR
36 changes: 36 additions & 0 deletions rewrite/data/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,39 @@ def datetime(self):
time_utc = self.data["gameData"]["datetime"]["dateTime"]

return dt.fromisoformat(time_utc.replace("Z", "+00:00"))

def home_score(self):
return self.data["liveData"]["linescore"]["teams"]["home"].get("runs", 0)

def away_score(self):
return self.data["liveData"]["linescore"]["teams"]["away"].get("runs", 0)

def home_hits(self):
return self.data["liveData"]["linescore"]["teams"]["home"].get("hits", 0)

def away_hits(self):
return self.data["liveData"]["linescore"]["teams"]["away"].get("hits", 0)

def home_errors(self):
return self.data["liveData"]["linescore"]["teams"]["home"].get("errors", 0)

def away_errors(self):
return self.data["liveData"]["linescore"]["teams"]["away"].get("errors", 0)

def winning_team(self):
if self.status == GameState.FINAL:
if self.home_score() > self.away_score():
return "home"
if self.home_score() < self.away_score():
return "away"

return None

def losing_team(self):
opposite = {"home": "away", "away": "home"}

return opposite.get(self.winning_team(), None)

def series_status(self):
# TODO: Reimplement series status
return "0-0"
49 changes: 49 additions & 0 deletions rewrite/presenters/postgame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class PostgamePresenter:
PITCHER_UNKNOWN = "Unknown"

def __init__(self, game):
winner_side = game.winning_team()

# Defaults
self.winning_pitcher = PostgamePresenter.PITCHER_UNKNOWN
self.winning_pitcher_wins = 0
self.winning_pitcher_losses = 0
self.losing_pitcher = PostgamePresenter.PITCHER_UNKNOWN
self.losing_pitcher_wins = 0
self.losing_pitcher_losses = 0
self.save_pitcher = None
self.save_pitcher_saves = None

# winner = game.decision_pitcher_id("winner")
# if winner is not None:
# self.winning_pitcher = game.full_name(winner)
# self.winning_pitcher_wins = game.pitcher_stat(winner, "wins", winner_side)
# self.winning_pitcher_losses = game.pitcher_stat(winner, "losses", winner_side)

# save = game.decision_pitcher_id("save")
# if save is not None:
# self.save_pitcher = game.full_name(save)
# self.save_pitcher_saves = game.pitcher_stat(save, "saves", winner_side)

# loser = game.decision_pitcher_id("loser")
# if loser is not None:
# loser_side = game.losing_team()
# self.losing_pitcher = game.full_name(loser)
# self.losing_pitcher_wins = game.pitcher_stat(loser, "wins", loser_side)
# self.losing_pitcher_losses = game.pitcher_stat(loser, "losses", loser_side)

self.series_status = game.series_status()

def __str__(self):
return "<{} {}> W: {} {}-{}; L: {} {}-{}; S: {} ({})".format(
self.__class__.__name__,
hex(id(self)),
self.winning_pitcher,
self.winning_pitcher_wins,
self.winning_pitcher_losses,
self.losing_pitcher,
self.losing_pitcher_wins,
self.losing_pitcher_losses,
self.save_pitcher,
self.save_pitcher_saves,
)
32 changes: 32 additions & 0 deletions rewrite/screens/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,25 @@ def ready_to_transition(self):
"""
return True

def create_cached_object(self, name, klass, *args, **kwargs):
"""
Creates an object and caches it between render cycles. The arguments and keyword arguments are passed to the class's constructor and stored in cache under
the configured name. Repeated calls to this function fetch from the cache instead of re-initializing.
"""
if not hasattr(self, "_object_cache"):
self._object_cache = {}

if name in self._object_cache:
return self._object_cache.get(name)

cached_object = klass(*args, **kwargs)
self._object_cache[name] = cached_object

return cached_object

@track_duration
def _render(self):
self.canvas.Fill(*self.background_color)
self.render()

@property
Expand All @@ -55,3 +72,18 @@ def config(self):
@property
def layout(self):
return self.config.layout

@property
def colors(self):
return self.config.colors

@property
def background_color(self):
"""
The default background can be overridden in child classes.
"""
return self.colors.graphics_color("default.background")

@property
def default_text_color(self):
return self.colors.graphics_color("default.text")
79 changes: 58 additions & 21 deletions rewrite/screens/games/postgame.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
import os, time

from driver import graphics

from screens.games.base import GameScreen

from presenters.postgame import PostgamePresenter

from utils.text import ScrollingText, CenteredText

class PostGameScreen(GameScreen):

class PostgameScreen(GameScreen):
MAX_DURATION_SECONDS = 5

def render(self):
game_text = "It's a post-game!"
presenter = self.create_cached_object("postgame_presenter", PostgamePresenter, self.game)

self.__render_final_inning(presenter)
self.__render_decision_scroll(presenter)

font, font_size = self.config.layout.font("4x6")
def __render_final_inning(self, presenter):
text = "FINAL"
color = self.colors.graphics_color("final.inning")
coords = self.layout.coords("final.inning")
font, font_size = self.layout.font_for("final.inning")

graphics.DrawText(self.canvas, font, 0, 10, (255, 255, 255), game_text)
# TODO: No concept of a "scoreboard" yet
# if scoreboard.inning.number != NORMAL_GAME_LENGTH:
# text += " " + str(scoreboard.inning.number)

def _render_decision_scroll(self):
coords = self.manager.layout.coords("final.scrolling_text")
font, font_size = self.manager.layout.font("final.scrolling_text")
center_text = self.create_cached_object(
"postgame_center_text", CenteredText, self.canvas, coords.x, coords.y, font, font_size, color, text
)
center_text.render_text()

# TODO: Handle no-hitters
# if layout.state_is_nohitter():
# nohit_text = nohitter._get_nohitter_text(layout)
# nohit_coords = layout.coords("final.nohit_text")
# nohit_color = colors.graphics_color("final.nohit_text")
# nohit_font = layout.font("final.nohit_text")
# graphics.DrawText(canvas, nohit_font["font"], nohit_coords["x"], nohit_coords["y"], nohit_color, nohit_text)

# color = colors.graphics_color("final.scrolling_text")
# bgcolor = colors.graphics_color("default.background")
def __render_decision_scroll(self, presenter):
coords = self.layout.coords("final.scrolling_text")
font, font_size = self.layout.font_for("final.scrolling_text")

color = (255, 255, 255)
bgcolor = (0, 0, 0)
color = self.colors.graphics_color("final.scrolling_text")
bgcolor = self.colors.graphics_color("default.background")

scroll_text = "W: {} {}-{} L: {} {}-{}".format(
self.game.winning_pitcher,
self.game.winning_pitcher_wins,
self.game.winning_pitcher_losses,
self.game.losing_pitcher,
self.game.losing_pitcher_wins,
self.game.losing_pitcher_losses,
text = "W: {} {}-{} L: {} {}-{}".format(
presenter.winning_pitcher,
presenter.winning_pitcher_wins,
presenter.winning_pitcher_losses,
presenter.losing_pitcher,
presenter.losing_pitcher_wins,
presenter.losing_pitcher_losses,
)

if False and self.game.save_pitcher:
if presenter.save_pitcher:
scroll_text += " SV: {} ({})".format(self.game.save_pitcher, self.game.save_pitcher_saves)

# TODO: Playoffs
Expand All @@ -44,3 +65,19 @@ def _render_decision_scroll(self):
# return scrollingtext.render_text(
# canvas, coords["x"], coords["y"], coords["width"], font, color, bgcolor, scroll_text, text_pos
# )

scroller = self.create_cached_object(
"postgame_scroller",
ScrollingText,
self.canvas,
coords.x,
coords.y,
coords.width,
coords.width,
font,
font_size,
color,
bgcolor,
text,
)
scroller.render_text()
4 changes: 2 additions & 2 deletions rewrite/screens/screen_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from screens.weather import WeatherScreen
from screens.games.pregame import PregameScreen
from screens.games.live_game import LiveGameScreen
from screens.games.postgame import PostGameScreen
from screens.games.postgame import PostgameScreen


class ScreenManager:
Expand All @@ -18,7 +18,7 @@ class ScreenManager:
Screen.WEATHER: WeatherScreen,
Screen.PREGAME: PregameScreen,
Screen.LIVE_GAME: LiveGameScreen,
Screen.POSTGAME: PostGameScreen,
Screen.POSTGAME: PostgameScreen,
}

def __init__(self, matrix, canvas, config, queue):
Expand Down
1 change: 1 addition & 0 deletions rewrite/screens/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(self, *args, **kwargs):
self.manager.matrix.SetImage(logo.convert("RGB"))

def render(self):
# TODO: Clearing screen resets this image
pass

def ready_to_transition(self):
Expand Down
Loading

0 comments on commit 803eea9

Please sign in to comment.