2020-12-24 09:51:38 +00:00
import json , random , os , math , jsonpickle
2020-12-21 05:23:02 +00:00
from enum import Enum
import database as db
def config ( ) :
if not os . path . exists ( " games_config.json " ) :
#generate default config
config_dic = {
" default_length " : 3 ,
" stlat_weights " : {
" batting_stars " : 1 , #batting
2020-12-27 23:09:19 +00:00
" pitching_stars " : 0.8 , #pitching
2020-12-21 05:23:02 +00:00
" baserunning_stars " : 1 , #baserunning
" defense_stars " : 1 #defense
2020-12-27 21:51:41 +00:00
} ,
" stolen_base_chance_mod " : 1 ,
" stolen_base_success_mod " : 1
2020-12-21 05:23:02 +00:00
}
with open ( " games_config.json " , " w " ) as config_file :
json . dump ( config_dic , config_file , indent = 4 )
return config_dic
else :
with open ( " games_config.json " ) as config_file :
return json . load ( config_file )
2020-12-27 22:27:30 +00:00
def all_weathers ( ) :
if not os . path . exists ( " weather_config.json " ) :
#generate default config
super_weather_json = jsonpickle . encode ( weather ( " Supernova " , " 🌟 " ) )
mid_weather_json = jsonpickle . encode ( weather ( " Midnight " , " 🕶 " ) )
2021-01-04 04:14:03 +00:00
crow_weather_json = jsonpickle . encode ( weather ( " Crowstorm " , " 🐦 " ) )
2021-01-04 01:49:51 +00:00
slime_weather_json = jsonpickle . encode ( weather ( " Slime " , " 🟢 " ) )
peanut_weather_json = jsonpickle . encode ( weather ( " Peanado " , " 🥜 " ) )
thickair_weather_json = jsonpickle . encode ( weather ( " Thick Air " , " 🌫️ " ) )
molasses_weather_json = jsonpickle . encode ( weather ( " Molasses " , " 🧇 " ) )
coke_weather_json = jsonpickle . encode ( weather ( " Coke Raine " , " 🥤 " ) )
2020-12-27 22:27:30 +00:00
config_dic = {
" Supernova " : super_weather_json ,
2021-01-04 01:49:51 +00:00
" Midnight " : mid_weather_json ,
" Crowstorm " : crow_weather_json ,
" Slime " : slime_weather_json ,
" Peanado " : peanut_weather_json ,
" Thick Air " : thickair_weather_json ,
" Molasses " : molasses_weather_json ,
" Coke Raine " : coke_weather_json
2020-12-27 22:27:30 +00:00
}
with open ( " weather_config.json " , " w " ) as config_file :
json . dump ( config_dic , config_file , indent = 4 )
with open ( " weather_config.json " ) as config_file :
weather_dic = { }
for weather_json in json . load ( config_file ) . values ( ) :
this_weather = jsonpickle . decode ( weather_json , classes = weather )
weather_dic [ this_weather . name ] = this_weather
return weather_dic
2020-12-21 05:23:02 +00:00
class appearance_outcomes ( Enum ) :
strikeoutlooking = " strikes out looking. "
strikeoutswinging = " strikes out swinging. "
groundout = " grounds out to "
flyout = " flies out to "
2020-12-27 07:06:43 +00:00
fielderschoice = " reaches on fielder ' s choice. {} is out at {} base. " #requires .format(player, base_string)
2020-12-21 05:23:02 +00:00
doubleplay = " grounds into a double play! "
2020-12-26 10:21:14 +00:00
sacrifice = " hits a sacrifice fly towards "
2020-12-21 09:46:12 +00:00
walk = " draws a walk. "
2020-12-21 05:23:02 +00:00
single = " hits a single! "
double = " hits a double! "
triple = " hits a triple! "
homerun = " hits a home run! "
grandslam = " hits a grand slam! "
2021-01-04 02:37:40 +00:00
crows = " is chased away by crows. "
2020-12-21 05:23:02 +00:00
class player ( object ) :
def __init__ ( self , json_string ) :
self . stlats = json . loads ( json_string )
self . id = self . stlats [ " id " ]
self . name = self . stlats [ " name " ]
2020-12-22 06:56:33 +00:00
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
}
2020-12-23 01:20:58 +00:00
def star_string ( self , key ) :
str_out = " "
starstring = str ( self . stlats [ key ] )
if " .5 " in starstring :
starnum = int ( starstring [ 0 ] )
addhalf = True
else :
starnum = int ( starstring [ 0 ] )
addhalf = False
str_out + = " ⭐ " * starnum
if addhalf :
str_out + = " ✨ "
return str_out
2020-12-22 06:56:33 +00:00
def __str__ ( self ) :
return self . name
2020-12-21 05:23:02 +00:00
class team ( object ) :
def __init__ ( self ) :
self . name = None
self . lineup = [ ]
self . lineup_position = 0
2021-01-02 06:10:52 +00:00
self . rotation = [ ]
2020-12-21 05:23:02 +00:00
self . pitcher = None
2020-12-22 06:56:33 +00:00
self . score = 0
2020-12-24 09:51:38 +00:00
self . slogan = None
2020-12-21 05:23:02 +00:00
2021-01-02 09:02:57 +00:00
def find_player ( self , name ) :
for index in range ( 0 , len ( self . lineup ) ) :
if self . lineup [ index ] . name == name :
return ( self . lineup [ index ] , index , self . lineup )
for index in range ( 0 , len ( self . rotation ) ) :
if self . rotation [ index ] . name == name :
return ( self . rotation [ index ] , index , self . rotation )
else :
return ( None , None , None )
2021-01-03 06:04:51 +00:00
def average_stars ( self ) :
total_stars = 0
for _player in self . lineup :
total_stars + = _player . stlats [ " batting_stars " ]
for _player in self . rotation :
total_stars + = _player . stlats [ " pitching_stars " ]
return total_stars / ( len ( self . lineup ) + len ( self . rotation ) )
2021-01-02 06:10:52 +00:00
def swap_player ( self , name ) :
2021-01-02 09:02:57 +00:00
this_player , index , roster = self . find_player ( name )
if this_player is not None and len ( roster ) > 1 :
if roster == self . lineup :
if self . add_pitcher ( this_player ) :
roster . pop ( index )
return True
else :
if self . add_lineup ( this_player ) [ 0 ] :
self . rotation . pop ( index )
return True
2021-01-02 06:10:52 +00:00
return False
2021-01-02 09:02:57 +00:00
def delete_player ( self , name ) :
this_player , index , roster = self . find_player ( name )
if this_player is not None and len ( roster ) > 1 :
roster . pop ( index )
return True
else :
return False
def slide_player ( self , name , new_spot ) :
this_player , index , roster = self . find_player ( name )
2021-01-04 20:37:53 +00:00
if this_player is not None and new_spot < = len ( roster ) :
2021-01-02 09:02:57 +00:00
roster . pop ( index )
roster . insert ( new_spot - 1 , this_player )
return True
else :
return False
2021-01-05 03:02:33 +00:00
2020-12-21 05:23:02 +00:00
def add_lineup ( self , new_player ) :
2020-12-28 08:21:47 +00:00
if len ( self . lineup ) < 20 :
2020-12-21 05:23:02 +00:00
self . lineup . append ( new_player )
return ( True , )
else :
2020-12-28 08:21:47 +00:00
return ( False , " 20 players in the lineup, maximum. We ' re being really generous here. " )
2021-01-05 03:02:33 +00:00
2021-01-02 06:10:52 +00:00
def add_pitcher ( self , new_player ) :
if len ( self . rotation ) < 8 :
self . rotation . append ( new_player )
return True
else :
return False
2021-01-04 01:49:51 +00:00
2021-01-02 06:10:52 +00:00
def set_pitcher ( self , rotation_slot = None , use_lineup = False ) :
temp_rotation = self . rotation . copy ( )
2021-01-05 03:02:33 +00:00
if use_lineup :
2021-01-02 06:10:52 +00:00
for batter in self . rotation :
temp_rotation . append ( batter )
if rotation_slot is None :
self . pitcher = random . choice ( temp_rotation )
else :
self . pitcher = temp_rotation [ rotation_slot % len ( temp_rotation ) ]
2020-12-21 05:23:02 +00:00
def is_ready ( self ) :
2021-01-03 22:59:13 +00:00
try :
return ( len ( self . lineup ) > = 1 and len ( self . rotation ) > 0 )
except AttributeError :
self . rotation = [ self . pitcher ]
self . pitcher = None
return ( len ( self . lineup ) > = 1 and len ( self . rotation ) > 0 )
2020-12-21 05:23:02 +00:00
2020-12-24 09:51:38 +00:00
def prepare_for_save ( self ) :
self . lineup_position = 0
self . score = 0
2021-01-02 06:10:52 +00:00
if self . pitcher is not None and self . pitcher not in self . rotation :
self . rotation . append ( self . pitcher )
self . pitcher = None
2020-12-24 09:51:38 +00:00
for this_player in self . lineup :
for stat in this_player . game_stats . keys ( ) :
this_player . game_stats [ stat ] = 0
2021-01-02 06:10:52 +00:00
for this_player in self . rotation :
for stat in this_player . game_stats . keys ( ) :
this_player . game_stats [ stat ] = 0
2021-01-04 04:18:45 +00:00
return self
2020-12-24 09:51:38 +00:00
2020-12-21 05:23:02 +00:00
def finalize ( self ) :
if self . is_ready ( ) :
2021-01-04 04:18:45 +00:00
self . set_pitcher ( )
2020-12-21 05:23:02 +00:00
while len ( self . lineup ) < = 4 :
self . lineup . append ( random . choice ( self . lineup ) )
2021-01-02 06:10:52 +00:00
return self
2021-01-04 01:49:51 +00:00
else :
2020-12-21 05:23:02 +00:00
return False
class game ( object ) :
2021-01-03 11:06:51 +00:00
def __init__ ( self , team1 , team2 , length = None ) :
2020-12-22 06:56:33 +00:00
self . over = False
2020-12-21 05:23:02 +00:00
self . teams = { " away " : team1 , " home " : team2 }
self . inning = 1
2020-12-22 06:56:33 +00:00
self . outs = 0
2020-12-21 05:23:02 +00:00
self . top_of_inning = True
2020-12-27 21:51:41 +00:00
self . last_update = ( { } , 0 ) #this is a ({outcome}, runs) tuple
2020-12-23 01:20:58 +00:00
self . owner = None
self . ready = False
2020-12-31 08:32:01 +00:00
self . victory_lap = False
2020-12-21 05:23:02 +00:00
if length is not None :
self . max_innings = length
else :
self . max_innings = config ( ) [ " default_length " ]
self . bases = { 1 : None , 2 : None , 3 : None }
2020-12-27 21:51:41 +00:00
self . weather = weather ( " Sunny " , " 🌞 " )
2020-12-24 09:51:38 +00:00
2020-12-21 05:23:02 +00:00
def get_batter ( self ) :
if self . top_of_inning :
bat_team = self . teams [ " away " ]
else :
bat_team = self . teams [ " home " ]
2021-01-02 10:42:14 +00:00
if self . weather . name == " Heavy Snow " and counter == bat_team . lineup_position :
return bat_team . pitcher
2020-12-21 05:23:02 +00:00
return bat_team . lineup [ bat_team . lineup_position % len ( bat_team . lineup ) ]
def get_pitcher ( self ) :
if self . top_of_inning :
return self . teams [ " home " ] . pitcher
else :
return self . teams [ " away " ] . pitcher
def at_bat ( self ) :
2020-12-22 06:56:33 +00:00
outcome = { }
2020-12-21 05:23:02 +00:00
pitcher = self . get_pitcher ( )
batter = self . get_batter ( )
2020-12-22 06:56:33 +00:00
2020-12-21 05:23:02 +00:00
if self . top_of_inning :
2020-12-30 01:25:41 +00:00
defender_list = self . teams [ " home " ] . lineup . copy ( )
2020-12-21 05:23:02 +00:00
else :
2020-12-30 01:25:41 +00:00
defender_list = self . teams [ " away " ] . lineup . copy ( )
defender_list . append ( pitcher )
defender = random . choice ( defender_list ) #make pitchers field
2020-12-21 05:23:02 +00:00
2020-12-22 06:56:33 +00:00
outcome [ " batter " ] = batter
outcome [ " defender " ] = " "
2020-12-21 05:23:02 +00:00
bat_stat = random_star_gen ( " batting_stars " , batter )
pitch_stat = random_star_gen ( " pitching_stars " , pitcher )
2021-01-04 02:53:38 +00:00
if self . weather . name == " Supernova " :
2021-01-04 01:49:51 +00:00
pitch_stat = pitch_stat / 0.9
2021-01-04 02:53:38 +00:00
elif self . weather . name == " Thick Air " :
2020-12-27 21:51:41 +00:00
pitch_stat = pitch_stat * 0.9
2020-12-21 09:46:12 +00:00
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 )
2021-01-04 02:41:29 +00:00
crow = False
2020-12-21 05:23:02 +00:00
2021-01-04 02:53:38 +00:00
if self . weather . name == " Crowstorm " :
2021-01-04 02:37:40 +00:00
randomchance = random . gauss ( 0 , 1 )
2021-01-04 03:25:46 +00:00
if randomchance < - 1.5 or randomchance > 1.5 :
2021-01-04 03:12:01 +00:00
outcome [ " ishit " ] = False
fc_flag = False
2021-01-04 02:37:40 +00:00
outcome [ " text " ] = appearance_outcomes . crows
crow = True
2021-01-04 03:16:46 +00:00
else :
if pb_system_stat < = 0 and crow == False :
outcome [ " ishit " ] = False
fc_flag = False
if hitnum < - 1.5 :
outcome [ " text " ] = random . choice ( [ appearance_outcomes . strikeoutlooking , appearance_outcomes . strikeoutswinging ] )
elif hitnum < 1 :
outcome [ " text " ] = appearance_outcomes . groundout
outcome [ " defender " ] = defender
elif 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 :
outcome [ " text " ] = appearance_outcomes . doubleplay
outcome [ " defender " ] = " "
#for base in self.bases.values():
#if base is not None:
#fc_flag = True
runners = [ ( 0 , self . get_batter ( ) ) ]
for base in range ( 1 , 4 ) :
if self . bases [ base ] == None :
break
runners . append ( ( base , self . bases [ base ] ) )
outcome [ " runners " ] = runners #list of consecutive baserunners: (base number, player 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 self . weather . name == " Coke Raine " :
def_stat = def_stat / 0.9
elif self . weather . name == " Molasses " :
def_stat = def_stat * 0.9
if - 1.5 < = hitnum and 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 self . bases [ 2 ] is not None or self . bases [ 3 ] is not None :
outcome [ " advance " ] = True
elif crow == False :
outcome [ " ishit " ] = True
if hitnum < 1 :
outcome [ " text " ] = appearance_outcomes . single
elif hitnum < 2.85 :
outcome [ " text " ] = appearance_outcomes . double
elif 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 :
outcome [ " text " ] = appearance_outcomes . grandslam
else :
outcome [ " text " ] = appearance_outcomes . homerun
elif pb_system_stat < = 0 and crow == False :
2020-12-21 05:23:02 +00:00
outcome [ " ishit " ] = False
fc_flag = False
2020-12-21 09:46:12 +00:00
if hitnum < - 1.5 :
2020-12-21 05:23:02 +00:00
outcome [ " text " ] = random . choice ( [ appearance_outcomes . strikeoutlooking , appearance_outcomes . strikeoutswinging ] )
2020-12-21 09:46:12 +00:00
elif hitnum < 1 :
2020-12-21 05:23:02 +00:00
outcome [ " text " ] = appearance_outcomes . groundout
outcome [ " defender " ] = defender
2021-01-04 01:49:51 +00:00
elif hitnum < 4 :
2020-12-21 05:23:02 +00:00
outcome [ " text " ] = appearance_outcomes . flyout
outcome [ " defender " ] = defender
2020-12-21 09:46:12 +00:00
else :
outcome [ " text " ] = appearance_outcomes . walk
2020-12-21 05:23:02 +00:00
2020-12-27 07:06:43 +00:00
if self . bases [ 1 ] is not None and hitnum < - 2 and self . outs != 2 :
2020-12-21 05:23:02 +00:00
outcome [ " text " ] = appearance_outcomes . doubleplay
2020-12-22 06:56:33 +00:00
outcome [ " defender " ] = " "
2020-12-27 07:06:43 +00:00
#for base in self.bases.values():
#if base is not None:
#fc_flag = True
2020-12-22 06:56:33 +00:00
2020-12-27 07:06:43 +00:00
runners = [ ( 0 , self . get_batter ( ) ) ]
for base in range ( 1 , 4 ) :
if self . bases [ base ] == None :
break
runners . append ( ( base , self . bases [ base ] ) )
outcome [ " runners " ] = runners #list of consecutive baserunners: (base number, player object)
2020-12-22 06:56:33 +00:00
2020-12-27 07:06:43 +00:00
if self . outs < 2 and len ( runners ) > 1 : #fielder's choice replaces not great groundouts if any forceouts are present
2020-12-27 07:44:40 +00:00
def_stat = random_star_gen ( " defense_stars " , defender )
2021-01-04 01:49:51 +00:00
if self . weather . name == " Coke Raine " :
def_stat = def_stat / 0.9
elif self . weather . name == " Molasses " :
def_stat = def_stat * 0.9
2020-12-27 07:58:41 +00:00
if - 1.5 < = hitnum and hitnum < - 0.5 : #poorly hit groundouts
2020-12-22 06:56:33 +00:00
outcome [ " text " ] = appearance_outcomes . fielderschoice
outcome [ " defender " ] = " "
2021-01-04 01:49:51 +00:00
2020-12-27 07:44:40 +00:00
if 2.5 < = hitnum and self . outs < 2 : #well hit flyouts can lead to sacrifice flies/advanced runners
2020-12-27 07:06:43 +00:00
if self . bases [ 2 ] is not None or self . bases [ 3 ] is not None :
outcome [ " advance " ] = True
2021-01-04 03:16:46 +00:00
elif crow == False :
2020-12-21 09:46:12 +00:00
outcome [ " ishit " ] = True
2020-12-21 05:23:02 +00:00
if hitnum < 1 :
outcome [ " text " ] = appearance_outcomes . single
2020-12-21 09:46:12 +00:00
elif hitnum < 2.85 :
2020-12-21 05:23:02 +00:00
outcome [ " text " ] = appearance_outcomes . double
2020-12-21 09:46:12 +00:00
elif hitnum < 3.1 :
2020-12-21 05:23:02 +00:00
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 :
outcome [ " text " ] = appearance_outcomes . grandslam
else :
outcome [ " text " ] = appearance_outcomes . homerun
2020-12-22 06:56:33 +00:00
return outcome
2020-12-27 21:51:41 +00:00
def thievery_attempts ( self ) : #returns either false or "at-bat" outcome
thieves = [ ]
attempts = [ ]
for base in self . bases . keys ( ) :
if self . bases [ base ] is not None and base != 3 : #no stealing home in simsim, sorry stu
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
2021-01-04 01:49:51 +00:00
elif self . weather . name == " Slime " :
run_stars = run_stars * .75
2020-12-27 21:51:41 +00:00
def_stars = random_star_gen ( " defense_stars " , self . get_pitcher ( ) )
if run_stars > = ( 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
attempts . append ( ( baserunner , start_base ) )
if len ( attempts ) == 0 :
return False
2021-01-04 01:49:51 +00:00
else :
2020-12-27 21:51:41 +00:00
return ( self . steals_check ( attempts ) , 0 ) #effectively an at-bat outcome with no score
def steals_check ( self , attempts ) :
if self . top_of_inning :
defense_team = self . teams [ " home " ]
else :
defense_team = self . teams [ " away " ]
outcome = { }
outcome [ " steals " ] = [ ]
for baserunner , start_base in attempts :
defender = random . choice ( defense_team . lineup ) #excludes pitcher
run_stat = random_star_gen ( " baserunning_stars " , baserunner )
def_stat = random_star_gen ( " defense_stars " , defender )
2020-12-27 23:09:19 +00:00
run_roll = random . gauss ( 2 * math . erf ( ( run_stat - def_stat ) / 4 ) - 1 , 3 ) * config ( ) [ " stolen_base_success_mod " ]
2020-12-27 21:51:41 +00:00
if start_base == 2 :
run_roll = run_roll * .9 #stealing third is harder
if run_roll < 1 :
outcome [ " steals " ] . append ( f " { baserunner } was caught stealing { base_string ( start_base + 1 ) } base by { defender } ! " )
self . outs + = 1
else :
outcome [ " steals " ] . append ( f " { baserunner } steals { base_string ( start_base + 1 ) } base! " )
self . bases [ start_base + 1 ] = baserunner
self . bases [ start_base ] = None
if self . outs > = 3 :
self . flip_inning ( )
return outcome
2020-12-22 06:56:33 +00:00
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 ( ) :
2020-12-23 23:02:55 +00:00
runs = 0
2020-12-22 06:56:33 +00:00
if self . bases [ 3 ] is not None :
2020-12-26 10:21:14 +00:00
outcome [ " text " ] = appearance_outcomes . sacrifice
2021-01-04 01:49:51 +00:00
self . get_batter ( ) . game_stats [ " sacrifices " ] + = 1
2020-12-22 06:56:33 +00:00
self . bases [ 3 ] = None
runs = 1
if self . bases [ 2 ] is not None :
2020-12-27 08:16:19 +00:00
run_roll = random . gauss ( 2 * math . erf ( ( random_star_gen ( " baserunning_stars " , self . bases [ 2 ] ) - def_stat ) / 4 ) - 1 , 3 )
if run_roll > 2 :
2020-12-22 06:56:33 +00:00
self . bases [ 3 ] = self . bases [ 2 ]
self . bases [ 2 ] = None
return runs
elif outcome [ " text " ] == appearance_outcomes . fielderschoice :
2020-12-27 07:06:43 +00:00
furthest_base , runner = outcome [ " runners " ] . pop ( ) #get furthest baserunner
2021-01-04 01:49:51 +00:00
self . bases [ furthest_base ] = None
2020-12-27 07:06:43 +00:00
outcome [ " fc_out " ] = ( runner . name , base_string ( furthest_base + 1 ) ) #runner thrown out
for index in range ( 0 , len ( outcome [ " runners " ] ) ) :
base , this_runner = outcome [ " runners " ] . pop ( )
self . bases [ base + 1 ] = this_runner #includes batter, at base 0
if self . bases [ 3 ] is not None and furthest_base == 1 : #fielders' choice with runners on the corners
self . bases [ 3 ] = None
return 1
2020-12-22 06:56:33 +00:00
return 0
2020-12-27 07:06:43 +00:00
elif outcome [ " text " ] == appearance_outcomes . groundout or outcome [ " text " ] == appearance_outcomes . doubleplay :
runs = 0
if self . bases [ 3 ] is not None :
runs + = 1
self . bases [ 3 ] = None
if self . bases [ 2 ] is not None :
2020-12-27 08:16:19 +00:00
run_roll = random . gauss ( 2 * math . erf ( ( random_star_gen ( " baserunning_stars " , self . bases [ 2 ] ) - def_stat ) / 4 ) - 1 , 3 )
2020-12-27 19:23:17 +00:00
if run_roll > 1.5 or outcome [ " text " ] == appearance_outcomes . doubleplay : #double play gives them time to run, guaranteed
2020-12-27 07:06:43 +00:00
self . bases [ 3 ] = self . bases [ 2 ]
self . bases [ 2 ] = None
if self . bases [ 1 ] is not None : #double plays set this to None before this call
2020-12-27 08:16:19 +00:00
run_roll = random . gauss ( 2 * math . erf ( ( random_star_gen ( " baserunning_stars " , self . bases [ 1 ] ) - def_stat ) / 4 ) - 1 , 3 )
if run_roll < 2 or self . bases [ 2 ] is not None : #if runner can't make it or if baserunner blocking on second, convert to fielder's choice
2020-12-27 07:44:40 +00:00
outcome [ " text " ] == appearance_outcomes . fielderschoice
runners = [ ( 0 , self . get_batter ( ) ) ]
for base in range ( 1 , 4 ) :
if self . bases [ base ] == None :
break
runners . append ( ( base , self . bases [ base ] ) )
outcome [ " runners " ] = runners #rebuild consecutive runners
return runs + self . baserunner_check ( defender , outcome ) #run again as fielder's choice instead
else :
2020-12-27 07:06:43 +00:00
self . bases [ 2 ] = self . bases [ 1 ]
self . bases [ 1 ] = None
return runs
2020-12-22 06:56:33 +00:00
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
2021-01-04 01:49:51 +00:00
2020-12-21 05:23:02 +00:00
2020-12-22 06:56:33 +00:00
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 ( )
2020-12-21 05:23:02 +00:00
if self . top_of_inning :
2020-12-22 06:56:33 +00:00
offense_team = self . teams [ " away " ]
defense_team = self . teams [ " home " ]
2020-12-21 05:23:02 +00:00
else :
2020-12-22 06:56:33 +00:00
offense_team = self . teams [ " home " ]
defense_team = self . teams [ " away " ]
2020-12-27 21:51:41 +00:00
2021-01-05 03:02:33 +00:00
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 :
2021-01-05 20:18:18 +00:00
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 :
2021-01-02 10:42:14 +00:00
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 )
2020-12-27 21:51:41 +00:00
defenders = defense_team . lineup . copy ( )
defenders . append ( defense_team . pitcher )
defender = random . choice ( defenders ) #pitcher can field outs now :3
2020-12-22 06:56:33 +00:00
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 :
2021-01-04 01:49:51 +00:00
self . get_batter ( ) . game_stats [ " total_bases " ] + = 1
2020-12-22 06:56:33 +00:00
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
2021-01-04 01:49:51 +00:00
2020-12-22 06:56:33 +00:00
elif result [ " text " ] == appearance_outcomes . doubleplay :
self . get_pitcher ( ) . game_stats [ " outs_pitched " ] + = 2
self . outs + = 2
2021-01-04 01:49:51 +00:00
self . bases [ 1 ] = None
2020-12-27 07:44:40 +00:00
if self . outs < 3 :
scores_to_add + = self . baserunner_check ( defender , result )
self . get_batter ( ) . game_stats [ " rbis " ] - = scores_to_add #remove the fake rbi from the player in advance
2020-12-22 06:56:33 +00:00
2020-12-27 07:44:40 +00:00
elif result [ " text " ] == appearance_outcomes . fielderschoice or result [ " text " ] == appearance_outcomes . groundout :
2020-12-22 06:56:33 +00:00
self . get_pitcher ( ) . game_stats [ " outs_pitched " ] + = 1
self . outs + = 1
2020-12-27 07:44:40 +00:00
if self . outs < 3 :
scores_to_add + = self . baserunner_check ( defender , result )
2020-12-22 06:56:33 +00:00
elif " advance " in result . keys ( ) :
self . get_pitcher ( ) . game_stats [ " outs_pitched " ] + = 1
self . outs + = 1
2020-12-27 07:44:40 +00:00
if self . outs < 3 :
if self . bases [ 3 ] is not None :
self . get_batter ( ) . game_stats [ " sacrifices " ] + = 1
scores_to_add + = self . baserunner_check ( defender , result )
2020-12-22 06:56:33 +00:00
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
2021-01-04 01:49:51 +00:00
else :
2020-12-22 06:56:33 +00:00
self . get_pitcher ( ) . game_stats [ " outs_pitched " ] + = 1
self . outs + = 1
2020-12-26 10:21:14 +00:00
self . get_batter ( ) . game_stats [ " plate_appearances " ] + = 1
2021-01-04 01:49:51 +00:00
2020-12-27 19:23:17 +00:00
if self . outs < 3 :
offense_team . score + = scores_to_add #only add points if inning isn't over
else :
scores_to_add = 0
2020-12-22 06:56:33 +00:00
self . get_batter ( ) . game_stats [ " rbis " ] + = scores_to_add
self . get_pitcher ( ) . game_stats [ " runs_allowed " ] + = scores_to_add
2020-12-26 10:21:14 +00:00
offense_team . lineup_position + = 1 #put next batter up
2020-12-22 06:56:33 +00:00
if self . outs > = 3 :
self . flip_inning ( )
2021-01-04 01:49:51 +00:00
2020-12-22 06:56:33 +00:00
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
2021-01-02 10:42:14 +00:00
2020-12-22 06:56:33 +00:00
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
2021-01-02 10:42:14 +00:00
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
2020-12-22 06:56:33 +00:00
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
}
2020-12-28 08:21:47 +00:00
def named_bases ( self ) :
name_bases = { }
for base in range ( 1 , 4 ) :
if self . bases [ base ] is not None :
name_bases [ base ] = self . bases [ base ] . name
else :
name_bases [ base ] = None
return name_bases
2020-12-22 12:59:46 +00:00
2020-12-27 21:51:41 +00:00
def gamestate_update_full ( self ) :
attempts = self . thievery_attempts ( )
if attempts == False :
self . last_update = self . batterup ( )
else :
self . last_update = attempts
2020-12-22 06:56:33 +00:00
return self . gamestate_display_full ( )
def gamestate_display_full ( self ) :
2020-12-27 21:51:41 +00:00
if " steals " in self . last_update [ 0 ] . keys ( ) :
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 "
2020-12-22 06:56:33 +00:00
2021-01-02 10:42:14 +00:00
updatestring = " this isn ' t used but i don ' t want to break anything "
2020-12-22 06:56:33 +00:00
2021-01-02 10:42:14 +00:00
return " this isn ' t used but i don ' t want to break anything "
2020-12-27 21:51:41 +00:00
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. "
2020-12-26 09:46:42 +00:00
def add_stats ( self ) :
players = [ ]
for this_player in self . teams [ " away " ] . lineup :
players . append ( ( this_player . name , this_player . game_stats ) )
for this_player in self . teams [ " home " ] . lineup :
players . append ( ( this_player . name , this_player . game_stats ) )
players . append ( ( self . teams [ " home " ] . pitcher . name , self . teams [ " home " ] . pitcher . game_stats ) )
players . append ( ( self . teams [ " away " ] . pitcher . name , self . teams [ " away " ] . pitcher . game_stats ) )
db . add_stats ( players )
2021-01-04 01:49:51 +00:00
2020-12-21 09:46:12 +00:00
2020-12-21 05:23:02 +00:00
def random_star_gen ( key , player ) :
return random . gauss ( config ( ) [ " stlat_weights " ] [ key ] * player . stlats [ key ] , 1 )
# innings_pitched
# walks_allowed
# strikeouts_given
# runs_allowed
# plate_appearances
# walks
# hits
# total_bases
# rbis
# walks_taken
# strikeouts_taken
2020-12-24 09:51:38 +00:00
def get_team ( name ) :
2020-12-27 08:42:09 +00:00
try :
team_json = jsonpickle . decode ( db . get_team ( name ) [ 0 ] , keys = True , classes = team )
if team_json is not None :
2021-01-02 06:10:52 +00:00
if team_json . pitcher is not None : #detects old-format teams, adds pitcher
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
2020-12-27 08:42:09 +00:00
return team_json
return None
2021-01-02 06:10:52 +00:00
except AttributeError :
team_json . rotation = [ ]
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
return team_json
2020-12-27 08:42:09 +00:00
except :
return None
2020-12-24 09:51:38 +00:00
2020-12-28 00:05:49 +00:00
def get_team_and_owner ( name ) :
2021-01-02 06:10:52 +00:00
try :
counter , name , team_json_string , timestamp , owner_id = db . get_team ( name , owner = True )
team_json = jsonpickle . decode ( team_json_string , keys = True , classes = team )
if team_json is not None :
if team_json . pitcher is not None : #detects old-format teams, adds pitcher
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
return ( team_json , owner_id )
return None
except AttributeError :
team_json . rotation = [ ]
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
2020-12-28 00:05:49 +00:00
return ( team_json , owner_id )
2021-01-02 06:10:52 +00:00
except :
return None
2020-12-28 00:05:49 +00:00
2020-12-27 02:53:46 +00:00
def save_team ( this_team , user_id ) :
2020-12-24 09:51:38 +00:00
try :
this_team . prepare_for_save ( )
team_json_string = jsonpickle . encode ( this_team , keys = True )
2020-12-27 02:53:46 +00:00
db . save_team ( this_team . name , team_json_string , user_id )
2020-12-24 09:51:38 +00:00
return True
except :
2020-12-26 09:46:42 +00:00
return None
2021-01-02 06:10:52 +00:00
def update_team ( this_team ) :
try :
this_team . prepare_for_save ( )
team_json_string = jsonpickle . encode ( this_team , keys = True )
db . update_team ( this_team . name , team_json_string )
return True
except :
return None
2020-12-26 09:46:42 +00:00
def get_all_teams ( ) :
teams = [ ]
for team_pickle in db . get_all_teams ( ) :
this_team = jsonpickle . decode ( team_pickle [ 0 ] , keys = True , classes = team )
teams . append ( this_team )
return teams
2021-01-04 07:36:53 +00:00
def get_history ( ) :
2021-01-04 08:01:51 +00:00
games = [ ]
2021-01-04 07:36:53 +00:00
for game in db . get_history ( ) :
2021-01-04 08:01:51 +00:00
game_dict = { " Team1 " : game [ 1 ] ,
" Team1Score " : game [ 2 ] ,
" Team2 " : game [ 3 ] ,
2021-01-07 01:35:37 +00:00
" Team2Score " : game [ 4 ] ,
" Weather " : game [ 5 ] }
2021-01-04 08:01:51 +00:00
games . append ( game_dict )
return games
2021-01-04 07:36:53 +00:00
2020-12-26 09:46:42 +00:00
def search_team ( search_term ) :
teams = [ ]
for team_pickle in db . search_teams ( search_term ) :
2021-01-05 03:32:41 +00:00
print ( team_pickle )
2021-01-02 06:10:52 +00:00
team_json = jsonpickle . decode ( team_pickle [ 0 ] , keys = True , classes = team )
2021-01-05 03:02:33 +00:00
try :
2021-01-02 06:10:52 +00:00
if team_json . pitcher is not None :
if len ( team_json . rotation ) == 0 : #detects old-format teams, adds pitcher
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
except AttributeError :
team_json . rotation = [ ]
team_json . rotation . append ( team_json . pitcher )
team_json . pitcher = None
update_team ( team_json )
except :
return None
teams . append ( team_json )
2020-12-27 07:06:43 +00:00
return teams
def base_string ( base ) :
if base == 1 :
return " first "
elif base == 2 :
return " second "
elif base == 3 :
return " third "
elif base == 4 :
2020-12-27 21:51:41 +00:00
return " fourth "
class weather ( object ) :
name = " Sunny "
emoji = " 🌞 "
def __init__ ( self , new_name , new_emoji ) :
self . name = new_name
self . emoji = new_emoji
def __str__ ( self ) :
2021-01-04 01:49:51 +00:00
return f " { self . emoji } { self . name } "