From ab45b5f057704b36aed48292e8f406da065449ac Mon Sep 17 00:00:00 2001 From: Sakimori Date: Tue, 22 Dec 2020 01:56:33 -0500 Subject: [PATCH] functional blaseball games with a preset team. --- database.py | 1 + games.py | 303 ++++++++++++++++++++++++++++++++++++++++++++++-- the_prestige.py | 21 +++- 3 files changed, 313 insertions(+), 12 deletions(-) diff --git a/database.py b/database.py index bb5e91a..3fd652e 100644 --- a/database.py +++ b/database.py @@ -43,6 +43,7 @@ def initialcheck(): runs_allowed integer DEFAULT 0, plate_appearances integer DEFAULT 0, walks_taken integer DEFAULT 0, + sacrifices integer DEFAULT 0, hits integer DEFAULT 0, home_runs integer DEFAULT 0, total_bases integer DEFAULT 0, diff --git a/games.py b/games.py index 6a6105f..858d433 100644 --- a/games.py +++ b/games.py @@ -41,7 +41,24 @@ class player(object): self.stlats = json.loads(json_string) self.id = self.stlats["id"] self.name = self.stlats["name"] - self.game_stats = {} + self.game_stats = { + "outs_pitched" : 0, + "walks_allowed" : 0, + "hits_allowed" : 0, + "strikeouts_given" : 0, + "runs_allowed" : 0, + "plate_appearances" : 0, + "walks_taken" : 0, + "sacrifices" : 0, + "hits" : 0, + "home_runs" : 0, + "total_bases" : 0, + "rbis" : 0, + "strikeouts_taken" : 0 + } + + def __str__(self): + return self.name class team(object): @@ -50,6 +67,7 @@ class team(object): self.lineup = [] self.lineup_position = 0 self.pitcher = None + self.score = 0 def add_lineup(self, new_player): if len(self.lineup) <= 12: @@ -77,9 +95,12 @@ class team(object): class game(object): def __init__(self, team1, team2, length=None): + self.over = False self.teams = {"away" : team1, "home" : team2} self.inning = 1 + self.outs = 0 self.top_of_inning = True + self.last_update = None if length is not None: self.max_innings = length else: @@ -100,21 +121,25 @@ class game(object): return self.teams["away"].pitcher def at_bat(self): + outcome = {} pitcher = self.get_pitcher() batter = self.get_batter() + if self.top_of_inning: defender = random.choice(self.teams["home"].lineup) else: defender = random.choice(self.teams["away"].lineup) + outcome["batter"] = batter + outcome["defender"] = "" + bat_stat = random_star_gen("batting_stars", batter) pitch_stat = random_star_gen("pitching_stars", pitcher) - def_stat = random_star_gen("defense_stars", defender) 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) - outcome = {} + if pb_system_stat <= 0: outcome["ishit"] = False fc_flag = False @@ -129,13 +154,21 @@ class game(object): else: outcome["text"] = appearance_outcomes.walk - if self.bases[1] is not None and hitnum < 1: + if self.bases[1] is not None and hitnum < -1 and self.outs != 2: outcome["text"] = appearance_outcomes.doubleplay + outcome["defender"] = "" + for base in self.bases.values(): if base is not None: fc_flag = True - if fc_flag and 1 <= hitnum and hitnum < 2: - outcome["text"] = appearance_outcomes.fielderschoice + + if fc_flag and self.outs < 2: + if 0 <= hitnum and hitnum < 1.5: + outcome["text"] = appearance_outcomes.fielderschoice + outcome["defender"] = "" + elif 2.5 <= hitnum: + if self.bases[2] is not None or self.bases[3] is not None: + outcome["advance"] = True else: outcome["ishit"] = True if hitnum < 1: @@ -149,12 +182,228 @@ class game(object): outcome["text"] = appearance_outcomes.grandslam else: outcome["text"] = appearance_outcomes.homerun - - if self.top_of_inning: - self.teams["away"].lineup_position += 1 - else: - self.teams["home"].lineup_position += 1 return outcome + + def baserunner_check(self, defender, outcome): + def_stat = random_star_gen("defense_stars", defender) + if outcome["text"] == appearance_outcomes.homerun or outcome["text"] == appearance_outcomes.grandslam: + runs = 1 + for base in self.bases.values(): + if base is not None: + runs += 1 + self.bases = {1 : None, 2 : None, 3 : None} + return runs + + elif "advance" in outcome.keys(): + if self.bases[3] is not None: + self.bases[3] = None + runs = 1 + if self.bases[2] is not None: + runs = 0 + run_roll = random.gauss(math.erf(random_star_gen("baserunning_stars", self.bases[2])-def_stat)-.5,1.5) + if run_roll > 0: + self.bases[3] = self.bases[2] + self.bases[2] = None + return runs + + elif outcome["text"] == appearance_outcomes.fielderschoice: + for base in range(3, 0, -1): + if self.bases[base] is not None: + self.fc_out = self.bases[base] + for movebase in range(base,1,-1): + self.bases[movebase] = self.bases[movebase-1] + break + self.bases[1] = self.get_batter() + return 0 + + elif outcome["ishit"]: + runs = 0 + if outcome["text"] == appearance_outcomes.single: + if self.bases[3] is not None: + runs += 1 + self.bases[3] = None + if self.bases[2] is not None: + run_roll = random.gauss(math.erf(random_star_gen("baserunning_stars", self.bases[2])-def_stat)-.5,1.5) + if run_roll > 0: + runs += 1 + else: + self.bases[3] = self.bases[2] + self.bases[2] = None + if self.bases[1] is not None: + if self.bases[3] is None: + run_roll = random.gauss(math.erf(random_star_gen("baserunning_stars", self.bases[1])-def_stat)-.5,1.5) + if run_roll > 0.75: + self.bases[3] = self.bases[1] + else: + self.bases[2] = self.bases[1] + else: + self.bases[2] = self.bases[1] + self.bases[1] = None + + self.bases[1] = self.get_batter() + return runs + + elif outcome["text"] == appearance_outcomes.double: + runs = 0 + if self.bases[3] is not None: + runs += 1 + self.bases[3] = None + if self.bases[2] is not None: + runs += 1 + self.bases[2] = None + if self.bases[1] is not None: + run_roll = random.gauss(math.erf(random_star_gen("baserunning_stars", self.bases[1])-def_stat)-.5,1.5) + if run_roll > 1: + runs += 1 + self.bases[1] = None + else: + self.bases[3] = self.bases[1] + self.bases[1] = None + self.bases[2] = self.get_batter() + return runs + + + elif outcome["text"] == appearance_outcomes.triple: + runs = 0 + for basenum in self.bases.keys(): + if self.bases[basenum] is not None: + runs += 1 + self.bases[basenum] = None + self.bases[3] = self.get_batter() + return runs + + + def batterup(self): + scores_to_add = 0 + result = self.at_bat() + self.get_batter() + if self.top_of_inning: + offense_team = self.teams["away"] + defense_team = self.teams["home"] + defender = random.choice(self.teams["home"].lineup) + else: + offense_team = self.teams["home"] + defense_team = self.teams["away"] + defender = random.choice(self.teams["away"].lineup) + + if result["ishit"]: #if batter gets a hit: + self.get_batter().game_stats["hits"] += 1 + self.get_pitcher().game_stats["hits_allowed"] += 1 + + if result["text"] == appearance_outcomes.single: + self.get_batter().game_stats["total_bases"] += 1 + elif result["text"] == appearance_outcomes.double: + self.get_batter().game_stats["total_bases"] += 2 + elif result["text"] == appearance_outcomes.triple: + self.get_batter().game_stats["total_bases"] += 3 + 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 + + scores_to_add += self.baserunner_check(defender, result) + + else: #batter did not get a hit + if result["text"] == appearance_outcomes.walk: + walkers = [(0,self.get_batter())] + for base in range(1,4): + if self.bases[base] == None: + break + walkers.append((base, self.bases[base])) + for i in range(0, len(walkers)): + this_walker = walkers.pop() + if this_walker[0] == 3: + self.bases[3] = None + scores_to_add += 1 + else: + self.bases[this_walker[0]+1] = this_walker[1] #this moves all consecutive baserunners one forward + + self.get_batter().game_stats["walks_taken"] += 1 + self.get_pitcher().game_stats["walks_allowed"] += 1 + + elif result["text"] == appearance_outcomes.doubleplay: + self.get_pitcher().game_stats["outs_pitched"] += 2 + self.outs += 2 + self.bases[1] = None + + elif result["text"] == appearance_outcomes.fielderschoice: + self.get_pitcher().game_stats["outs_pitched"] += 1 + self.outs += 1 + scores_to_add += self.baserunner_check(defender, result) + + elif "advance" in result.keys(): + self.get_pitcher().game_stats["outs_pitched"] += 1 + self.outs += 1 + if self.bases[3] is not None: + self.get_batter().game_stats["sacrifices"] += 1 + scores_to_add += self.baserunner_check(defender, result) + + elif result["text"] == appearance_outcomes.strikeoutlooking or result["text"] == appearance_outcomes.strikeoutswinging: + self.get_pitcher().game_stats["outs_pitched"] += 1 + self.outs += 1 + self.get_batter().game_stats["strikeouts_taken"] += 1 + self.get_pitcher().game_stats["strikeouts_given"] += 1 + + else: + self.get_pitcher().game_stats["outs_pitched"] += 1 + self.outs += 1 + + offense_team.lineup_position += 1 #put next batter up + offense_team.score += scores_to_add + self.get_batter().game_stats["rbis"] += scores_to_add + self.get_pitcher().game_stats["runs_allowed"] += scores_to_add + + if self.outs >= 3: + self.flip_inning() + + + return (result, scores_to_add) #returns ab information and scores + + def flip_inning(self): + for base in self.bases.keys(): + self.bases[base] = None + self.outs = 0 + if not 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 + self.top_of_inning = not self.top_of_inning + + + def end_of_game_report(self): + return { + "away_team" : self.teams["away"], + "away_pitcher" : self.teams["away"].pitcher, + "home_team" : self.teams["home"], + "home_pitcher" : self.teams["home"].pitcher + } + + def gamestate_update_full(self): + self.last_update = self.batterup() + return self.gamestate_display_full() + + def gamestate_display_full(self): + if not self.over: + if self.top_of_inning: + inningtext = "top" + else: + inningtext = "bottom" + + updatestring = f"{self.last_update[0]['batter']} {self.last_update[0]['text'].value} {self.last_update[0]['defender']}\n" + + if self.last_update[1] > 0: + updatestring += f"{self.last_update[1]} runs scored!" + + return f"""Last update: {updatestring} + +Score: {self.teams['away'].score} - {self.teams['home'].score}. +Current inning: {inningtext} of {self.inning}. {self.outs} outs. +Pitcher: {self.get_pitcher().name} +Batter: {self.get_batter().name} +Bases: 3: {str(self.bases[3])} 2: {str(self.bases[2])} 1: {str(self.bases[1])} +""" + else: + return f"""Game over! Final score: **{self.teams['away'].score} - {self.teams['home'].score}** +Last update: {updatestring}""" @@ -173,3 +422,35 @@ def random_star_gen(key, player): # strikeouts_taken +def debug_game(): + average_player = player('{"id" : "average", "name" : "AJ", "batting_stars" : 2.5, "pitching_stars" : 2.5, "defense_stars" : 2.5, "baserunning_stars" : 2.5}') + average_player2 = player('{"id" : "average", "name" : "Astrid", "batting_stars" : 2.5, "pitching_stars" : 2.5, "defense_stars" : 2.5, "baserunning_stars" : 2.5}') + average_player3 = player('{"id" : "average", "name" : "xvi", "batting_stars" : 2.5, "pitching_stars" : 2.5, "defense_stars" : 2.5, "baserunning_stars" : 2.5}') + average_player4 = player('{"id" : "average", "name" : "Fox", "batting_stars" : 2.5, "pitching_stars" : 2.5, "defense_stars" : 2.5, "baserunning_stars" : 2.5}') + average_player5 = player('{"id" : "average", "name" : "Pigeon", "batting_stars" : 2.5, "pitching_stars" : 2.5, "defense_stars" : 2.5, "baserunning_stars" : 2.5}') + max_player = player('{"id" : "max", "name" : "max", "batting_stars" : 5, "pitching_stars" : 5, "defense_stars" : 5, "baserunning_stars" : 5}') + min_player = player('{"id" : "min", "name" : "min", "batting_stars" : 1, "pitching_stars" : 1, "defense_stars" : 1, "baserunning_stars" : 1}') + team_avg = team() + team_avg.add_lineup(average_player) + team_avg.add_lineup(average_player2) + team_avg.add_lineup(average_player3) + team_avg.add_lineup(average_player4) + team_avg.set_pitcher(average_player5) + team_avg.finalize() + team_avg2 = team() + team_avg2.add_lineup(average_player5) + team_avg2.add_lineup(average_player4) + team_avg2.add_lineup(average_player3) + team_avg2.add_lineup(average_player2) + team_avg2.set_pitcher(average_player) + team_avg2.finalize() + team_min = team() + team_min.add_lineup(min_player) + team_min.set_pitcher(min_player) + team_min.finalize() + + average_game = game(team_avg, team_avg2) + #slugging_game = game(team_max, team_min) + #shutout_game = game(team_min, team_max) + + return average_game \ No newline at end of file diff --git a/the_prestige.py b/the_prestige.py index aa9816d..ebf0030 100644 --- a/the_prestige.py +++ b/the_prestige.py @@ -1,8 +1,9 @@ -import discord, json, os, roman, games +import discord, json, os, roman, games, asyncio import database as db import onomancer as ono client = discord.Client() +gamesarray = [] def config(): if not os.path.exists("config.json"): @@ -101,6 +102,11 @@ async def on_message(msg): await msg.channel.send(f"{batter.name} {atbat['text'].value}") + elif command == "startgame" and msg.author.id in config()["owners"]: + game_task = asyncio.create_task(start_game(msg.channel)) + await game_task + + elif command == "credit": await msg.channel.send("Our avatar was graciously provided to us, with permission, by @HetreaSky on Twitter.") @@ -117,6 +123,19 @@ Our avatar was graciously provided to us, with permission, by @HetreaSky on Twit """ await channel.send(text) +async def start_game(channel): + msg = await channel.send("Play ball!") + await asyncio.sleep(4) + newgame = games.debug_game() + gamesarray.append(newgame) + while not newgame.over: + state = newgame.gamestate_update_full() + if not state.startswith("Game over"): + await msg.edit(content=state) + await asyncio.sleep(10) + await channel.send(state) + gamesarray.pop() + def build_star_embed(player_json): starkeys = {"batting_stars" : "Batting", "pitching_stars" : "Pitching", "baserunning_stars" : "Baserunning", "defense_stars" : "Defense"}