From 0953f0547102d4ee3c3a61f833633205c8e2ab47 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Sun, 27 Jun 2021 19:15:56 -0400 Subject: [PATCH 1/5] added real player names to draft pool (slow) and fixed #238 --- .gitignore | 1 + real_players.py | 24 ++++++++++++++++++++++++ the-prestige.pyproj | 3 +++ the_draft.py | 19 ++++++++++++------- the_prestige.py | 32 +++++++++++++++++++++++--------- 5 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 real_players.py diff --git a/.gitignore b/.gitignore index 473c2b0..ce115fc 100644 --- a/.gitignore +++ b/.gitignore @@ -358,3 +358,4 @@ env /data/leagues /simmadome/build /simmadome/.eslintcache +/matteo_env/Lib/site-packages diff --git a/real_players.py b/real_players.py new file mode 100644 index 0000000..c118231 --- /dev/null +++ b/real_players.py @@ -0,0 +1,24 @@ +from lxml import html +import requests, json +import onomancer as ono + + +def random_real_player(): + name = [] + while name == []: + page = requests.get("http://www.baseball-reference.com/rand.fcgi") + tree = html.fromstring(page.content) + name = tree.xpath("//h1[@itemprop='name']/span/text()") + if len(name) > 0 and len(name[0]) == 4: + name = [] #gets rid of years + name = name[0] + return name + +def get_real_players(num): + names = [] + for i in range(0, num): + names.append(random_real_player()) + players = {} + for name in names: + players[name] = json.loads(ono.get_stats(name)) + return players \ No newline at end of file diff --git a/the-prestige.pyproj b/the-prestige.pyproj index e9f0b7a..ce3561e 100644 --- a/the-prestige.pyproj +++ b/the-prestige.pyproj @@ -46,6 +46,9 @@ Code + + Code + diff --git a/the_draft.py b/the_draft.py index 15504c9..13771d2 100644 --- a/the_draft.py +++ b/the_draft.py @@ -2,7 +2,7 @@ from collections import namedtuple import games import json import uuid - +import real_players import onomancer Participant = namedtuple('Participant', ['handle', 'team']) @@ -16,19 +16,22 @@ class Draft: """ @classmethod - def make_draft(cls, teamsize, draftsize, minsize, pitchers): - draft = cls(teamsize, draftsize, minsize, pitchers) + def make_draft(cls, teamsize, draftsize, minsize, pitchers, ono_ratio): + draft = cls(teamsize, draftsize, minsize, pitchers, ono_ratio) return draft - def __init__(self, teamsize, draftsize, minsize, pitchers): - self.DRAFT_SIZE = draftsize + def __init__(self, teamsize, draftsize, minsize, pitchers, ono_ratio): + self.DRAFT_SIZE = int(draftsize * ono_ratio) + self.REAL_SIZE = draftsize - self.DRAFT_SIZE self.REFRESH_DRAFT_SIZE = minsize # fewer players remaining than this and the list refreshes self.DRAFT_ROUNDS = teamsize self.pitchers = pitchers self._id = str(uuid.uuid4())[:6] self._participants = [] self._active_participant = BOOKMARK # draft mutex - self._players = onomancer.get_names(limit=self.DRAFT_SIZE) + nameslist = onomancer.get_names(limit=self.DRAFT_SIZE) + nameslist.update(real_players.get_real_players(self.REAL_SIZE)) + self._players = nameslist self._round = 0 @property @@ -68,7 +71,9 @@ class Draft: self.advance_draft() def refresh_players(self): - self._players = onomancer.get_names(limit=self.DRAFT_SIZE) + nameslist = onomancer.get_names(limit=self.DRAFT_SIZE) + nameslist.update(real_players.get_real_players(self.REAL_SIZE)) + self._players = nameslist def advance_draft(self): """ diff --git a/the_prestige.py b/the_prestige.py index 9728df8..1db0192 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1,4 +1,4 @@ -import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues, datetime, gametext +import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues, datetime, gametext, real_players import database as db import onomancer as ono from league_storage import league_exists, season_save, season_restart @@ -646,10 +646,12 @@ class StartDraftCommand(Command): draftsize = 20 minsize = 4 pitchers = 3 + ono_ratio = 0.5 + timeout = 120 for flag in flags: try: - if flag[0] == "t": + if flag[0] == "t": teamsize = int(flag[1]) elif flag[0] == "d": draftsize = int(flag[1]) @@ -657,10 +659,16 @@ class StartDraftCommand(Command): minsize = int(flag[1]) elif flag[0] == "p": pitchers = int(flag[1]) + elif flag[0] == "c": + ono_ratio = float(flag[1]) + if ono_ratio > 1 or ono_ratio < 0: + raise CommandError("The Chaos value needs to be between 0 and 1, chief. Probability has rules.") + elif flag[0] == "w": #wait + timeout = int(flag[1]) else: raise CommandError(f"We don't recognize that {flag[0]} flag.") except ValueError: - raise CommandError(f"Your {flag[0]} flag isn't a valid integer, boss.") + raise CommandError(f"Your {flag[0]} flag isn't a valid nummber, boss.") if teamsize-pitchers > 20 or pitchers > 8: raise CommandError("You can't fit that many players on a team, chief. Slow your roll.") @@ -669,7 +677,13 @@ class StartDraftCommand(Command): if draftsize > 40: raise CommandError("40 players is the max. We're not too confident about pushing for more.") - draft = Draft.make_draft(teamsize, draftsize, minsize, pitchers) + await msg.channel.send("Got it, boss. Give me a sec to find all the paperwork.") + + try: + draft = Draft.make_draft(teamsize, draftsize, minsize, pitchers, ono_ratio) + except ConnectionError: + await msg.channel.send("Baseball Reference isn't playing nice. Could you try again for us in a minute or so?") + mentions = {f'<@!{m.id}>' for m in msg.mentions} content = msg.content.split('\n')[1:] # drop command out of message if not content or len(content) % 3: @@ -683,7 +697,7 @@ class StartDraftCommand(Command): handle = mention break else: - await msg.channel.send(f"I don't recognize {handle_token}") + await msg.channel.send(f"I don't recognize {handle_token}.") return team_name = content[i + 1].strip() if games.get_team(team_name): @@ -727,7 +741,7 @@ class StartDraftCommand(Command): embed=build_draft_embed(draft.get_draftees(), footer=footer), ) try: - draft_message = await self.wait_draft(msg.channel, draft) + draft_message = await self.wait_draft(msg.channel, draft, timeout) draft.draft_player(f'<@!{draft_message.author.id}>', draft_message.content.split(' ', 1)[1]) except SlowDraftError: player = random.choice(draft.get_draftees()) @@ -777,7 +791,7 @@ class StartDraftCommand(Command): return False return False - async def wait_draft(self, channel, draft): + async def wait_draft(self, channel, draft, timeout): def check(m): if m.channel != channel: @@ -790,9 +804,9 @@ class StartDraftCommand(Command): return False try: - draft_message = await client.wait_for('message', timeout=120.0, check=check) + draft_message = await client.wait_for('message', timeout=timeout, check=check) except asyncio.TimeoutError: - raise SlowDraftError('Too slow') + raise SlowDraftError('Too slow, boss.') return draft_message From ced7fa1eb145d5f572eef68393da0d07ef35cc8d Mon Sep 17 00:00:00 2001 From: Sakimori Date: Sun, 27 Jun 2021 19:16:44 -0400 Subject: [PATCH 2/5] fixes #261 --- the_prestige.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the_prestige.py b/the_prestige.py index 1db0192..09c7d0a 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -813,7 +813,7 @@ class StartDraftCommand(Command): class StartLeagueCommand(Command): name = "startleague" template = "m;startleague [league name]\n[games per hour]" - description = """Optional flags for the first line: `--queue X` or `-q X` to play X number of series before stopping; `--noautopostseason` will pause the league before starting postseason. + description = """Optional flags for the first line: `--queue X` or `-q X` to play X number of series before stopping; `--autopostseason` will automatically start postseason at the end of the season. Starts games from a league with a given name, provided that league has been saved on the website and has been claimed using claimleague. The games per hour sets how often the games will start (e.g. GPH 2 will start games at X:00 and X:30). By default it will play the entire season followed by the postseason and then stop but this can be customized using the flags. Not every team will play every series, due to how the scheduling algorithm is coded but it will all even out by the end.""" From fffcb47054e2b81eec6d9ecb308115c9f54117e2 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Tue, 29 Jun 2021 16:44:33 -0400 Subject: [PATCH 3/5] fixed the FC duplicating batters in text --- gametext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gametext.py b/gametext.py index 7e8146a..9958581 100644 --- a/gametext.py +++ b/gametext.py @@ -45,7 +45,7 @@ class game_strings_base(object): twoparts = [] - diff_formats = {fielderschoice[0]: ("batter", "base_string"), + diff_formats = {fielderschoice[0]: ("runner", "base_string"), steal_success[0]: ("runner", "base_string"), steal_caught[0]: ("runner", "base_string", "defender")} no_formats = strikeoutlooking + strikeoutswinging + doubleplay + walk + single + double + triple + homerun + grandslam From f73e0590f4b18c7a6e017411bc1d586315bc721e Mon Sep 17 00:00:00 2001 From: Sakimori Date: Wed, 30 Jun 2021 16:03:33 -0400 Subject: [PATCH 4/5] added easter egg --- games.py | 20 ++++++++++++++++++++ the_prestige.py | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/games.py b/games.py index a4f2a08..6c95f14 100644 --- a/games.py +++ b/games.py @@ -50,6 +50,8 @@ class player(object): "strikeouts_taken" : 0 } self.stat_name = self.name + if self.name == "Tim Locastro": + self.randomize_stars() def star_string(self, key): str_out = "" @@ -68,6 +70,15 @@ class player(object): def __str__(self): return self.name + def randomize_stars(self): + for key in ["batting_stars", "pitching_stars", "baserunning_stars", "defense_stars"]: + #random star value between 0 and 6.5 + stars = random.randint(0,6) + half_star = random.random() < 0.5 #half star addition + if half_star: + stars = half_star + 0.5 + self.stlats[key] = stars + class team(object): def __init__(self): @@ -798,6 +809,9 @@ def get_team(name): team_json.rotation.append(team_json.pitcher) team_json.pitcher = None update_team(team_json) + for player in team_json.rotation + team_json.lineup: + if player.name == "Tim Locastro": + player.randomize_stars() return team_json return None except AttributeError: @@ -818,6 +832,9 @@ def get_team_and_owner(name): team_json.rotation.append(team_json.pitcher) team_json.pitcher = None update_team(team_json) + for player in team_json.rotation + team_json.lineup: + if player.name == "Tim Locastro": + player.randomize_stars() return (team_json, owner_id) return None except AttributeError: @@ -864,6 +881,9 @@ def search_team(search_term): team_json.rotation.append(team_json.pitcher) team_json.pitcher = None update_team(team_json) + for player in team_json.rotation + team_json.lineup: + if player.name == "Tim Locastro": + player.randomize_stars() except AttributeError: team_json.rotation = [] team_json.rotation.append(team_json.pitcher) diff --git a/the_prestige.py b/the_prestige.py index 09c7d0a..265fe2f 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -2109,6 +2109,16 @@ def build_team_embed(team): def build_star_embed(player_json): starkeys = {"batting_stars" : "Batting", "pitching_stars" : "Pitching", "baserunning_stars" : "Baserunning", "defense_stars" : "Defense"} embed = discord.Embed(color=discord.Color.purple(), title=player_json["name"]) + + if player_json["name"] == "Tim Locastro": #the tim easter egg + for key in ["batting_stars", "pitching_stars", "baserunning_stars", "defense_stars"]: + #random star value between 0 and 6.5 + stars = random.randint(0,6) + half_star = random.random() < 0.5 #half star addition + if half_star: + stars = half_star + 0.5 + player_json[key] = stars + for key in starkeys.keys(): embedstring = "" starstring = str(player_json[key]) From 99593483223a83a53f69712ba4a1ae8970a5cbef Mon Sep 17 00:00:00 2001 From: Sakimori Date: Wed, 30 Jun 2021 17:01:19 -0400 Subject: [PATCH 5/5] implemented smog --- games.py | 24 ++++++++++++++++-------- main_controller.py | 8 ++++++-- weather.py | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/games.py b/games.py index 6c95f14..8371337 100644 --- a/games.py +++ b/games.py @@ -219,6 +219,7 @@ class game(object): def __init__(self, team1, team2, length=None): self.over = False + self.random_weather_flag = False self.teams = {"away" : team1, "home" : team2} self.inning = 1 self.outs = 0 @@ -693,6 +694,9 @@ class game(object): self.top_of_inning = not self.top_of_inning + if self.random_weather_flag and self.top_of_inning: + setattr(self, "weather", random.choice(list(weather.safe_weathers().values()))(self)) + self.weather.on_flip_inning(self) self.choose_next_batter() @@ -701,14 +705,18 @@ class game(object): self.inning += 1 if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over self.over = True - if self.max_innings >= 9 or self.weather.name in ["Leaf Eddies", "Torrential Downpour"]: - if self.teams["home"].score == 16: - this_xvi_team = self.teams["home"] - elif self.teams["away"].score == 16: - this_xvi_team = self.teams["away"] - else: - this_xvi_team = None - db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"], xvi_team=this_xvi_team) + try: #if something goes wrong with OBL don't erase game + if self.max_innings >= 9 or self.weather.name in ["Leaf Eddies", "Torrential Downpour"]: + if self.teams["home"].score == 16: + this_xvi_team = self.teams["home"] + elif self.teams["away"].score == 16: + this_xvi_team = self.teams["away"] + else: + this_xvi_team = None + db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"], xvi_team=this_xvi_team) + except: + pass + def end_of_game_report(self): diff --git a/main_controller.py b/main_controller.py index 95d4f82..2cf6db1 100644 --- a/main_controller.py +++ b/main_controller.py @@ -190,13 +190,17 @@ def update_loop(): else: if this_game.top_of_inning: state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" + this_game.weather.modify_top_of_inning_message(this_game, state) + if this_game.random_weather_flag: + this_game.weather.weather_report(this_game, state) else: if this_game.inning >= this_game.max_innings: if this_game.teams["home"].score > this_game.teams["away"].score: this_game.victory_lap = True state["update_text"] = f"Bottom of {this_game.inning}. {this_game.teams['home'].name} batting!" + this_game.weather.modify_top_of_inning_message(this_game, state) - this_game.weather.modify_top_of_inning_message(this_game, state) + elif state["update_pause"] != 1 and this_game.play_has_begun: @@ -256,4 +260,4 @@ def update_loop(): socket_thread = threading.Thread(target=socketio.emit, args=("states_update", game_states)) socket_thread.start() #socketio.emit("states_update", game_states) - time.sleep(8) \ No newline at end of file + time.sleep(3) \ No newline at end of file diff --git a/weather.py b/weather.py index b4cf74e..1049cd7 100644 --- a/weather.py +++ b/weather.py @@ -57,6 +57,11 @@ class Weather: def modify_game_end_message(self, game, state): pass + def weather_report(self, game, state): + game.weather = random.choice(list(safe_weathers().values()))(game) + state["update_emoji"] = "🚌" + state["update_text"] += f" Weather report: {game.weather.name} {game.weather.emoji}" + class Supernova(Weather): name = "Supernova" @@ -545,6 +550,18 @@ class LeafEddies(Weather): if game.inning == 1: state["weather_text"] = self.name +class Smog(Weather): + name = "Smog" + emoji = "🚌" + duration_range = [1,1] + + def __init__(self, game): + game.random_weather_flag = True + setattr(game, "weather", random.choice(list(safe_weathers().values()))(game)) + pass + + + def all_weathers(): weathers_dic = { "Supernova" : Supernova, @@ -562,7 +579,26 @@ def all_weathers(): "Tornado" : Tornado, "Torrential Downpour" : Downpour, "Summer Mist" : SummerMist, - "Leaf Eddies" : LeafEddies + "Leaf Eddies" : LeafEddies, + "Smog" : Smog + } + return weathers_dic + +def safe_weathers(): + """weathers safe to swap in mid-game""" + weathers_dic = { + "Supernova" : Supernova, + "Midnight": Midnight, + "Slight Tailwind": SlightTailwind, + "Twilight" : Twilight, + "Thinned Veil" : ThinnedVeil, + "Drizzle" : Drizzle, + "Breezy": Breezy, + "Starlight" : Starlight, + "Meteor Shower" : MeteorShower, + "Hurricane" : Hurricane, + "Tornado" : Tornado, + "Summer Mist" : SummerMist } return weathers_dic