From e478589b8ece2a4ec538374b1e4433e7b67a314b Mon Sep 17 00:00:00 2001 From: hillexed Date: Sun, 21 Feb 2021 03:03:25 -0500 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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