Skip to content

Commit

Permalink
Render MLB Standings for a division
Browse files Browse the repository at this point in the history
See #4
  • Loading branch information
ajbowler committed Feb 3, 2018
1 parent 430c9d3 commit dc1346d
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 66 deletions.
50 changes: 15 additions & 35 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
from rgbmatrix import RGBMatrix
from utils import args, refresh_scoreboard, bump_counter
from utils import args
from renderer import render_games, render_standings
import datetime
import mlbgame
import time

args = args()

# TODO: Uncomment once the season starts, testing with random games for now
# games = mlbgame.games(year, month, day)[0]
games = mlbgame.games(2017, 5, 10)[0]

# Get the game to start on. If the provided team does not have a game today,
# or the team name isn't provided, then the first game in the list is used.
game_idx = 0
if args.team:
game_idx = next(
(i for i, game in enumerate(games) if game.away_team ==
args.team or game.home_team == args.team), 0
)
game = games[game_idx]

# Initialize the matrix and fill it in with a dark blue color
rgb = RGBMatrix()
matrix = rgb.CreateFrameCanvas()
matrix.Fill(7, 14, 25)

# Refresh the board every 15 seconds and rotate the games if the command flag is passed
starttime = time.time()
while True:
success = refresh_scoreboard(matrix, game)
matrix = rgb.SwapOnVSync(matrix)
time.sleep(15.0 - ((time.time() - starttime) % 15.0))
if args.rotate:
game_idx = bump_counter(game_idx, games, bool(args.rotate))
game = games[game_idx]
matrix.Fill(7, 14, 25)
matrix = RGBMatrix()
canvas = matrix.CreateFrameCanvas()
args = args()

if not success:
# TODO https://github.com/ajbowler/mlb-led-scoreboard/issues/13
continue
if args.standings:
standings = mlbgame.standings(datetime.datetime(2017, 9, 30))
division = next(division for division in standings.divisions if division.name == args.standings)
render_standings(matrix, canvas, division)
else:
# TODO: Uncomment once the season starts, testing with random games for now
# games = mlbgame.games(year, month, day)[0]
games = mlbgame.games(2017, 9, 30)[0]
render_games(matrix, canvas, games, args)
58 changes: 58 additions & 0 deletions renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from utils import refresh_scoreboard, bump_counter
from rgbmatrix import graphics
import time

def render_games(matrix, canvas, games, args):
# Get the game to start on. If the provided team does not have a game today,
# or the team name isn't provided, then the first game in the list is used.
game_idx = 0
if args.team:
game_idx = next(
(i for i, game in enumerate(games) if game.away_team ==
args.team or game.home_team == args.team), 0
)
game = games[game_idx]

# Refresh the board every 15 seconds and rotate the games if the command flag is passed
starttime = time.time()
canvas.Fill(7, 14, 25)
while True:
success = refresh_scoreboard(canvas, game)
canvas = matrix.SwapOnVSync(canvas)
time.sleep(15.0 - ((time.time() - starttime) % 15.0))
if args.rotate:
game_idx = bump_counter(game_idx, games, bool(args.rotate))
game = games[game_idx]
canvas.Fill(7, 14, 25)

if not success:
# TODO https://github.com/ajbowler/mlb-led-scoreboard/issues/13
continue

def render_standings(matrix, canvas, division):
font = graphics.Font()
font.LoadFont('Assets/tom-thumb.bdf')
text_color = graphics.Color(171, 181, 170)

canvas.Fill(37, 102, 30)

stat = 'w'
starttime = time.time()
while True:
offset = 6
for team in division.teams:
abbrev = '{:3s}'.format(team.team_abbrev)
text = '%s %s' % (abbrev, getattr(team, stat))
graphics.DrawText(canvas, font, 1, offset, text_color, text)

for x in range(0, canvas.width):
canvas.SetPixel(x, offset, 13, 25, 11)
for y in range(0, canvas.height):
canvas.SetPixel(14, y, 13, 35, 11)
offset += 6

matrix.SwapOnVSync(canvas)
time.sleep(5.0 - ((time.time() - starttime) % 5.0))

canvas.Fill(37, 102, 30)
stat = 'w' if stat == 'l' else 'l'
56 changes: 28 additions & 28 deletions scoreboard_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from rgbmatrix import graphics

class ScoreboardRenderer:
def __init__(self, matrix, scoreboard):
self.matrix = matrix
def __init__(self, canvas, scoreboard):
self.canvas = canvas
self.scoreboard = scoreboard
self.colors = json.load(open('Assets/colors.json'))

Expand All @@ -26,10 +26,10 @@ def __render_team_colors(self):
home_team_color = home_team_color_data['home']

scores_height = 14
for x in range(self.matrix.width):
for x in range(self.canvas.width):
for y in range(scores_height):
color = home_team_color if y >= scores_height / 2 else away_team_color
self.matrix.SetPixel(x, y, color['r'], color['g'], color['b'])
self.canvas.SetPixel(x, y, color['r'], color['g'], color['b'])

def __render_team_text(self):
away_team = self.scoreboard.game_data['away_team']
Expand All @@ -42,13 +42,13 @@ def __render_team_text(self):
home_text_color_graphic = graphics.Color(home_text_color['r'], home_text_color['g'], home_text_color['b'])
home_text = home_team.upper() + ' ' + str(self.scoreboard.game_data['inning']['at_bat']['home_team_runs'])

graphics.DrawText(self.matrix, self.font, 1, 6, away_text_color_graphic, away_text)
graphics.DrawText(self.matrix, self.font, 1, 13, home_text_color_graphic, home_text)
graphics.DrawText(self.canvas, self.font, 1, 6, away_text_color_graphic, away_text)
graphics.DrawText(self.canvas, self.font, 1, 13, home_text_color_graphic, home_text)

def __render_pitches(self):
at_bat = self.scoreboard.game_data['inning']['at_bat']
pitches_color = graphics.Color(255, 235, 59)
graphics.DrawText(self.matrix, self.font, 1, 23, pitches_color, str(at_bat['balls']) + '-' + str(at_bat['strikes']))
graphics.DrawText(self.canvas, self.font, 1, 23, pitches_color, str(at_bat['balls']) + '-' + str(at_bat['strikes']))

def __render_outs(self):
outs = self.scoreboard.game_data['inning']['at_bat']['outs']
Expand All @@ -60,7 +60,7 @@ def __render_outs(self):
self.__render_out_circle(out_px[out])
# Fill in the circle if that out has occurred
if (outs >= out):
self.matrix.SetPixel(out_px[out]['x'], out_px[out]['y'], 255, 235, 59)
self.canvas.SetPixel(out_px[out]['x'], out_px[out]['y'], 255, 235, 59)

def __render_bases(self):
bases = self.scoreboard.game_data['inning']['at_bat']['bases']
Expand All @@ -81,7 +81,7 @@ def __render_inning(self):
self.__render_inning_half(inning)
number = inning['number']
number_color = graphics.Color(255, 235, 59)
graphics.DrawText(self.matrix, self.font, 28, 20, number_color, str(number))
graphics.DrawText(self.canvas, self.font, 28, 20, number_color, str(number))

def __render_out_circle(self, out):
offset = 1
Expand All @@ -90,39 +90,39 @@ def __render_out_circle(self, out):
# The dead center is filled in only if that many outs has occurred, and happens elsewhere
if x == 0 and y == 0:
continue
self.matrix.SetPixel(out['x'] + x, out['y'] + y, 255, 235, 59)
self.canvas.SetPixel(out['x'] + x, out['y'] + y, 255, 235, 59)

def __render_base_outline(self, base):
# Hollow diamonds are a popular homework problem but IDGAF
self.matrix.SetPixel(base['x'] - 3, base['y'], 255, 235, 59)
self.matrix.SetPixel(base['x'] - 2, base['y'] - 1, 255, 235, 59)
self.matrix.SetPixel(base['x'] - 2, base['y'] + 1, 255, 235, 59)
self.matrix.SetPixel(base['x'] - 1, base['y'] - 2, 255, 235, 59)
self.matrix.SetPixel(base['x'] - 1, base['y'] + 2, 255, 235, 59)
self.matrix.SetPixel(base['x'], base['y'] - 3, 255, 235, 59)
self.matrix.SetPixel(base['x'], base['y'] + 3, 255, 235, 59)
self.matrix.SetPixel(base['x'] + 1, base['y'] - 2, 255, 235, 59)
self.matrix.SetPixel(base['x'] + 1, base['y'] + 2, 255, 235, 59)
self.matrix.SetPixel(base['x'] + 2, base['y'] - 1, 255, 235, 59)
self.matrix.SetPixel(base['x'] + 2, base['y'] + 1, 255, 235, 59)
self.matrix.SetPixel(base['x'] + 3, base['y'], 255, 235, 59)
self.canvas.SetPixel(base['x'] - 3, base['y'], 255, 235, 59)
self.canvas.SetPixel(base['x'] - 2, base['y'] - 1, 255, 235, 59)
self.canvas.SetPixel(base['x'] - 2, base['y'] + 1, 255, 235, 59)
self.canvas.SetPixel(base['x'] - 1, base['y'] - 2, 255, 235, 59)
self.canvas.SetPixel(base['x'] - 1, base['y'] + 2, 255, 235, 59)
self.canvas.SetPixel(base['x'], base['y'] - 3, 255, 235, 59)
self.canvas.SetPixel(base['x'], base['y'] + 3, 255, 235, 59)
self.canvas.SetPixel(base['x'] + 1, base['y'] - 2, 255, 235, 59)
self.canvas.SetPixel(base['x'] + 1, base['y'] + 2, 255, 235, 59)
self.canvas.SetPixel(base['x'] + 2, base['y'] - 1, 255, 235, 59)
self.canvas.SetPixel(base['x'] + 2, base['y'] + 1, 255, 235, 59)
self.canvas.SetPixel(base['x'] + 3, base['y'], 255, 235, 59)

def __render_baserunner(self, base):
offset = 2
for x in range(-offset, offset + 1):
for y in range(-offset, offset + 1):
if abs(x) == offset and abs(y) == offset:
continue
self.matrix.SetPixel(base['x'] + x, base['y'] + y, 255, 235, 59)
self.canvas.SetPixel(base['x'] + x, base['y'] + y, 255, 235, 59)

def __render_inning_half(self, inning):
tri_px = {'x': 24, 'y': 17}
offset = 2
for x in range(-offset, offset + 1):
self.matrix.SetPixel(tri_px['x'] + x, tri_px['y'], 255, 235, 59)
self.canvas.SetPixel(tri_px['x'] + x, tri_px['y'], 255, 235, 59)

offset = 1 if inning['bottom'] else -1
self.matrix.SetPixel(tri_px['x'] - 1, tri_px['y'] + offset, 255, 235, 59)
self.matrix.SetPixel(tri_px['x'], tri_px['y'] + offset, 255, 235, 59)
self.matrix.SetPixel(tri_px['x'] + 1, tri_px['y'] + offset, 255, 235, 59)
self.matrix.SetPixel(tri_px['x'], tri_px['y'] + offset + offset, 255, 235, 59)
self.canvas.SetPixel(tri_px['x'] - 1, tri_px['y'] + offset, 255, 235, 59)
self.canvas.SetPixel(tri_px['x'], tri_px['y'] + offset, 255, 235, 59)
self.canvas.SetPixel(tri_px['x'] + 1, tri_px['y'] + offset, 255, 235, 59)
self.canvas.SetPixel(tri_px['x'], tri_px['y'] + offset + offset, 255, 235, 59)
8 changes: 5 additions & 3 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
from scoreboard_renderer import ScoreboardRenderer
import argparse

def refresh_scoreboard(matrix, game):
def refresh_scoreboard(canvas, game):
scoreboard = Scoreboard(game)
if not scoreboard.game_data:
return False
renderer = ScoreboardRenderer(matrix, scoreboard)
renderer = ScoreboardRenderer(canvas, scoreboard)
renderer.render()
return True

Expand All @@ -21,5 +21,7 @@ def args():
parser.add_argument(
'-t', '--team', help='Pick a team to display a game for. Example: "Cubs"')
parser.add_argument(
'-r', '--rotate', help="Rotate through each game of the day every 15 seconds", action='store_true')
'-r', '--rotate', help='Rotate through each game of the day every 15 seconds', action='store_true')
parser.add_argument(
'-s', '--standings', help='Display standings for the provided division. Example: "NL Central"', metavar="division")
return parser.parse_args()

0 comments on commit dc1346d

Please sign in to comment.