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/games.py b/games.py index a4f2a08..8371337 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): @@ -208,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 @@ -682,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() @@ -690,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): @@ -798,6 +817,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 +840,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 +889,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/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 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/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..265fe2f 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,16 +804,16 @@ 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 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.""" @@ -2095,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]) 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