-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
331 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
from utils import format_id, value_at_keypath | ||
|
||
class Pitches: | ||
|
||
# A list of mlb pitch types appearing in statcast | ||
# from statsapi.meta("pitchTypes") | ||
# Dont change the index, but feel free to change | ||
# the descriptions | ||
|
||
PITCH_LONG = { | ||
"AB": "Auto Ball", # MLB default is "Automatic Ball" | ||
"AS": "Auto Strike", # MLB default is "Automatic Strike" | ||
"CH": "Change-up", | ||
"CU": "Curveball", | ||
"CS": "Slow Curve", | ||
"EP": "Eephus", | ||
"FC": "Cutter", | ||
"FA": "Fastball", | ||
"FF": "Fastball", # MLB default is "Four-Seam Fastball" | ||
"FO": "Forkball", | ||
"FS": "Splitter", | ||
"FT": "2 Seamer", # MLB default is "Two-Seam Fastball" | ||
"GY": "Gyroball", | ||
"IN": "Int Ball", # MLB default is "Intentional Ball" | ||
"KC": "Knuckle Curve", | ||
"KN": "Knuckleball", | ||
"NP": "No Pitch", | ||
"PO": "Pitchout", | ||
"SC": "Screwball", | ||
"SI": "Sinker", | ||
"SL": "Slider", | ||
"ST": "Sweeper", | ||
"SV": "Slurve", | ||
"UN": "Unknown", | ||
} | ||
|
||
PITCH_SHORT = { | ||
"AB": "AB", | ||
"AS": "AS", | ||
"CH": "CH", | ||
"CU": "CU", | ||
"CS": "CS", | ||
"EP": "EP", | ||
"FC": "FC", | ||
"FA": "FA", | ||
"FF": "FF", | ||
"FO": "FO", | ||
"FS": "FS", | ||
"FT": "FT", | ||
"GY": "GY", | ||
"IN": "IN", | ||
"KC": "KC", | ||
"KN": "KN", | ||
"NP": "NP", | ||
"PO": "PO", | ||
"SC": "SC", | ||
"SI": "SI", | ||
"SL": "SL", | ||
"SV": "SV", | ||
"ST": "SW", # MLB default is "ST" | ||
"UN": "UN", | ||
} | ||
|
||
def __init__(self, game): | ||
self.game = game | ||
|
||
self.balls = self.balls() | ||
self.strikes = self.strikes() | ||
self.pitch_count = self.current_pitcher_pitch_count() | ||
|
||
last_pitch = self.last_pitch() | ||
self.last_pitch_speed = "0" | ||
self.last_pitch_type = Pitches.PITCH_SHORT["UN"] | ||
self.last_pitch_type_long = Pitches.PITCH_LONG["UN"] | ||
|
||
if last_pitch: | ||
self.last_pitch_speed = f"{round(last_pitch[0])}" | ||
self.last_pitch_type = Pitches.fetch_short(last_pitch[1]) | ||
self.last_pitch_type_long = Pitches.fetch_long(last_pitch[1]) | ||
|
||
def balls(self): | ||
return self.__fetch_count_part("balls") | ||
|
||
def strikes(self): | ||
return self.__fetch_count_part("strikes") | ||
|
||
def last_pitch(self): | ||
# TODO: Clean this up. | ||
try: | ||
play = self.game.data["liveData"]["plays"].get("currentPlay", {}).get("playEvents", [{}])[-1] | ||
if play.get("isPitch", False): | ||
return ( | ||
play["pitchData"].get("startSpeed", 0), | ||
play["details"]["type"]["code"], | ||
play["details"]["type"]["description"], | ||
) | ||
except: | ||
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: | ||
return 0 | ||
|
||
def __fetch_count_part(self, part): | ||
return value_at_keypath(self.game.data, "liveData.linescore").get(part, 0) | ||
|
||
@staticmethod | ||
def fetch_long(value): | ||
return Pitches.PITCH_LONG.get(value, Pitches.PITCH_LONG["UN"]) | ||
|
||
@staticmethod | ||
def fetch_short(value): | ||
return Pitches.PITCH_SHORT.get(value, Pitches.PITCH_SHORT["UN"]) | ||
|
||
def __str__(self) -> str: | ||
return ( | ||
f"Count: {self.balls} - {self.strikes}. " | ||
+ f"Last pitch: {self.last_pitch_speed}mph {self.last_pitch_type} {self.last_pitch_type_long} " | ||
+ f" Total pitches: {self.pitch_count}" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class LiveGamePresenter: | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from driver import graphics | ||
|
||
class Base: | ||
def __init__(self, number, screen): | ||
self.number = number | ||
self.screen = screen | ||
|
||
def render(self): | ||
color = self.colors.graphics_color(f"bases.{self.number}B") | ||
coords = self.layout.coords(f"bases.{self.number}B") | ||
|
||
self.__render_outline(coords, color) | ||
|
||
if self.game.man_on(self.number): | ||
self.__render_runner(coords, color) | ||
|
||
def __render_outline(self, coords, color): | ||
x, y = coords.x, coords.y | ||
size = coords.size | ||
half = abs(size // 2) | ||
|
||
graphics.DrawLine(self.canvas, x + half, y, x, y + half, color) | ||
graphics.DrawLine(self.canvas, x + half, y, x + size, y + half, color) | ||
graphics.DrawLine(self.canvas, x + half, y + size, x, y + half, color) | ||
graphics.DrawLine(self.canvas, x + half, y + size, x + size, y + half, color) | ||
|
||
def __render_runner(self, coords, color): | ||
x, y = coords.x, coords.y | ||
size = coords.size | ||
half = abs(size // 2) | ||
for offset in range(1, half + 1): | ||
graphics.DrawLine(self.canvas, x + half - offset, y + size - offset, x + half + offset, y + size - offset, color) | ||
graphics.DrawLine(self.canvas, x + half - offset, y + offset, x + half + offset, y + offset, color) | ||
|
||
@property | ||
def game(self): | ||
return self.screen.game | ||
|
||
@property | ||
def canvas(self): | ||
return self.screen.canvas | ||
|
||
@property | ||
def config(self): | ||
return self.screen.config | ||
|
||
@property | ||
def colors(self): | ||
return self.screen.colors | ||
|
||
@property | ||
def layout(self): | ||
return self.screen.layout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from driver import graphics | ||
|
||
from utils.graphics import DrawRect | ||
|
||
class Out: | ||
def __init__(self, number, screen): | ||
self.number = number | ||
self.screen = screen | ||
|
||
def render(self): | ||
color = self.colors.graphics_color(f"outs.{self.number}") | ||
coords = self.layout.coords(f"outs.{self.number}") | ||
|
||
if self.game.outs() >= self.number: | ||
self.__render_out(coords, color) | ||
else: | ||
self.__render_outline(coords, color) | ||
|
||
|
||
def __render_outline(self, coords, color): | ||
x, y, size = coords.x, coords.y, coords.size | ||
|
||
DrawRect(self.canvas, x, y, size, size, color, filled=False) | ||
|
||
def __render_out(self, coords, color): | ||
x, y, size = coords.x, coords.y, coords.size | ||
|
||
DrawRect(self.canvas, x, y, size, size, color, filled=True) | ||
|
||
@property | ||
def game(self): | ||
return self.screen.game | ||
|
||
@property | ||
def canvas(self): | ||
return self.screen.canvas | ||
|
||
@property | ||
def config(self): | ||
return self.screen.config | ||
|
||
@property | ||
def colors(self): | ||
return self.screen.colors | ||
|
||
@property | ||
def layout(self): | ||
return self.screen.layout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,52 @@ | ||
from driver import graphics | ||
|
||
from screens.games.base import GameScreen | ||
from screens.components.base import Base | ||
from screens.components.out import Out | ||
from presenters.live_game import LiveGamePresenter | ||
|
||
|
||
class LiveGameScreen(GameScreen): | ||
MAX_DURATION_SECONDS = 5 | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
self.bases = [ | ||
Base(1, self), | ||
Base(2, self), | ||
Base(3, self) | ||
] | ||
|
||
self.outs = [ | ||
Out(1, self), | ||
Out(2, self), | ||
Out(3, self), | ||
] | ||
|
||
def render(self): | ||
game_text = "It's a game!" | ||
presenter = self.create_cached_object("live_game_presenter", LiveGamePresenter, self.game, self.config) | ||
|
||
self.__render_bases() | ||
self.__render_outs() | ||
self.__render_count(presenter) | ||
|
||
# Overlay banners | ||
self.away_team_banner.render() | ||
self.home_team_banner.render() | ||
|
||
def __render_bases(self): | ||
for base in self.bases: | ||
base.render() | ||
|
||
font, font_size = self.config.layout.font("4x6") | ||
def __render_outs(self): | ||
for out in self.outs: | ||
out.render() | ||
|
||
graphics.DrawText(self.canvas, font, 0, 10, (255, 255, 255), game_text) | ||
def __render_count(self, presenter): | ||
text = presenter.batter_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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.