From e478589b8ece2a4ec538374b1e4433e7b67a314b Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 03:03:25 -0500 Subject: [PATCH 01/15] Begin moving weather to separate files --- games.py | 131 +++++----------------- gametext.py | 27 +++++ main_controller.py | 6 +- the_prestige.py | 8 +- weather.py | 273 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 334 insertions(+), 111 deletions(-) create mode 100644 gametext.py create mode 100644 weather.py diff --git a/games.py b/games.py index f12b787..42b3473 100644 --- a/games.py +++ b/games.py @@ -1,6 +1,7 @@ import json, random, os, math, jsonpickle -from enum import Enum import database as db +import weather +from gametext import base_string, appearance_outcomes data_dir = "data" games_config_file = os.path.join(data_dir, "games_config.json") @@ -28,33 +29,6 @@ def config(): with open(games_config_file) as config_file: return json.load(config_file) -def all_weathers(): - weathers_dic = { - #"Supernova" : weather("Supernova", "🌟"), - #"Midnight": weather("Midnight", "🕶"), - #"Slight Tailwind": weather("Slight Tailwind", "🏌️‍♀️"), - "Heavy Snow": weather("Heavy Snow", "❄"), - "Twilight" : weather("Twilight", "👻"), - "Thinned Veil" : weather("Thinned Veil", "🌌"), - "Heat Wave" : weather("Heat Wave", "🌄"), - "Drizzle" : weather("Drizzle", "🌧") - } - return weathers_dic - -class appearance_outcomes(Enum): - strikeoutlooking = "strikes out looking." - strikeoutswinging = "strikes out swinging." - groundout = "grounds out to" - flyout = "flies out to" - fielderschoice = "reaches on fielder's choice. {} is out at {} base." #requires .format(player, base_string) - doubleplay = "grounds into a double play!" - sacrifice = "hits a sacrifice fly towards" - walk = "draws a walk." - single = "hits a single!" - double = "hits a double!" - triple = "hits a triple!" - homerun = "hits a dinger!" - grandslam = "hits a grand slam!" class player(object): @@ -248,19 +222,22 @@ class game(object): else: self.max_innings = config()["default_length"] self.bases = {1 : None, 2 : None, 3 : None} - self.weather = weather("Sunny","🌞") + self.weather = weather.Weather() + self.current_batter = None - def get_batter(self): + def choose_next_batter(self): if self.top_of_inning: bat_team = self.teams["away"] - counter = self.weather.counter_away else: bat_team = self.teams["home"] - counter = self.weather.counter_home - if self.weather.name == "Heavy Snow" and counter == bat_team.lineup_position: - return bat_team.pitcher - return bat_team.lineup[bat_team.lineup_position % len(bat_team.lineup)] + self.current_batter = bat_team.lineup[bat_team.lineup_position % len(bat_team.lineup)] + self.weather.on_choose_next_batter(self) + + def get_batter(self): + if self.current_batter == None: + self.choose_next_batter() + return self.current_batter def get_pitcher(self): if self.top_of_inning: @@ -286,8 +263,8 @@ class game(object): bat_stat = random_star_gen("batting_stars", batter) pitch_stat = random_star_gen("pitching_stars", pitcher) - if weather.name == "Supernova": - pitch_stat = pitch_stat * 0.9 + + bat_stat, pitch_stat = self.weather.modify_stats_preroll(bat_stat, pitch_stat) pb_system_stat = (random.gauss(1*math.erf((bat_stat - pitch_stat)*1.5)-1.8,2.2)) hitnum = random.gauss(2*math.erf(bat_stat/4)-1,3) @@ -533,25 +510,19 @@ class game(object): def batterup(self): scores_to_add = 0 result = self.at_bat() + + self.weather.activate(self, result) # possibly modify result in-place + + if "text_only" in result: + return (result, 0) + if self.top_of_inning: offense_team = self.teams["away"] - weather_count = self.weather.counter_away defense_team = self.teams["home"] else: offense_team = self.teams["home"] - weather_count = self.weather.counter_home defense_team = self.teams["away"] - if self.weather.name == "Slight Tailwind" and "mulligan" not in self.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk: - mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1 - if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5: - result["mulligan"] = True - return (result, 0) - - if self.weather.name == "Heavy Snow" and weather_count == offense_team.lineup_position and "snow_atbat" not in self.last_update[0].keys(): - result["snow_atbat"] = True - result["text"] = f"{offense_team.lineup[offense_team.lineup_position % len(offense_team.lineup)].name}'s hands are too cold! {self.get_batter().name} is forced to bat!" - return (result, 0) defenders = defense_team.lineup.copy() defenders.append(defense_team.pitcher) @@ -570,8 +541,6 @@ class game(object): elif result["text"] == appearance_outcomes.homerun or result["text"] == appearance_outcomes.grandslam: self.get_batter().game_stats["total_bases"] += 4 self.get_batter().game_stats["home_runs"] += 1 - if self.weather.name == "Thinned Veil": - result["veil"] = True @@ -636,27 +605,10 @@ class game(object): self.get_batter().game_stats["rbis"] += scores_to_add self.get_pitcher().game_stats["runs_allowed"] += scores_to_add offense_team.lineup_position += 1 #put next batter up + self.choose_next_batter() if self.outs >= 3: self.flip_inning() - if self.weather.name == "Heat Wave": - if self.top_of_inning: - self.weather.home_pitcher = self.get_pitcher() - if self.inning >= self.weather.counter_home: - self.weather.counter_home = self.weather.counter_home - (self.weather.counter_home % 5) + 5 + random.randint(1,4) #rounds down to last 5, adds up to next 5. then adds a random number 2<=x<=5 to determine next pitcher - tries = 0 - while self.get_pitcher() == self.weather.home_pitcher and tries < 3: - self.teams["home"].set_pitcher(use_lineup = True) - tries += 1 - - - else: - self.weather.away_pitcher = self.get_pitcher() - if self.inning >= self.weather.counter_away: - self.weather.counter_away = self.weather.counter_away - (self.weather.counter_away % 5) + 5 + random.randint(1,4) - tries = 0 - while self.get_pitcher() == self.weather.away_pitcher and tries < 3: - self.teams["away"].set_pitcher(use_lineup = True) - tries += 1 + return (result, scores_to_add) #returns ab information and scores @@ -665,22 +617,17 @@ class game(object): for base in self.bases.keys(): self.bases[base] = None self.outs = 0 - if self.top_of_inning and self.weather.name == "Heavy Snow" and self.weather.counter_away < self.teams["away"].lineup_position: - self.weather.counter_away = self.pitcher_insert(self.teams["away"]) + + self.top_of_inning = not self.top_of_inning + + self.weather.on_flip_inning(self) + + self.choose_next_batter() if not self.top_of_inning: - if self.weather.name == "Heavy Snow" and self.weather.counter_home < self.teams["home"].lineup_position: - self.weather.counter_home = self.pitcher_insert(self.teams["home"]) self.inning += 1 if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over self.over = True - self.top_of_inning = not self.top_of_inning - - if self.weather.name == "Drizzle": - if self.top_of_inning: - self.bases[2] = self.teams["away"].lineup[(self.teams["away"].lineup_position-1) % len(self.teams["away"].lineup)] - else: - self.bases[2] = self.teams["home"].lineup[(self.teams["home"].lineup_position-1) % len(self.teams["home"].lineup)] def pitcher_insert(self, this_team): rounds = math.ceil(this_team.lineup_position / len(this_team.lineup)) @@ -868,25 +815,3 @@ def search_team(search_term): teams.append(team_json) return teams -def base_string(base): - if base == 1: - return "first" - elif base == 2: - return "second" - elif base == 3: - return "third" - elif base == 4: - return "None" - -class weather(object): - name = "Sunny" - emoji = "🌞" - - def __init__(self, new_name, new_emoji): - self.name = new_name - self.emoji = new_emoji + "\uFE00" - self.counter_away = 0 - self.counter_home = 0 - - def __str__(self): - return f"{self.emoji} {self.name}" diff --git a/gametext.py b/gametext.py new file mode 100644 index 0000000..d7ec310 --- /dev/null +++ b/gametext.py @@ -0,0 +1,27 @@ +from enum import Enum + +class appearance_outcomes(Enum): + strikeoutlooking = "strikes out looking." + strikeoutswinging = "strikes out swinging." + groundout = "grounds out to" + flyout = "flies out to" + fielderschoice = "reaches on fielder's choice. {} is out at {} base." #requires .format(player, base_string) + doubleplay = "grounds into a double play!" + sacrifice = "hits a sacrifice fly towards" + walk = "draws a walk." + single = "hits a single!" + double = "hits a double!" + triple = "hits a triple!" + homerun = "hits a dinger!" + grandslam = "hits a grand slam!" + +def base_string(base): + if base == 1: + return "first" + elif base == 2: + return "second" + elif base == 3: + return "third" + elif base == 4: + return "None" + diff --git a/main_controller.py b/main_controller.py index 176c28f..b513322 100644 --- a/main_controller.py +++ b/main_controller.py @@ -174,7 +174,7 @@ def update_loop(): state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" if this_game.weather.name == "Drizzle": state["update_emoji"] = "🌧" - state["update_text"] += f' Due to inclement weather, {this_game.teams["away"].lineup[(this_game.teams["away"].lineup_position-1) % len(this_game.teams["away"].lineup)].name} is placed on second base.' + state["update_text"] += f'Due to inclement weather, {this_game.teams["away"].lineup[(this_game.teams["away"].lineup_position-1) % len(this_game.teams["away"].lineup)].name} is placed on second base.' elif this_game.weather.name == "Heat Wave" and hasattr(this_game.weather, "home_pitcher") and this_game.weather.home_pitcher.name != state["pitcher"]: state["update_emoji"] = "🌄" state["update_text"] += f' {this_game.weather.home_pitcher} is exhausted from the heat. {state["pitcher"]} is forced to pitch!' @@ -234,7 +234,7 @@ def update_loop(): if "veil" in this_game.last_update[0].keys(): state["update_emoji"] = "🌌" - state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {games.base_string(this_game.last_update[1])} base." + state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {gametext.base_string(this_game.last_update[1])} base." elif "error" in this_game.last_update[0].keys(): state["update_emoji"] = "👻" state["update_text"] = f"{this_game.last_update[0]['batter']}'s hit goes ethereal, and {this_game.last_update[0]['defender']} can't catch it! {this_game.last_update[0]['batter']} reaches base safely." @@ -261,4 +261,4 @@ def update_loop(): state["update_pause"] -= 1 socketio.emit("states_update", game_states) - time.sleep(8) \ No newline at end of file + time.sleep(8) diff --git a/the_prestige.py b/the_prestige.py index 1003b1a..6d63519 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -5,6 +5,7 @@ from league_storage import league_exists, season_save, season_restart from the_draft import Draft, DRAFT_ROUNDS from flask import Flask from uuid import uuid4 +import weather data_dir = "data" config_filename = os.path.join(data_dir, "config.json") @@ -1396,8 +1397,8 @@ async def watch_game(channel, newgame, user = None, league = None): def prepare_game(newgame, league = None, weather_name = None): if weather_name is None: - weathers = games.all_weathers() - newgame.weather = weathers[random.choice(list(weathers.keys()))] + weathers = weather.all_weathers() + newgame.weather = weathers[random.choice(list(weathers.keys()))]() state_init = { "away_name" : newgame.teams['away'].name, @@ -1420,9 +1421,6 @@ def prepare_game(newgame, league = None, weather_name = None): if newgame.weather.name == "Heavy Snow": newgame.weather.counter_away = random.randint(0,len(newgame.teams['away'].lineup)-1) newgame.weather.counter_home = random.randint(0,len(newgame.teams['home'].lineup)-1) - elif newgame.weather.name == "Heat Wave": - newgame.weather.counter_away = random.randint(2,4) - newgame.weather.counter_home = random.randint(2,4) return newgame, state_init async def start_tournament_round(channel, tourney, seeding = None): diff --git a/weather.py b/weather.py new file mode 100644 index 0000000..4087492 --- /dev/null +++ b/weather.py @@ -0,0 +1,273 @@ +import random +from gametext import appearance_outcomes + +class Weather: + def __init__(self): + self.name = "Sunny" + self.emoji = "🌞" + "\uFE00" + self.counter_away = 0 + self.counter_home = 0 + + def __str__(self): + return f"{self.emoji} {self.name}" + + def activate(self, game, result): + # activates after the batter calculation. modify result, or just return another thing + pass + + def modify_stats_preroll(self, bat_stat, pitch_stat): # ugly + # Activates before batting and pitch + return bat_stat, pitch_stat + + def on_flip_inning(self, game): + pass + + def on_choose_next_batter(self, game): + pass + +class Supernova(Weather): # todo + def __init__(self): + super().__init__() + self.name = "Supernova" + self.emoji = "🌟" + "\uFE00" + def activate(self, game, result): + pass + + def modify_stats_preroll(self, bat_stat, pitch_stat): + if self.weather.name == "Supernova": + pitch_stat = pitch_stat * 0.9 + +class Midnight(Weather): # todo + def __init__(self): + super().__init__() + self.name = "Midnight" + self.emoji = "🕶" + "\uFE00" + def activate(self, game, result): + pass + +class SlightTailwind(Weather): + def __init__(self): + super().__init__() + self.name = "Slight Tailwind" + self.emoji = "🏌️‍♀️" + "\uFE00" + def activate(self, game, result): + + if game.top_of_inning: + offense_team = game.teams["away"] + weather_count = self.counter_away + defense_team = game.teams["home"] + else: + offense_team = game.teams["home"] + weather_count = self.counter_home + defense_team = game.teams["away"] + + if "mulligan" not in game.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk: + mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1 + if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5: + result["mulligan"] = True + +class HeavySnow(Weather): + def __init__(self): + super().__init__() + self.name = "Heavy Snow" + self.emoji = "❄" + "\uFE00" + + def activate(self, game, result): + if game.top_of_inning: + offense_team = game.teams["away"] + weather_count = self.counter_away + else: + offense_team = game.teams["home"] + weather_count = self.counter_home + + if weather_count == offense_team.lineup_position and "snow_atbat" not in game.last_update[0].keys(): + result.clear() + result.update({ + "snow_atbat": True, + "text": f"{offense_team.lineup[offense_team.lineup_position % len(offense_team.lineup)].name}'s hands are too cold! {game.get_batter().name} is forced to bat!", + "text_only": True, + }) + + def on_flip_inning(self, game): + if game.top_of_inning and self.counter_away < game.teams["away"].lineup_position: + self.counter_away = game.pitcher_insert(game.teams["away"]) + + if not game.top_of_inning and self.counter_home < game.teams["home"].lineup_position: + self.counter_home = game.pitcher_insert(game.teams["home"]) + + def on_choose_next_batter(self, game): + if game.top_of_inning: + bat_team = game.teams["away"] + counter = self.counter_away + else: + bat_team = game.teams["home"] + counter = self.counter_home + + if counter == bat_team.lineup_position: + game.current_batter = bat_team.pitcher + +class Twilight(Weather): + def __init__(self): + super().__init__() + self.name = "Twilight" + self.emoji = "👻" + "\uFE00" + def activate(self, game, result): + pass + +class ThinnedVeil(Weather): + def __init__(self): + super().__init__() + self.name = "Thinned Veil" + self.emoji = "🌌" + "\uFE00" + + def activate(self, game, result): + if result["ishit"]: + if result["text"] == appearance_outcomes.homerun or result["text"] == appearance_outcomes.grandslam: + result["veil"] = True + +class HeatWave(Weather): + def __init__(self): + super().__init__() + self.name = "Heat Wave" + self.emoji = "🌄" + "\uFE00" + + self.counter_away = random.randint(2,4) + self.counter_home = random.randint(2,4) + + def on_flip_inning(self, game): + current_pitcher = game.get_pitcher() + if game.top_of_inning: + bat_team = game.teams["away"] + counter = self.counter_away + else: + bat_team = game.teams["home"] + counter = self.counter_home + + should_change_pitcher = False + if game.inning >= counter: + change_pitcher = True + if game.top_of_inning: + self.counter_home = self.counter_home - (self.counter_home % 5) + 5 + random.randint(1,4) #rounds down to last 5, adds up to next 5. then adds a random number 2<=x<=5 to determine next pitcher + else: + self.counter_away = self.counter_away - (self.counter_away % 5) + 5 + random.randint(1,4) + + if should_change_pitcher: + tries = 0 + while game.get_pitcher() == current_pitcher and tries < 3: + bat_team.set_pitcher(use_lineup = True) + tries += 1 + +class Drizzle(Weather): + def __init__(self): + super().__init__() + self.name = "Drizzle" + self.emoji = "🌧" + + def on_flip_inning(self, game): + if game.top_of_inning: + next_team = "away" + else: + next_team = "home" + + lineup = game.teams[next_team].lineup + game.bases[2] = lineup[(game.teams[next_team].lineup_position-1) % len(lineup)] + +class Sun2(Weather): + def __init__(self): + super().__init__() + self.name = "Sun 2" + def activate(self, game): + for teamtype in game.teams: + team = game.teams[teamtype] + if team.score >= 10: + team.score -= 10 + # no win counting yet :( + result.clear() + result.update({ + "text": "The {} collect 10! Sun 2 smiles.".format(team.name), + "text_only": True, + }) + +class NameSwappyWeather(Weather): + def __init__(self): + super().__init__() + self.name = "Literacy" + self.activation_chance = 0.01 + def activate(self, game): + if random.random() < self.activation_chance: + teamtype = random.choice(["away","home"]) + team = game.teams[teamtype] + player = random.choice(team.lineup) + old_player_name = player.name + if ' ' in player.name: + names = player.name.split(" ") + first_first_letter = names[0][0] + last_first_letter = names[-1][0] + names[0][0] = last_first_letter + names[-1][0] = first_first_letter + player.name = ' '.join(names) + else: + #name is one word, so turn 'bartholemew' into 'martholebew' + first_letter = player.name[0] + last_letter = player.name[-1] + player.name[0] = last_letter + player.name[-1] = first_letter + + result.clear() + result.update({ + "text": "{} is Literate! {} is now {}!".format(old_player_name,old_player_name, player.name), + "text_only": True, + }) + +class Feedback(Weather): + def __init__(self): + super().__init__() + self.name = "Feedback" + self.activation_chance = 0.01 + self.swap_batter_vs_pitcher_chance = 0.8 + def activate(self, game, result): + if random.random() < self.activation_chance: + # feedback time + result = {} + player1 = None + player2 = None + if random.random() < self.swap_batter_vs_pitcher_chance: + # swapping batters + # theoretically this could swap players already on base :( + homePlayerIndex = random.randint(len(teams["home"].lineup)) + awayPlayerIndex = random.randint(len(teams["away"].lineup)) + + homePlayer = teams["home"].lineup[homePlayerIndex] + awayPlayer = teams["away"].lineup[awayPlayerIndex] + + teams["home"].lineup[homePlayerIndex] = awayPlayer + teams["away"].lineup[awayPlayerIndex] = homePlayer + else: + # swapping pitchers + player1 = teams["home"].pitcher + player2 = teams["away"].pitcher + teams["home"].set_pitcher(player2) + teams["away"].set_pitcher(player1) + + result.clear() + result.update({ + "text": "{} and {} switched teams in the feedback!".format(player1.name,player2.name), + "text_only": True, + }) + +def all_weathers(): + weathers_dic = { + #"Supernova" : Supernova, + #"Midnight": Midnight, + #"Slight Tailwind": SlightTailwind, + "Heavy Snow": HeavySnow, +# "Twilight" : Twilight, +# "Thinned Veil" : ThinnedVeil, + "Heat Wave" : HeatWave, + "Drizzle" : Drizzle, # works +# Sun2, +# Feedback, +# NameSwappyWeather, + } + return weathers_dic + From a2c5d3cb0deeef52cec4232a7555ee627b8a44c1 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 13:02:47 -0500 Subject: [PATCH 02/15] Move weathers to new file --- games.py | 57 +++++++++++++-------------- main_controller.py | 9 +++-- the_prestige.py | 5 +-- weather.py | 97 +++++++++++++++++++++++++--------------------- 4 files changed, 86 insertions(+), 82 deletions(-) diff --git a/games.py b/games.py index 42b3473..e6f5c7e 100644 --- a/games.py +++ b/games.py @@ -222,7 +222,7 @@ class game(object): else: self.max_innings = config()["default_length"] self.bases = {1 : None, 2 : None, 3 : None} - self.weather = weather.Weather() + self.weather = weather.Weather(self) self.current_batter = None def choose_next_batter(self): @@ -261,38 +261,34 @@ class game(object): outcome["batter"] = batter outcome["defender"] = "" - bat_stat = random_star_gen("batting_stars", batter) - pitch_stat = random_star_gen("pitching_stars", pitcher) + player_rolls = {} + player_rolls["bat_stat"] = random_star_gen("batting_stars", batter) + player_rolls["pitch_stat"] = random_star_gen("pitching_stars", pitcher) - bat_stat, pitch_stat = self.weather.modify_stats_preroll(bat_stat, pitch_stat) + self.weather.modify_atbat_stats(player_rolls) - pb_system_stat = (random.gauss(1*math.erf((bat_stat - pitch_stat)*1.5)-1.8,2.2)) - hitnum = random.gauss(2*math.erf(bat_stat/4)-1,3) + roll = {} + roll["pb_system_stat"] = (random.gauss(1*math.erf((player_rolls["bat_stat"] - player_rolls["pitch_stat"])*1.5)-1.8,2.2)) + roll["hitnum"] = random.gauss(2*math.erf(player_rolls["bat_stat"]/4)-1,3) - if self.weather.name == "Twilight": - error_line = - (math.log(defender.stlats["defense_stars"] + 1)/50) + 1 - error_roll = random.random() - if error_roll > error_line: - outcome["error"] = True - outcome["defender"] = defender - pb_system_stat = 0.1 + self.weather.modify_atbat_roll(outcome, roll, defender) - if pb_system_stat <= 0: + if roll["pb_system_stat"] <= 0: outcome["ishit"] = False fc_flag = False - if hitnum < -1.5: + if roll["hitnum"] < -1.5: outcome["text"] = random.choice([appearance_outcomes.strikeoutlooking, appearance_outcomes.strikeoutswinging]) - elif hitnum < 1: + elif roll["hitnum"] < 1: outcome["text"] = appearance_outcomes.groundout outcome["defender"] = defender - elif hitnum < 4: + elif roll["hitnum"] < 4: outcome["text"] = appearance_outcomes.flyout outcome["defender"] = defender else: outcome["text"] = appearance_outcomes.walk - if self.bases[1] is not None and hitnum < -2 and self.outs != 2: + if self.bases[1] is not None and roll["hitnum"] < -2 and self.outs != 2: outcome["text"] = appearance_outcomes.doubleplay outcome["defender"] = "" @@ -309,20 +305,20 @@ class game(object): if self.outs < 2 and len(runners) > 1: #fielder's choice replaces not great groundouts if any forceouts are present def_stat = random_star_gen("defense_stars", defender) - if -1.5 <= hitnum and hitnum < -0.5: #poorly hit groundouts + if -1.5 <= roll["hitnum"] and roll["hitnum"] < -0.5: #poorly hit groundouts outcome["text"] = appearance_outcomes.fielderschoice outcome["defender"] = "" - if 2.5 <= hitnum and self.outs < 2: #well hit flyouts can lead to sacrifice flies/advanced runners + if 2.5 <= roll["hitnum"] and self.outs < 2: #well hit flyouts can lead to sacrifice flies/advanced runners if self.bases[2] is not None or self.bases[3] is not None: outcome["advance"] = True else: outcome["ishit"] = True - if hitnum < 1: + if roll["hitnum"] < 1: outcome["text"] = appearance_outcomes.single - elif hitnum < 2.85 or "error" in outcome.keys(): + elif roll["hitnum"] < 2.85 or "error" in outcome.keys(): outcome["text"] = appearance_outcomes.double - elif hitnum < 3.1: + elif roll["hitnum"] < 3.1: outcome["text"] = appearance_outcomes.triple else: if self.bases[1] is not None and self.bases[2] is not None and self.bases[3] is not None: @@ -339,13 +335,16 @@ class game(object): if self.bases[base+1] is None: #if there's somewhere to go thieves.append((self.bases[base], base)) for baserunner, start_base in thieves: - run_stars = random_star_gen("baserunning_stars", baserunner)*config()["stolen_base_chance_mod"] - if self.weather.name == "Midnight": - run_stars = run_stars*2 - def_stars = random_star_gen("defense_stars", self.get_pitcher()) - if run_stars >= (def_stars - 1.5): #if baserunner isn't worse than pitcher + stats = { + "run_stars": random_star_gen("baserunning_stars", baserunner)*config()["stolen_base_chance_mod"], + "def_stars": random_star_gen("defense_stars", self.get_pitcher()) + } + + self.weather.modify_steal_stats(stats) + + if stats["run_stars"] >= (stats["def_stars"] - 1.5): #if baserunner isn't worse than pitcher roll = random.random() - if roll >= (-(((run_stars+1)/14)**2)+1): #plug it into desmos or something, you'll see + if roll >= (-(((stats["run_stars"]+1)/14)**2)+1): #plug it into desmos or something, you'll see attempts.append((baserunner, start_base)) if len(attempts) == 0: diff --git a/main_controller.py b/main_controller.py index b513322..251e1c3 100644 --- a/main_controller.py +++ b/main_controller.py @@ -3,6 +3,7 @@ from leagues import league_structure from league_storage import league_exists from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory, abort from flask_socketio import SocketIO, emit +from gametext import base_string import database as db app = Flask("the-prestige", static_folder='simmadome/build') @@ -156,7 +157,7 @@ def update_loop(): state["display_inning"] -= 1 state["display_top_of_inning"] = False - if state["update_pause"] == 1: + if state["update_pause"] == 1: #generate the top of the inning message before displaying the at bat result state["update_emoji"] = "🍿" if this_game.over: state["display_inning"] -= 1 @@ -222,8 +223,8 @@ def update_loop(): if "fc_out" in this_game.last_update[0].keys(): - name, base_string = this_game.last_update[0]['fc_out'] - updatestring = f"{this_game.last_update[0]['batter']} {this_game.last_update[0]['text'].value.format(name, base_string)} {this_game.last_update[0]['defender']}{punc}" + name, out_at_base_string = this_game.last_update[0]['fc_out'] + updatestring = f"{this_game.last_update[0]['batter']} {this_game.last_update[0]['text'].value.format(name, out_at_base_string)} {this_game.last_update[0]['defender']}{punc}" else: updatestring = f"{this_game.last_update[0]['batter']} {this_game.last_update[0]['text'].value} {this_game.last_update[0]['defender']}{punc}" if this_game.last_update[1] > 0: @@ -234,7 +235,7 @@ def update_loop(): if "veil" in this_game.last_update[0].keys(): state["update_emoji"] = "🌌" - state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {gametext.base_string(this_game.last_update[1])} base." + state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {base_string(this_game.last_update[1])} base." elif "error" in this_game.last_update[0].keys(): state["update_emoji"] = "👻" state["update_text"] = f"{this_game.last_update[0]['batter']}'s hit goes ethereal, and {this_game.last_update[0]['defender']} can't catch it! {this_game.last_update[0]['batter']} reaches base safely." diff --git a/the_prestige.py b/the_prestige.py index 6d63519..cdf95b8 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1398,7 +1398,7 @@ async def watch_game(channel, newgame, user = None, league = None): def prepare_game(newgame, league = None, weather_name = None): if weather_name is None: weathers = weather.all_weathers() - newgame.weather = weathers[random.choice(list(weathers.keys()))]() + newgame.weather = weathers[random.choice(list(weathers.keys()))](newgame) state_init = { "away_name" : newgame.teams['away'].name, @@ -1418,9 +1418,6 @@ def prepare_game(newgame, league = None, weather_name = None): else: state_init["is_league"] = True - if newgame.weather.name == "Heavy Snow": - newgame.weather.counter_away = random.randint(0,len(newgame.teams['away'].lineup)-1) - newgame.weather.counter_home = random.randint(0,len(newgame.teams['home'].lineup)-1) return newgame, state_init async def start_tournament_round(channel, tourney, seeding = None): diff --git a/weather.py b/weather.py index 4087492..a38f478 100644 --- a/weather.py +++ b/weather.py @@ -1,12 +1,11 @@ import random +import math from gametext import appearance_outcomes class Weather: - def __init__(self): + def __init__(self, game): self.name = "Sunny" self.emoji = "🌞" + "\uFE00" - self.counter_away = 0 - self.counter_home = 0 def __str__(self): return f"{self.emoji} {self.name}" @@ -15,43 +14,44 @@ class Weather: # activates after the batter calculation. modify result, or just return another thing pass - def modify_stats_preroll(self, bat_stat, pitch_stat): # ugly - # Activates before batting and pitch - return bat_stat, pitch_stat - def on_flip_inning(self, game): pass def on_choose_next_batter(self, game): pass + def modify_steal_stats(self, roll): + pass + + def modify_atbat_stats(self, player_rolls): + # Activates before batting + pass + + def modify_atbat_roll(self, outcome, roll, defender): + pass + class Supernova(Weather): # todo - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Supernova" self.emoji = "🌟" + "\uFE00" - def activate(self, game, result): - pass - def modify_stats_preroll(self, bat_stat, pitch_stat): - if self.weather.name == "Supernova": - pitch_stat = pitch_stat * 0.9 + def modify_atbat_stats(self, roll): + roll["pitch_stat"] *= 0.9 class Midnight(Weather): # todo - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Midnight" self.emoji = "🕶" + "\uFE00" - def activate(self, game, result): - pass + + def modify_steal_stats(self, roll): + roll["run_stars"] *= 2 class SlightTailwind(Weather): - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Slight Tailwind" self.emoji = "🏌️‍♀️" + "\uFE00" + def activate(self, game, result): - if game.top_of_inning: offense_team = game.teams["away"] weather_count = self.counter_away @@ -67,10 +67,11 @@ class SlightTailwind(Weather): result["mulligan"] = True class HeavySnow(Weather): - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Heavy Snow" self.emoji = "❄" + "\uFE00" + self.counter_away = random.randint(0,len(game.teams['away'].lineup)-1) + self.counter_home = random.randint(0,len(game.teams['home'].lineup)-1) def activate(self, game, result): if game.top_of_inning: @@ -107,16 +108,23 @@ class HeavySnow(Weather): game.current_batter = bat_team.pitcher class Twilight(Weather): - def __init__(self): - super().__init__() + def __init__(self,game): self.name = "Twilight" self.emoji = "👻" + "\uFE00" def activate(self, game, result): pass + + def modify_atbat_roll(self, outcome, roll, defender): + error_line = - (math.log(defender.stlats["defense_stars"] + 1)/50) + 1 + error_roll = random.random() + if error_roll > error_line: + outcome["error"] = True + outcome["defender"] = defender + roll["pb_system_stat"] = 0.1 + class ThinnedVeil(Weather): - def __init__(self): - super().__init__() + def __init__(self,game): self.name = "Thinned Veil" self.emoji = "🌌" + "\uFE00" @@ -126,22 +134,21 @@ class ThinnedVeil(Weather): result["veil"] = True class HeatWave(Weather): - def __init__(self): - super().__init__() + def __init__(self,game): self.name = "Heat Wave" self.emoji = "🌄" + "\uFE00" - self.counter_away = random.randint(2,4) - self.counter_home = random.randint(2,4) + self.counter_away = -1# random.randint(2,4) + self.counter_home = -1 # random.randint(2,4) def on_flip_inning(self, game): current_pitcher = game.get_pitcher() if game.top_of_inning: - bat_team = game.teams["away"] - counter = self.counter_away - else: bat_team = game.teams["home"] counter = self.counter_home + else: + bat_team = game.teams["away"] + counter = self.counter_away should_change_pitcher = False if game.inning >= counter: @@ -158,8 +165,7 @@ class HeatWave(Weather): tries += 1 class Drizzle(Weather): - def __init__(self): - super().__init__() + def __init__(self,game): self.name = "Drizzle" self.emoji = "🌧" @@ -173,9 +179,10 @@ class Drizzle(Weather): game.bases[2] = lineup[(game.teams[next_team].lineup_position-1) % len(lineup)] class Sun2(Weather): - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Sun 2" + + def activate(self, game): for teamtype in game.teams: team = game.teams[teamtype] @@ -184,15 +191,15 @@ class Sun2(Weather): # no win counting yet :( result.clear() result.update({ - "text": "The {} collect 10! Sun 2 smiles.".format(team.name), + "text": "The {} collect 10! Sun 2 smiles.".format(team.name), "text_only": True, }) class NameSwappyWeather(Weather): - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Literacy" self.activation_chance = 0.01 + def activate(self, game): if random.random() < self.activation_chance: teamtype = random.choice(["away","home"]) @@ -220,11 +227,11 @@ class NameSwappyWeather(Weather): }) class Feedback(Weather): - def __init__(self): - super().__init__() + def __init__(self, game): self.name = "Feedback" self.activation_chance = 0.01 self.swap_batter_vs_pitcher_chance = 0.8 + def activate(self, game, result): if random.random() < self.activation_chance: # feedback time @@ -261,8 +268,8 @@ def all_weathers(): #"Midnight": Midnight, #"Slight Tailwind": SlightTailwind, "Heavy Snow": HeavySnow, -# "Twilight" : Twilight, -# "Thinned Veil" : ThinnedVeil, + # "Twilight" : Twilight, # works + # "Thinned Veil" : ThinnedVeil, # works "Heat Wave" : HeatWave, "Drizzle" : Drizzle, # works # Sun2, From d415f658b0bcc9f264a0d6943d47f4f135bda204 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 14:12:34 -0500 Subject: [PATCH 03/15] Add Literacy weather, with procedural literature types --- games.py | 27 +++++---------------------- main_controller.py | 19 ++++++++----------- weather.py | 45 ++++++++++++++++++++++++++------------------- 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/games.py b/games.py index e6f5c7e..4c3d58f 100644 --- a/games.py +++ b/games.py @@ -214,8 +214,8 @@ class game(object): self.outs = 0 self.top_of_inning = True self.last_update = ({},0) #this is a ({outcome}, runs) tuple + self.play_has_begun = False self.owner = None - self.ready = False self.victory_lap = False if length is not None: self.max_innings = length @@ -653,37 +653,20 @@ class game(object): def gamestate_update_full(self): + self.play_has_begun = True attempts = self.thievery_attempts() if attempts == False: self.last_update = self.batterup() + print(self.last_update[0]) ############# DEBUG REMOVE ME ################# else: self.last_update = attempts return self.gamestate_display_full() def gamestate_display_full(self): - if "steals" in self.last_update[0].keys(): + if not self.over: return "Still in progress." else: - try: - punc = "" - if self.last_update[0]["defender"] != "": - punc = "." - if not self.over: - if self.top_of_inning: - inningtext = "top" - else: - inningtext = "bottom" - - updatestring = "this isn't used but i don't want to break anything" - - return "this isn't used but i don't want to break anything" - else: - return f"""Game over! Final score: **{self.teams['away'].score} - {self.teams['home'].score}** - Last update: {self.last_update[0]['batter']} {self.last_update[0]['text'].value} {self.last_update[0]['defender']}{punc}""" - except TypeError: - return "Game not started." - except KeyError: - return "Game not started." + return f"""Game over! Final score: **{self.teams['away'].score} - {self.teams['home'].score}**""" def add_stats(self): players = self.get_stats() diff --git a/main_controller.py b/main_controller.py index 251e1c3..8f9964e 100644 --- a/main_controller.py +++ b/main_controller.py @@ -141,7 +141,7 @@ def update_loop(): state["away_score"] = this_game.teams["away"].score #top_of_inning = True state["home_score"] = this_game.teams["home"].score #update_pause = 0 #victory_lap = False - if test_string == "Game not started.": #weather_emoji + if not this_game.play_has_begun: #weather_emoji state["update_emoji"] = "🍿" #weather_text state["update_text"] = "Play blall!" #they also need a timestamp state["start_delay"] -= 1 @@ -192,7 +192,11 @@ def update_loop(): state["update_emoji"] = "🌄" state["update_text"] += f' {this_game.weather.away_pitcher} is exhausted from the heat. {state["pitcher"]} is forced to pitch!' - elif state["update_pause"] != 1 and test_string != "Game not started.": + elif state["update_pause"] != 1 and this_game.play_has_begun: + + if "weather_message" in this_game.last_update[0].keys(): + state["update_emoji"] = this_game.weather.emoji + if "steals" in this_game.last_update[0].keys(): updatestring = "" for attempt in this_game.last_update[0]["steals"]: @@ -207,21 +211,16 @@ def update_loop(): if this_game.last_update[0]["defender"] != "": punc = ", " - state["update_emoji"] = "🏌️‍♀️" state["update_text"] = f"{this_game.last_update[0]['batter']} would have gone out, but they took a mulligan!" - elif "snow_atbat" in this_game.last_update[0].keys(): - state["update_emoji"] = "❄" + elif "text_only" in this_game.last_update[0].keys(): state["update_text"] = this_game.last_update[0]["text"] - else: updatestring = "" punc = "" if this_game.last_update[0]["defender"] != "": punc = ". " - - if "fc_out" in this_game.last_update[0].keys(): name, out_at_base_string = this_game.last_update[0]['fc_out'] updatestring = f"{this_game.last_update[0]['batter']} {this_game.last_update[0]['text'].value.format(name, out_at_base_string)} {this_game.last_update[0]['defender']}{punc}" @@ -233,11 +232,9 @@ def update_loop(): state["update_emoji"] = "🏏" state["update_text"] = updatestring - if "veil" in this_game.last_update[0].keys(): - state["update_emoji"] = "🌌" + if "veil" in this_game.last_update[0].keys(): state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {base_string(this_game.last_update[1])} base." elif "error" in this_game.last_update[0].keys(): - state["update_emoji"] = "👻" state["update_text"] = f"{this_game.last_update[0]['batter']}'s hit goes ethereal, and {this_game.last_update[0]['defender']} can't catch it! {this_game.last_update[0]['batter']} reaches base safely." if this_game.last_update[1] > 0: updatestring += f"{this_game.last_update[1]} runs scored!" diff --git a/weather.py b/weather.py index a38f478..40e12bf 100644 --- a/weather.py +++ b/weather.py @@ -65,6 +65,7 @@ class SlightTailwind(Weather): mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1 if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5: result["mulligan"] = True + result["weather_message"] = True class HeavySnow(Weather): def __init__(self, game): @@ -84,9 +85,9 @@ class HeavySnow(Weather): if weather_count == offense_team.lineup_position and "snow_atbat" not in game.last_update[0].keys(): result.clear() result.update({ - "snow_atbat": True, "text": f"{offense_team.lineup[offense_team.lineup_position % len(offense_team.lineup)].name}'s hands are too cold! {game.get_batter().name} is forced to bat!", "text_only": True, + "weather_message": True, }) def on_flip_inning(self, game): @@ -111,15 +112,13 @@ class Twilight(Weather): def __init__(self,game): self.name = "Twilight" self.emoji = "👻" + "\uFE00" - def activate(self, game, result): - pass - def modify_atbat_roll(self, outcome, roll, defender): error_line = - (math.log(defender.stlats["defense_stars"] + 1)/50) + 1 error_roll = random.random() if error_roll > error_line: outcome["error"] = True + outcome["weather_message"] = True outcome["defender"] = defender roll["pb_system_stat"] = 0.1 @@ -132,6 +131,7 @@ class ThinnedVeil(Weather): if result["ishit"]: if result["text"] == appearance_outcomes.homerun or result["text"] == appearance_outcomes.grandslam: result["veil"] = True + result["weather_message"] = True class HeatWave(Weather): def __init__(self,game): @@ -198,9 +198,10 @@ class Sun2(Weather): class NameSwappyWeather(Weather): def __init__(self, game): self.name = "Literacy" + self.emoji = "📚" self.activation_chance = 0.01 - def activate(self, game): + def activate(self, game, result): if random.random() < self.activation_chance: teamtype = random.choice(["away","home"]) team = game.teams[teamtype] @@ -210,32 +211,37 @@ class NameSwappyWeather(Weather): names = player.name.split(" ") first_first_letter = names[0][0] last_first_letter = names[-1][0] - names[0][0] = last_first_letter - names[-1][0] = first_first_letter + names[0] = last_first_letter + names[0][1:] + names[-1] = first_first_letter + names[-1][1:] player.name = ' '.join(names) else: #name is one word, so turn 'bartholemew' into 'martholebew' first_letter = player.name[0] last_letter = player.name[-1] - player.name[0] = last_letter - player.name[-1] = first_letter + player.name = last_letter + player.name[1:-1] + last_letter + + book_adjectives = ["action-packed", "historical", "friendly", "rude", "mystery", "thriller", "horror", "sci-fi", "fantasy", "spooky","romantic"] + book_types = ["novel","novella","poem","anthology","fan fiction","tablet","carving", "autobiography"] + book = "{} {}".format(random.choice(book_adjectives),random.choice(book_types)) result.clear() result.update({ - "text": "{} is Literate! {} is now {}!".format(old_player_name,old_player_name, player.name), + "text": "{} stopped to read a {} and became Literate! {} is now {}!".format(old_player_name, book, old_player_name, player.name), "text_only": True, + "weather_message": True }) + class Feedback(Weather): def __init__(self, game): self.name = "Feedback" - self.activation_chance = 0.01 + self.emoji = "🎤" + self.activation_chance = 0.25 self.swap_batter_vs_pitcher_chance = 0.8 def activate(self, game, result): if random.random() < self.activation_chance: # feedback time - result = {} player1 = None player2 = None if random.random() < self.swap_batter_vs_pitcher_chance: @@ -260,6 +266,7 @@ class Feedback(Weather): result.update({ "text": "{} and {} switched teams in the feedback!".format(player1.name,player2.name), "text_only": True, + "weather_message": True, }) def all_weathers(): @@ -267,14 +274,14 @@ def all_weathers(): #"Supernova" : Supernova, #"Midnight": Midnight, #"Slight Tailwind": SlightTailwind, - "Heavy Snow": HeavySnow, +# "Heavy Snow": HeavySnow, # "Twilight" : Twilight, # works - # "Thinned Veil" : ThinnedVeil, # works - "Heat Wave" : HeatWave, - "Drizzle" : Drizzle, # works -# Sun2, -# Feedback, -# NameSwappyWeather, + "Thinned Veil" : ThinnedVeil, # works +# "Heat Wave" : HeatWave, +# "Drizzle" : Drizzle, # works +# "Sun 2": Sun2, +# "Feedback": Feedback, + "Literacy": NameSwappyWeather, } return weathers_dic From f021cb4ea401f4049cc23c5e916e0beb2e940ba6 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 14:56:27 -0500 Subject: [PATCH 04/15] Implement Feedback weather --- weather.py | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/weather.py b/weather.py index 40e12bf..55790b6 100644 --- a/weather.py +++ b/weather.py @@ -85,6 +85,7 @@ class HeavySnow(Weather): if weather_count == offense_team.lineup_position and "snow_atbat" not in game.last_update[0].keys(): result.clear() result.update({ + "snow_atbat": True, "text": f"{offense_team.lineup[offense_team.lineup_position % len(offense_team.lineup)].name}'s hands are too cold! {game.get_batter().name} is forced to bat!", "text_only": True, "weather_message": True, @@ -138,7 +139,7 @@ class HeatWave(Weather): self.name = "Heat Wave" self.emoji = "🌄" + "\uFE00" - self.counter_away = -1# random.randint(2,4) + self.counter_away = -1 # random.randint(2,4) self.counter_home = -1 # random.randint(2,4) def on_flip_inning(self, game): @@ -152,7 +153,7 @@ class HeatWave(Weather): should_change_pitcher = False if game.inning >= counter: - change_pitcher = True + should_change_pitcher = True if game.top_of_inning: self.counter_home = self.counter_home - (self.counter_home % 5) + 5 + random.randint(1,4) #rounds down to last 5, adds up to next 5. then adds a random number 2<=x<=5 to determine next pitcher else: @@ -236,7 +237,7 @@ class Feedback(Weather): def __init__(self, game): self.name = "Feedback" self.emoji = "🎤" - self.activation_chance = 0.25 + self.activation_chance = 0.02 self.swap_batter_vs_pitcher_chance = 0.8 def activate(self, game, result): @@ -244,24 +245,29 @@ class Feedback(Weather): # feedback time player1 = None player2 = None + team1 = game.teams["home"] + team2 = game.teams["away"] if random.random() < self.swap_batter_vs_pitcher_chance: # swapping batters # theoretically this could swap players already on base :( - homePlayerIndex = random.randint(len(teams["home"].lineup)) - awayPlayerIndex = random.randint(len(teams["away"].lineup)) + team1 = game.teams["home"] + team2 = game.teams["away"] + homePlayerIndex = random.randint(0,len(team1.lineup)-1) + awayPlayerIndex = random.randint(0,len(team2.lineup)-1) - homePlayer = teams["home"].lineup[homePlayerIndex] - awayPlayer = teams["away"].lineup[awayPlayerIndex] + player1 = team1.lineup[homePlayerIndex] + player2 = team2.lineup[awayPlayerIndex] - teams["home"].lineup[homePlayerIndex] = awayPlayer - teams["away"].lineup[awayPlayerIndex] = homePlayer + team1.lineup[homePlayerIndex] = player2 + team2.lineup[awayPlayerIndex] = player1 else: # swapping pitchers - player1 = teams["home"].pitcher - player2 = teams["away"].pitcher - teams["home"].set_pitcher(player2) - teams["away"].set_pitcher(player1) - + player1 = team1.pitcher + player2 = team2.pitcher + + team1.pitcher = player2 + team2.pitcher = player1 + result.clear() result.update({ "text": "{} and {} switched teams in the feedback!".format(player1.name,player2.name), @@ -274,13 +280,13 @@ def all_weathers(): #"Supernova" : Supernova, #"Midnight": Midnight, #"Slight Tailwind": SlightTailwind, -# "Heavy Snow": HeavySnow, - # "Twilight" : Twilight, # works - "Thinned Veil" : ThinnedVeil, # works -# "Heat Wave" : HeatWave, -# "Drizzle" : Drizzle, # works -# "Sun 2": Sun2, -# "Feedback": Feedback, + "Heavy Snow": HeavySnow, # works + "Twilight" : Twilight, # works + "Thinned Veil" : ThinnedVeil, # works + "Heat Wave" : HeatWave, + "Drizzle" : Drizzle, # works +# "Sun 2": Sun2, + "Feedback": Feedback, "Literacy": NameSwappyWeather, } return weathers_dic From 384b6448055dd3cad3e7eb607b3fed256e60e0b2 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 18:15:23 -0500 Subject: [PATCH 05/15] Move all weather-related code into weather.py --- games.py | 5 -- main_controller.py | 51 +++++------------ weather.py | 134 +++++++++++++++++++++++++++++++-------------- 3 files changed, 106 insertions(+), 84 deletions(-) diff --git a/games.py b/games.py index 4c3d58f..6493e94 100644 --- a/games.py +++ b/games.py @@ -628,10 +628,6 @@ class game(object): if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over self.over = True - def pitcher_insert(self, this_team): - rounds = math.ceil(this_team.lineup_position / len(this_team.lineup)) - position = random.randint(0, len(this_team.lineup)-1) - return rounds * len(this_team.lineup) + position def end_of_game_report(self): return { @@ -657,7 +653,6 @@ class game(object): attempts = self.thievery_attempts() if attempts == False: self.last_update = self.batterup() - print(self.last_update[0]) ############# DEBUG REMOVE ME ################# else: self.last_update = attempts return self.gamestate_display_full() diff --git a/main_controller.py b/main_controller.py index 8f9964e..ca693a2 100644 --- a/main_controller.py +++ b/main_controller.py @@ -3,7 +3,6 @@ from leagues import league_structure from league_storage import league_exists from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory, abort from flask_socketio import SocketIO, emit -from gametext import base_string import database as db app = Flask("the-prestige", static_folder='simmadome/build') @@ -159,7 +158,7 @@ def update_loop(): if state["update_pause"] == 1: #generate the top of the inning message before displaying the at bat result state["update_emoji"] = "🍿" - if this_game.over: + if this_game.over: # game over message state["display_inning"] -= 1 state["display_top_of_inning"] = False winning_team = this_game.teams['home'].name if this_game.teams['home'].score > this_game.teams['away'].score else this_game.teams['away'].name @@ -171,26 +170,17 @@ def update_loop(): state["update_text"] = f"{winning_team} wins!" state["pitcher"] = "-" state["batter"] = "-" - elif this_game.top_of_inning: - state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" - if this_game.weather.name == "Drizzle": - state["update_emoji"] = "🌧" - state["update_text"] += f'Due to inclement weather, {this_game.teams["away"].lineup[(this_game.teams["away"].lineup_position-1) % len(this_game.teams["away"].lineup)].name} is placed on second base.' - elif this_game.weather.name == "Heat Wave" and hasattr(this_game.weather, "home_pitcher") and this_game.weather.home_pitcher.name != state["pitcher"]: - state["update_emoji"] = "🌄" - state["update_text"] += f' {this_game.weather.home_pitcher} is exhausted from the heat. {state["pitcher"]} is forced to pitch!' - 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!" - if this_game.weather.name == "Drizzle": - state["update_emoji"] = "🌧" - state["update_text"] += f' Due to inclement weather, {this_game.teams["home"].lineup[(this_game.teams["home"].lineup_position-1) % len(this_game.teams["home"].lineup)].name} is placed on second base.' - elif this_game.weather.name == "Heat Wave" and hasattr(this_game.weather, "away_pitcher") and this_game.weather.away_pitcher.name != state["pitcher"]: - state["update_emoji"] = "🌄" - state["update_text"] += f' {this_game.weather.away_pitcher} is exhausted from the heat. {state["pitcher"]} is forced to pitch!' + if this_game.top_of_inning: + state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" + 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) + elif state["update_pause"] != 1 and this_game.play_has_begun: @@ -205,15 +195,7 @@ def update_loop(): state["update_emoji"] = "💎" state["update_text"] = updatestring - elif "mulligan" in this_game.last_update[0].keys(): - updatestring = "" - punc = "" - if this_game.last_update[0]["defender"] != "": - punc = ", " - - state["update_text"] = f"{this_game.last_update[0]['batter']} would have gone out, but they took a mulligan!" - - elif "text_only" in this_game.last_update[0].keys(): + elif "text_only" in this_game.last_update[0].keys(): #this handles many weather messages state["update_text"] = this_game.last_update[0]["text"] else: updatestring = "" @@ -231,13 +213,8 @@ def update_loop(): state["update_emoji"] = "🏏" state["update_text"] = updatestring - - if "veil" in this_game.last_update[0].keys(): - state["update_text"] += f" {this_game.last_update[0]['batter']}'s will manifests on {base_string(this_game.last_update[1])} base." - elif "error" in this_game.last_update[0].keys(): - state["update_text"] = f"{this_game.last_update[0]['batter']}'s hit goes ethereal, and {this_game.last_update[0]['defender']} can't catch it! {this_game.last_update[0]['batter']} reaches base safely." - if this_game.last_update[1] > 0: - updatestring += f"{this_game.last_update[1]} runs scored!" + + this_game.weather.modify_atbat_message(this_game, state) state["bases"] = this_game.named_bases() diff --git a/weather.py b/weather.py index 55790b6..2bc18b9 100644 --- a/weather.py +++ b/weather.py @@ -1,6 +1,6 @@ import random import math -from gametext import appearance_outcomes +from gametext import appearance_outcomes, base_string class Weather: def __init__(self, game): @@ -10,26 +10,34 @@ class Weather: def __str__(self): return f"{self.emoji} {self.name}" - def activate(self, game, result): - # activates after the batter calculation. modify result, or just return another thing - pass - - def on_flip_inning(self, game): - pass - - def on_choose_next_batter(self, game): + def modify_atbat_stats(self, player_rolls): + # Activates before batting pass def modify_steal_stats(self, roll): pass - def modify_atbat_stats(self, player_rolls): - # Activates before batting + def modify_atbat_roll(self, outcome, roll, defender): + # activates after batter roll pass - def modify_atbat_roll(self, outcome, roll, defender): + def activate(self, game, result): + # activates after the batter calculation. modify result, or just return another thing pass + def on_choose_next_batter(self, game): + pass + + def on_flip_inning(self, game): + pass + + def modify_top_of_inning_message(self, game, state): + pass + + def modify_atbat_message(self, game, state): + pass + + class Supernova(Weather): # todo def __init__(self, game): self.name = "Supernova" @@ -64,8 +72,12 @@ class SlightTailwind(Weather): if "mulligan" not in game.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk: mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1 if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5: - result["mulligan"] = True - result["weather_message"] = True + result.clear() + result.update({ + "text": f"{this_game.last_update[0]['batter']} would have gone out, but they took a mulligan!", + "text_only": True, + "weather_message": True, + }) class HeavySnow(Weather): def __init__(self, game): @@ -74,29 +86,31 @@ class HeavySnow(Weather): self.counter_away = random.randint(0,len(game.teams['away'].lineup)-1) self.counter_home = random.randint(0,len(game.teams['home'].lineup)-1) - def activate(self, game, result): - if game.top_of_inning: - offense_team = game.teams["away"] - weather_count = self.counter_away - else: - offense_team = game.teams["home"] - weather_count = self.counter_home + self.swapped_batter_data = None - if weather_count == offense_team.lineup_position and "snow_atbat" not in game.last_update[0].keys(): + def activate(self, game, result): + if self.swapped_batter_data: + original, sub = self.swapped_batter_data + self.swapped_batter_data = None result.clear() result.update({ "snow_atbat": True, - "text": f"{offense_team.lineup[offense_team.lineup_position % len(offense_team.lineup)].name}'s hands are too cold! {game.get_batter().name} is forced to bat!", + "text": f"{original.name}'s hands are too cold! {sub.name} is forced to bat!", "text_only": True, "weather_message": True, }) def on_flip_inning(self, game): if game.top_of_inning and self.counter_away < game.teams["away"].lineup_position: - self.counter_away = game.pitcher_insert(game.teams["away"]) + self.counter_away = self.pitcher_insert_index(game.teams["away"]) if not game.top_of_inning and self.counter_home < game.teams["home"].lineup_position: - self.counter_home = game.pitcher_insert(game.teams["home"]) + self.counter_home = self.pitcher_insert_index(game.teams["home"]) + + def pitcher_insert_index(self, this_team): + rounds = math.ceil(this_team.lineup_position / len(this_team.lineup)) + position = random.randint(0, len(this_team.lineup)-1) + return rounds * len(this_team.lineup) + position def on_choose_next_batter(self, game): if game.top_of_inning: @@ -106,7 +120,8 @@ class HeavySnow(Weather): bat_team = game.teams["home"] counter = self.counter_home - if counter == bat_team.lineup_position: + if bat_team.lineup_position == counter: + self.swapped_batter_data = (game.current_batter, bat_team.pitcher) # store this to generate the message during activate() game.current_batter = bat_team.pitcher class Twilight(Weather): @@ -123,6 +138,13 @@ class Twilight(Weather): outcome["defender"] = defender roll["pb_system_stat"] = 0.1 + def modify_atbat_message(self, this_game, state): + result = this_game.last_update[0] + if "error" in result.keys(): + state["update_text"] = f"{result['batter']}'s hit goes ethereal, and {result['defender']} can't catch it! {result['batter']} reaches base safely." + if this_game.last_update[1] > 0: + state["update_text"] += f"{this_game.last_update[1]} runs scored!" + class ThinnedVeil(Weather): def __init__(self,game): self.name = "Thinned Veil" @@ -132,18 +154,24 @@ class ThinnedVeil(Weather): if result["ishit"]: if result["text"] == appearance_outcomes.homerun or result["text"] == appearance_outcomes.grandslam: result["veil"] = True - result["weather_message"] = True + + def modify_atbat_message(self, game, state): + if "veil" in game.last_update[0].keys(): + state["update_emoji"] = self.emoji + state["update_text"] += f" {game.last_update[0]['batter']}'s will manifests on {base_string(game.last_update[1])} base." class HeatWave(Weather): def __init__(self,game): self.name = "Heat Wave" self.emoji = "🌄" + "\uFE00" - self.counter_away = -1 # random.randint(2,4) - self.counter_home = -1 # random.randint(2,4) + self.counter_away = random.randint(2,4) + self.counter_home = random.randint(2,4) + + self.swapped_pitcher_data = None def on_flip_inning(self, game): - current_pitcher = game.get_pitcher() + original_pitcher = game.get_pitcher() if game.top_of_inning: bat_team = game.teams["home"] counter = self.counter_home @@ -151,19 +179,28 @@ class HeatWave(Weather): bat_team = game.teams["away"] counter = self.counter_away - should_change_pitcher = False - if game.inning >= counter: - should_change_pitcher = True + if game.inning == counter: if game.top_of_inning: self.counter_home = self.counter_home - (self.counter_home % 5) + 5 + random.randint(1,4) #rounds down to last 5, adds up to next 5. then adds a random number 2<=x<=5 to determine next pitcher else: self.counter_away = self.counter_away - (self.counter_away % 5) + 5 + random.randint(1,4) - if should_change_pitcher: + #swap, accounting for teams where where someone's both batter and pitcher tries = 0 - while game.get_pitcher() == current_pitcher and tries < 3: + while game.get_pitcher() == original_pitcher and tries < 3: bat_team.set_pitcher(use_lineup = True) tries += 1 + if game.get_pitcher() != original_pitcher: + self.swapped_pitcher_data = (original_pitcher, game.get_pitcher()) + + def modify_top_of_inning_message(self, game, state): + if self.swapped_pitcher_data: + original, sub = self.swapped_pitcher_data + self.swapped_pitcher_data = None + state["update_emoji"] = self.emoji + state["update_text"] += f' {original} is exhausted from the heat. {sub} is forced to pitch!' + + class Drizzle(Weather): def __init__(self,game): @@ -179,6 +216,18 @@ class Drizzle(Weather): lineup = game.teams[next_team].lineup game.bases[2] = lineup[(game.teams[next_team].lineup_position-1) % len(lineup)] + def modify_top_of_inning_message(self, game, state): + if game.top_of_inning: + next_team = "away" + else: + next_team = "home" + + placed_player = game.teams[next_team].lineup[(game.teams[next_team].lineup_position-1) % len(game.teams[next_team].lineup)] + + state["update_emoji"] = self.emoji + state["update_text"] += f'Due to inclement weather, {placed_player.name} is placed on second base.' + + class Sun2(Weather): def __init__(self, game): self.name = "Sun 2" @@ -194,6 +243,7 @@ class Sun2(Weather): result.update({ "text": "The {} collect 10! Sun 2 smiles.".format(team.name), "text_only": True, + "weather_message": True }) class NameSwappyWeather(Weather): @@ -280,14 +330,14 @@ def all_weathers(): #"Supernova" : Supernova, #"Midnight": Midnight, #"Slight Tailwind": SlightTailwind, - "Heavy Snow": HeavySnow, # works - "Twilight" : Twilight, # works - "Thinned Veil" : ThinnedVeil, # works - "Heat Wave" : HeatWave, - "Drizzle" : Drizzle, # works + "Heavy Snow": HeavySnow, + "Twilight" : Twilight, + "Thinned Veil" : ThinnedVeil, + "Heat Wave" : HeatWave, + "Drizzle" : Drizzle, # "Sun 2": Sun2, - "Feedback": Feedback, - "Literacy": NameSwappyWeather, + "Feedback": Feedback, + "Literacy": NameSwappyWeather, } return weathers_dic From 067f07e8bb613d7717fa7bc17c4b69c941b2ea50 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 19:37:53 -0500 Subject: [PATCH 06/15] Fix Slight Tailwind --- weather.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/weather.py b/weather.py index 1abc16d..b14cd9e 100644 --- a/weather.py +++ b/weather.py @@ -62,19 +62,17 @@ class SlightTailwind(Weather): def activate(self, game, result): if game.top_of_inning: offense_team = game.teams["away"] - weather_count = self.counter_away defense_team = game.teams["home"] else: offense_team = game.teams["home"] - weather_count = self.counter_home defense_team = game.teams["away"] if "mulligan" not in game.last_update[0].keys() and not result["ishit"] and result["text"] != appearance_outcomes.walk: - mulligan_roll_target = -((((self.get_batter().stlats["batting_stars"])-5)/6)**2)+1 - if random.random() > mulligan_roll_target and self.get_batter().stlats["batting_stars"] <= 5: + mulligan_roll_target = -((((game.get_batter().stlats["batting_stars"])-5)/6)**2)+1 + if random.random() > mulligan_roll_target and game.get_batter().stlats["batting_stars"] <= 5: result.clear() result.update({ - "text": f"{this_game.last_update[0]['batter']} would have gone out, but they took a mulligan!", + "text": f"{game.get_batter()} would have gone out, but they took a mulligan!", "text_only": True, "weather_message": True, }) @@ -250,7 +248,7 @@ class NameSwappyWeather(Weather): def __init__(self, game): self.name = "Literacy" self.emoji = "📚" - self.activation_chance = 0.01 + self.activation_chance = 0.05 def activate(self, game, result): if random.random() < self.activation_chance: @@ -331,7 +329,7 @@ def all_weathers(): "Midnight": Midnight, "Slight Tailwind": SlightTailwind, "Heavy Snow": HeavySnow, - "Twilight" : Twilight, + "Twilight" : Twilight, "Thinned Veil" : ThinnedVeil, "Heat Wave" : HeatWave, "Drizzle" : Drizzle, From d44f757c7a675fe6e0de745780af8a35aa067853 Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 19:50:28 -0500 Subject: [PATCH 07/15] Rename name swapping weather to Breezy --- weather.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/weather.py b/weather.py index b14cd9e..8996a19 100644 --- a/weather.py +++ b/weather.py @@ -244,10 +244,10 @@ class Sun2(Weather): "weather_message": True }) -class NameSwappyWeather(Weather): +class Breezy(Weather): def __init__(self, game): - self.name = "Literacy" - self.emoji = "📚" + self.name = "Breezy" + self.emoji = "🎐" self.activation_chance = 0.05 def activate(self, game, result): @@ -335,7 +335,7 @@ def all_weathers(): "Drizzle" : Drizzle, # "Sun 2": Sun2, "Feedback": Feedback, - "Literacy": NameSwappyWeather, + "Breezy": Breezy, } return weathers_dic From 3e95916ddc8d7a17cd0357bedbe78866a93d85af Mon Sep 17 00:00:00 2001 From: hillexed Date: Mon, 22 Feb 2021 01:52:01 -0500 Subject: [PATCH 08/15] Fix inning numbering off-by-one --- games.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/games.py b/games.py index 9739055..732484c 100644 --- a/games.py +++ b/games.py @@ -622,7 +622,7 @@ class game(object): self.choose_next_batter() - if not self.top_of_inning: + if self.top_of_inning: self.inning += 1 if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over self.over = True From c92b44d3ba0c74381c5b4a920bc6b86b942ab88b Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 11:58:44 -0500 Subject: [PATCH 09/15] added comment for OBL --- games.py | 1 + 1 file changed, 1 insertion(+) diff --git a/games.py b/games.py index ed39dae..722ccec 100644 --- a/games.py +++ b/games.py @@ -674,6 +674,7 @@ 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 + #do the One Big League stuff here self.top_of_inning = not self.top_of_inning if self.weather.name == "Drizzle": From 51215cba5917f0e607036d3de94a8cbbd9133ca2 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 14:27:04 -0500 Subject: [PATCH 10/15] designed and implemented the One Big League --- database.py | 113 ++++++++++++++++++++++++++++++++++++++++++++ games.py | 10 +++- main_controller.py | 2 +- the-prestige.pyproj | 2 + the_prestige.py | 57 ++++++++++++++++++++++ 5 files changed, 182 insertions(+), 2 deletions(-) diff --git a/database.py b/database.py index 6e89b73..950e7fc 100644 --- a/database.py +++ b/database.py @@ -1,6 +1,7 @@ #handles the database interactions import os, json, datetime, re import sqlite3 as sql +from random import sample data_dir = "data" @@ -71,6 +72,14 @@ def initialcheck(): owner_id integer ); """ + one_big_league_check_string = """ CREATE TABLE IF NOT EXISTS one_big_league ( + counter integer PRIMARY KEY, + team_name text NOT NULL, + teams_beaten_list text, + current_opponent_pool text, + obl_points int DEFAULT 0 + );""" + if conn is not None: c = conn.cursor() c.execute(soulscream_table_check_string) @@ -78,6 +87,7 @@ def initialcheck(): c.execute(player_table_check_string) c.execute(player_stats_table_check_string) c.execute(teams_table_check_string) + c.execute(one_big_league_check_string) conn.commit() conn.close() @@ -297,6 +307,26 @@ def get_all_teams(): conn.close() return None +def get_all_team_names(): + conn = create_connection() + if conn is not None: + c = conn.cursor() + c.execute("SELECT name FROM teams") + team_names = c.fetchall() + team_names_out = [name for (name,) in team_names] + conn.close() + return team_names_out + conn.close() + return None + +def get_filtered_teams(filter_list): + teams_list = get_all_team_names() + out_list = [] + for team in teams_list: + if team not in filter_list: + out_list.append(team) + return out_list + def search_teams(search_string): conn = create_connection() if conn is not None: @@ -327,3 +357,86 @@ def add_stats(player_game_stats_list): c.execute(f"UPDATE stats SET {stat} = ? WHERE name=?",(player_stats_dic[stat],name)) conn.commit() conn.close() + + +def add_team_obl(team): + conn = create_connection() + if conn is not None: + c=conn.cursor() + opponents = sample(get_filtered_teams([team.name]), 5) + c.execute("INSERT INTO one_big_league(team_name, current_opponent_pool) VALUES (?, ?)", (team.name, list_to_newline_string(opponents))) + + conn.commit() + conn.close() + +def save_obl_results(winning_team, losing_team): + conn = create_connection() + if conn is not None: + c=conn.cursor() + c.execute("SELECT teams_beaten_list, current_opponent_pool, obl_points FROM one_big_league WHERE team_name = ?", (winning_team.name,)) + try: + beaten_string, opponents_string, obl_points = c.fetchone() + except TypeError: #add team to OBL + add_team_obl(winning_team) + add_team_obl(losing_team) + else: + beaten_teams = newline_string_to_list(beaten_string) + opponent_teams = newline_string_to_list(opponents_string) + if losing_team.name in opponent_teams: + beaten_teams.append(losing_team.name) + opponent_teams = sample(get_filtered_teams([winning_team.name]), 5) + obl_points += 1 + + c.execute("UPDATE one_big_league SET teams_beaten_list = ?, current_opponent_pool = ?, obl_points = ? WHERE team_name = ?", (list_to_newline_string(beaten_teams), list_to_newline_string(opponent_teams), obl_points, winning_team.name)) + + conn.commit() + conn.close() + return + +def get_obl_stats(team): + conn = create_connection() + if conn is not None: + c=conn.cursor() + opponents_string = None + while opponents_string is None: + c.execute("SELECT teams_beaten_list, current_opponent_pool FROM one_big_league WHERE team_name = ?", (team.name,)) + try: + beaten_string, opponents_string = c.fetchone() + except TypeError: #add team to OBL + add_team_obl(team) + + beaten_teams = newline_string_to_list(beaten_string) + opponent_teams = opponents_string + obl_points = len(beaten_teams) + + teams_list = [name for name, points in obl_leaderboards()] + rank = teams_list.index(team.name) + 1 + + return (obl_points, opponent_teams, rank) + conn.close() + return (None, None) + +def obl_leaderboards(): + conn = create_connection() + if conn is not None: + c=conn.cursor() + c.execute("SELECT team_name, obl_points FROM one_big_league ORDER BY obl_points DESC") + teams_list = c.fetchall() + + return teams_list #element (team_name, obl_points) + conn.close() + return False + +def list_to_newline_string(list): + string = "" + for element in list: + if string != "": + string += "\n" + string += element + return string + +def newline_string_to_list(string): + if string is not None and string != "": + return string.split("\n") + else: + return [] \ No newline at end of file diff --git a/games.py b/games.py index 30df172..283edf3 100644 --- a/games.py +++ b/games.py @@ -626,7 +626,7 @@ 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 - #do the One Big League stuff here + 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"]) def end_of_game_report(self): @@ -791,3 +791,11 @@ def search_team(search_term): teams.append(team_json) return teams + +def get_filtered_teams(teams_to_remove): + teams = [] + for team_pickle in db.get_all_teams(): + this_team = jsonpickle.decode(team_pickle[0], keys=True, classes=team) + if this_team.name not in teams_to_remove: + teams.append(this_team) + return teams \ No newline at end of file diff --git a/main_controller.py b/main_controller.py index ca693a2..7f5c17f 100644 --- a/main_controller.py +++ b/main_controller.py @@ -236,4 +236,4 @@ def update_loop(): state["update_pause"] -= 1 socketio.emit("states_update", game_states) - time.sleep(8) + time.sleep(1) diff --git a/the-prestige.pyproj b/the-prestige.pyproj index 090ed69..7ad6158 100644 --- a/the-prestige.pyproj +++ b/the-prestige.pyproj @@ -29,6 +29,7 @@ Code + Code @@ -46,6 +47,7 @@ + diff --git a/the_prestige.py b/the_prestige.py index 4787f60..6a497b3 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1233,6 +1233,60 @@ class LeagueRenameCommand(Command): else: await msg.channel.send("We can't find that league.") +class OBLExplainCommand(Command): + name = "oblhelp" + template = "m;oblhelp" + description = "Explains the One Big League!" + + async def execute(self, msg, command): + await msg.channel.send("""The One Big League, or OBL, is an asynchronous league that includes every team in the simsim's database. To participate, just use the m;oblteam command with your team of choice. No signup is required! This will give a list of five opponents; playing against one of them and winning nets you a point, and will refresh the list with five new opponents. Each meta-season will last for a few weeks, after which the leaderboards are reset to start the race again! + +Look out for the people wrestling emoji, which indicates the potential for a :people_wrestling:Wrassle Match:people_wrestling:, where both teams are on each others' lists and both have the opportunity to score a point. Team rankings and points can also be viewed in the oblteam command, and the overall OBL leaderboard can be checked with the m;oblstandings command. Best of luck!! +""") + +class OBLLeaderboardCommand(Command): + name = "oblstandings" + template = "m;oblstandings" + description = "Displays the 15 teams with the most OBL points in this meta-season." + + async def execute(self, msg, command): + leaders_list = db.obl_leaderboards()[:15] + leaders = {} + rank = 1 + for team, points in leaders_list: + leaders[team] = {"rank" : rank, "points" : points} + rank += 1 + + embed = discord.Embed(color=discord.Color.red(), title="The One Big League") + for team in leaders.keys(): + embed.add_field(name=f"{leaders[team]['rank']}. {team}", value=f"{leaders[team]['points']} points" , inline = False) + await msg.channel.send(embed=embed) + +class OBLTeamCommand(Command): + name = "oblteam" + template = "m;oblteam [team name]" + description = "Displays a team's rank, current OBL points, and current opponent selection." + + async def execute(self, msg, command): + team = get_team_fuzzy_search(command.strip()) + if team is None: + await msg.channel.send("Sorry boss, we can't find that team.") + return + + points, opponents_string, rank = db.get_obl_stats(team) + opponents_list = db.newline_string_to_list(opponents_string) + for index in range(0, len(opponents_list)): + oppteam = get_team_fuzzy_search(opponents_list[index]) + opplist = db.get_obl_stats(oppteam)[1] + if team.name in opplist: + opponents_list[index] = opponents_list[index] + " 🤼" + + embed = discord.Embed(color=discord.Color.red(), title=f"{team.name} in the One Big League") + embed.add_field(name="OBL Points", value=points) + embed.add_field(name="Rank", value=rank) + embed.add_field(name="Opponent Pool", value=opponents_string, inline=False) + await msg.channel.send(embed=embed) + commands = [ IntroduceCommand(), @@ -1256,6 +1310,9 @@ commands = [ StartGameCommand(), StartRandomGameCommand(), StartTournamentCommand(), + OBLExplainCommand(), + OBLTeamCommand(), + OBLLeaderboardCommand(), LeagueClaimCommand(), LeagueAddOwnersCommand(), StartLeagueCommand(), From 67e1a0d80184d8444c711f8f6065fb4ce4d0945f Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 14:38:21 -0500 Subject: [PATCH 11/15] adjusted the obl help text --- the_prestige.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the_prestige.py b/the_prestige.py index 6a497b3..bd99d2e 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1239,7 +1239,7 @@ class OBLExplainCommand(Command): description = "Explains the One Big League!" async def execute(self, msg, command): - await msg.channel.send("""The One Big League, or OBL, is an asynchronous league that includes every team in the simsim's database. To participate, just use the m;oblteam command with your team of choice. No signup is required! This will give a list of five opponents; playing against one of them and winning nets you a point, and will refresh the list with five new opponents. Each meta-season will last for a few weeks, after which the leaderboards are reset to start the race again! + await msg.channel.send("""The One Big League, or OBL, is an asynchronous league that includes every team in the simsim's database. To participate, just use the m;oblteam command with your team of choice. **No signup is required!** This will give you a list of five opponents; playing against one of them and winning nets you a point, and will refresh the list with five new opponents. **Losing results in no penalty!** Each meta-season will last for a few weeks, after which the leaderboards are reset to start the race again! Look out for the people wrestling emoji, which indicates the potential for a :people_wrestling:Wrassle Match:people_wrestling:, where both teams are on each others' lists and both have the opportunity to score a point. Team rankings and points can also be viewed in the oblteam command, and the overall OBL leaderboard can be checked with the m;oblstandings command. Best of luck!! """) From 4e18b62e2a38a7fa11638c0752ed7b10ffbf33bc Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 15:07:18 -0500 Subject: [PATCH 12/15] disabled turbo mode, added oblwins --- database.py | 8 ++++--- main_controller.py | 2 +- the_prestige.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/database.py b/database.py index 950e7fc..2c62c64 100644 --- a/database.py +++ b/database.py @@ -393,7 +393,7 @@ def save_obl_results(winning_team, losing_team): conn.close() return -def get_obl_stats(team): +def get_obl_stats(team, beaten = False): conn = create_connection() if conn is not None: c=conn.cursor() @@ -411,8 +411,10 @@ def get_obl_stats(team): teams_list = [name for name, points in obl_leaderboards()] rank = teams_list.index(team.name) + 1 - - return (obl_points, opponent_teams, rank) + if not beaten: + return (obl_points, opponent_teams, rank) + else: + return (obl_points, beaten_teams, rank) conn.close() return (None, None) diff --git a/main_controller.py b/main_controller.py index 7f5c17f..ca693a2 100644 --- a/main_controller.py +++ b/main_controller.py @@ -236,4 +236,4 @@ def update_loop(): state["update_pause"] -= 1 socketio.emit("states_update", game_states) - time.sleep(1) + time.sleep(8) diff --git a/the_prestige.py b/the_prestige.py index bd99d2e..8fdc493 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1287,6 +1287,59 @@ class OBLTeamCommand(Command): embed.add_field(name="Opponent Pool", value=opponents_string, inline=False) await msg.channel.send(embed=embed) +class OBLConqueredCommand(Command): + name = "oblwins" + template = "m;oblwins [team name]" + description = "Displays all teams that a given team has won points off of." + + async def execute(self, msg, command): + team = get_team_fuzzy_search(command.strip()) + if team is None: + await msg.channel.send("Sorry boss, we can't find that team.") + return + + points, teams, rank = db.get_obl_stats(team, beaten=True) + pages = [] + page_max = math.ceil(len(teams)/25) + + title_text = f"Rank {rank}: {team.name}" + + for page in range(0,page_max): + embed = discord.Embed(color=discord.Color.red(), title=title_text) + embed.set_footer(text = f"{points} OBL Points") + for i in range(0,25): + try: + thisteam = games.get_team(teams[i+25*page]) + if thisteam.slogan.strip() != "": + embed.add_field(name=thisteam.name, value=thisteam.slogan) + else: + embed.add_field(name=thisteam.name, value="404: Slogan not found") + except: + break + pages.append(embed) + + teams_list = await msg.channel.send(embed=pages[0]) + current_page = 0 + + if page_max > 1: + await teams_list.add_reaction("◀") + await teams_list.add_reaction("▶") + + def react_check(react, user): + return user == msg.author and react.message == teams_list + + while True: + try: + react, user = await client.wait_for('reaction_add', timeout=60.0, check=react_check) + if react.emoji == "◀" and current_page > 0: + current_page -= 1 + await react.remove(user) + elif react.emoji == "▶" and current_page < (page_max-1): + current_page += 1 + await react.remove(user) + await teams_list.edit(embed=pages[current_page]) + except asyncio.TimeoutError: + return commands = [ IntroduceCommand(), @@ -1312,6 +1365,7 @@ commands = [ StartTournamentCommand(), OBLExplainCommand(), OBLTeamCommand(), + OBLConqueredCommand(), OBLLeaderboardCommand(), LeagueClaimCommand(), LeagueAddOwnersCommand(), From de0288a1bf6bf571df0a159d1e2dcf700461fc21 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 18:35:40 -0500 Subject: [PATCH 13/15] obl rivals initial version --- database.py | 22 ++++++++++++++++------ the_prestige.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/database.py b/database.py index 2c62c64..8c4ec3d 100644 --- a/database.py +++ b/database.py @@ -77,7 +77,8 @@ def initialcheck(): team_name text NOT NULL, teams_beaten_list text, current_opponent_pool text, - obl_points int DEFAULT 0 + obl_points int DEFAULT 0, + rival_name text );""" if conn is not None: @@ -393,15 +394,15 @@ def save_obl_results(winning_team, losing_team): conn.close() return -def get_obl_stats(team, beaten = False): +def get_obl_stats(team, full = False): conn = create_connection() if conn is not None: c=conn.cursor() opponents_string = None while opponents_string is None: - c.execute("SELECT teams_beaten_list, current_opponent_pool FROM one_big_league WHERE team_name = ?", (team.name,)) + c.execute("SELECT teams_beaten_list, current_opponent_pool, rival_name FROM one_big_league WHERE team_name = ?", (team.name,)) try: - beaten_string, opponents_string = c.fetchone() + beaten_string, opponents_string, rival_name = c.fetchone() except TypeError: #add team to OBL add_team_obl(team) @@ -411,10 +412,10 @@ def get_obl_stats(team, beaten = False): teams_list = [name for name, points in obl_leaderboards()] rank = teams_list.index(team.name) + 1 - if not beaten: + if not full: return (obl_points, opponent_teams, rank) else: - return (obl_points, beaten_teams, rank) + return (obl_points, beaten_teams, opponent_teams, rank, rival_name) conn.close() return (None, None) @@ -429,6 +430,15 @@ def obl_leaderboards(): conn.close() return False +def set_obl_rival(base_team, rival): + conn = create_connectio() + if conn is not None: + c=conn.cursor() + + c.execute("UPDATE one_big_league SET rival_name = ? WHERE team_name = ?", (rival.name, base_team.name)) + conn.commit() + conn.close() + def list_to_newline_string(list): string = "" for element in list: diff --git a/the_prestige.py b/the_prestige.py index 8fdc493..94a4c04 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1273,20 +1273,44 @@ class OBLTeamCommand(Command): await msg.channel.send("Sorry boss, we can't find that team.") return - points, opponents_string, rank = db.get_obl_stats(team) + rival_team = None + points, beaten_teams_list, opponents_string, rank, rival_name = db.get_obl_stats(team, full=True) opponents_list = db.newline_string_to_list(opponents_string) for index in range(0, len(opponents_list)): oppteam = get_team_fuzzy_search(opponents_list[index]) opplist = db.get_obl_stats(oppteam)[1] if team.name in opplist: opponents_list[index] = opponents_list[index] + " 🤼" + if rival_name is not None: + rival_team = games.get_team(rival_name) embed = discord.Embed(color=discord.Color.red(), title=f"{team.name} in the One Big League") embed.add_field(name="OBL Points", value=points) embed.add_field(name="Rank", value=rank) embed.add_field(name="Opponent Pool", value=opponents_string, inline=False) + if rival_team is not None: + embed.add_field(name="Rival", value=f"{rival_team.name}\n{rival_team.slogan}") await msg.channel.send(embed=embed) +class OBLSetRivalCommand(Command): + name = "oblteam" + template = "m;oblrival\n[your team name]\n[rival team name]" + description = "Sets your team's OBL rival. Can be changed at any time. Requires ownership." + + async def execute(self, msg, command): + team_i = get_team_fuzzy_search(command.split("\n")[1].strip()) + team_r = get_team_fuzzy_search(command.split("\n")[2].strip()) + team, owner_id = games.get_team_and_owner(team_i.name) + if team is None or team_r is None: + await msg.channel.send("Can't find one of those teams, boss. Typo?") + return + elif owner_id != msg.author.id and msg.author.id not in config()["owners"]: + await msg.channel.send("You're not authorized to mess with this team. Sorry, boss.") + return + try: + db.set_obl_rival(team, team_r) + await msg.channel.send("One pair of mortal enemies, coming right up. Unless you're more of the 'enemies to lovers' type. We can manage that too, don't worry.") + class OBLConqueredCommand(Command): name = "oblwins" template = "m;oblwins [team name]" @@ -1298,7 +1322,7 @@ class OBLConqueredCommand(Command): await msg.channel.send("Sorry boss, we can't find that team.") return - points, teams, rank = db.get_obl_stats(team, beaten=True) + points, teams, oppTeams, rank, rivalName = db.get_obl_stats(team, full=True) pages = [] page_max = math.ceil(len(teams)/25) From b2fc07150239c3aa9f49a9e4adb3f01f5e82c465 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 18:49:36 -0500 Subject: [PATCH 14/15] finalized the rival system --- database.py | 2 +- the_prestige.py | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/database.py b/database.py index 8c4ec3d..8e5df7e 100644 --- a/database.py +++ b/database.py @@ -431,7 +431,7 @@ def obl_leaderboards(): return False def set_obl_rival(base_team, rival): - conn = create_connectio() + conn = create_connection() if conn is not None: c=conn.cursor() diff --git a/the_prestige.py b/the_prestige.py index 94a4c04..1eac92c 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1281,19 +1281,22 @@ class OBLTeamCommand(Command): opplist = db.get_obl_stats(oppteam)[1] if team.name in opplist: opponents_list[index] = opponents_list[index] + " 🤼" + if rival_name == opponents_list[index]: + opponents_list[index] = opponents_list[index] + " 😈" if rival_name is not None: rival_team = games.get_team(rival_name) + opponents_string = db.list_to_newline_string(opponents_list) embed = discord.Embed(color=discord.Color.red(), title=f"{team.name} in the One Big League") embed.add_field(name="OBL Points", value=points) embed.add_field(name="Rank", value=rank) - embed.add_field(name="Opponent Pool", value=opponents_string, inline=False) + embed.add_field(name="Bounty Board", value=opponents_string, inline=False) if rival_team is not None: - embed.add_field(name="Rival", value=f"{rival_team.name}\n{rival_team.slogan}") + embed.add_field(name="Rival", value=f"**{rival_team.name}**\n{rival_team.slogan}") await msg.channel.send(embed=embed) class OBLSetRivalCommand(Command): - name = "oblteam" + name = "oblrival" template = "m;oblrival\n[your team name]\n[rival team name]" description = "Sets your team's OBL rival. Can be changed at any time. Requires ownership." @@ -1307,9 +1310,11 @@ class OBLSetRivalCommand(Command): elif owner_id != msg.author.id and msg.author.id not in config()["owners"]: await msg.channel.send("You're not authorized to mess with this team. Sorry, boss.") return - try: - db.set_obl_rival(team, team_r) - await msg.channel.send("One pair of mortal enemies, coming right up. Unless you're more of the 'enemies to lovers' type. We can manage that too, don't worry.") + #try: + db.set_obl_rival(team, team_r) + await msg.channel.send("One pair of mortal enemies, coming right up. Unless you're more of the 'enemies to lovers' type. We can manage that too, don't worry.") + #except: + #await msg.channel.send("Hm. We don't think that team has tried to do anything in the One Big League yet, so you'll have to wait for consent. Get them to check their bounty board.") class OBLConqueredCommand(Command): name = "oblwins" @@ -1389,6 +1394,7 @@ commands = [ StartTournamentCommand(), OBLExplainCommand(), OBLTeamCommand(), + OBLSetRivalCommand(), OBLConqueredCommand(), OBLLeaderboardCommand(), LeagueClaimCommand(), From af750ff15dbb5401f4a581e585535db143204a05 Mon Sep 17 00:00:00 2001 From: Sakimori Date: Mon, 22 Feb 2021 18:57:07 -0500 Subject: [PATCH 15/15] really finalized rivals --- the_prestige.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/the_prestige.py b/the_prestige.py index 1eac92c..e8a3626 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1292,7 +1292,12 @@ class OBLTeamCommand(Command): embed.add_field(name="Rank", value=rank) embed.add_field(name="Bounty Board", value=opponents_string, inline=False) if rival_team is not None: - embed.add_field(name="Rival", value=f"**{rival_team.name}**\n{rival_team.slogan}") + r_points, r_beaten_teams_list, r_opponents_string, r_rank, r_rival_name = db.get_obl_stats(rival_team, full=True) + embed.add_field(name="Rival", value=f"**{rival_team.name}**: Rank {r_rank}\n{rival_team.slogan}\nPoints: {r_points}") + if r_rival_name == team.name: + embed.set_footer(text="🔥") + else: + embed.set_footer(text="Set a rival with m;oblrival!") await msg.channel.send(embed=embed) class OBLSetRivalCommand(Command):