2021-01-13 09:23:48 +00:00
import time , asyncio , json , jsonpickle , random , math , os
import league_storage as league_db
2021-01-11 11:17:14 +00:00
from itertools import chain
2021-01-03 06:04:51 +00:00
from games import team , game
2021-01-03 11:56:40 +00:00
from discord import Embed , Color
2021-01-02 06:10:52 +00:00
2021-01-13 09:23:48 +00:00
data_dir = " data "
league_dir = " leagues "
2021-01-02 06:10:52 +00:00
2021-01-11 11:17:14 +00:00
class league_structure ( object ) :
2021-01-13 09:23:48 +00:00
def __init__ ( self , name ) :
self . name = name
2021-01-14 00:20:58 +00:00
self . historic = False
self . owner = None
2021-01-14 05:20:43 +00:00
self . season = 1
2021-01-15 04:39:08 +00:00
self . autoplay = - 1
2021-01-13 09:23:48 +00:00
def setup ( self , league_dic , division_games = 1 , inter_division_games = 1 , inter_league_games = 1 , games_per_hour = 2 ) :
2021-01-14 06:30:31 +00:00
self . league = league_dic # { subleague name : { division name : [team object] } }
2021-01-11 11:17:14 +00:00
self . constraints = {
" division_games " : division_games ,
" inter_div_games " : inter_division_games ,
2021-01-14 22:47:57 +00:00
" inter_league_games " : inter_league_games ,
" division_leaders " : 0 ,
" wild_cards " : 0
2021-01-11 11:17:14 +00:00
}
2021-01-02 06:10:52 +00:00
self . day = 1
2021-01-11 11:17:14 +00:00
self . schedule = { }
self . series_length = 3 #can be changed
self . game_length = None
self . active = False
self . games_per_hour = games_per_hour
2021-01-11 11:36:13 +00:00
2021-01-13 11:32:28 +00:00
def add_stats_from_game ( self , players_dic ) :
league_db . add_stats ( self . name , players_dic )
2021-01-13 09:23:48 +00:00
def update_standings ( self , results_dic ) :
league_db . update_standings ( self . name , results_dic )
2021-01-11 11:17:14 +00:00
def last_series_check ( self ) :
2021-01-14 00:20:58 +00:00
return str ( math . ceil ( ( self . day ) / self . series_length ) + 1 ) not in self . schedule . keys ( )
2021-01-13 11:32:28 +00:00
def day_to_series_num ( self , day ) :
return math . ceil ( ( self . day ) / self . series_length )
2021-01-11 11:17:14 +00:00
def find_team ( self , team_name ) :
for subleague in iter ( self . league . keys ( ) ) :
for division in iter ( self . league [ subleague ] . keys ( ) ) :
if team_name in self . league [ subleague ] [ division ] :
return ( subleague , division )
def teams_in_league ( self ) :
teams = [ ]
for division in self . league . values ( ) :
for teams_list in division . values ( ) :
teams + = teams_list
return teams
def teams_in_subleague ( self , subleague_name ) :
teams = [ ]
if subleague_name in self . league . keys ( ) :
for division_list in self . league [ subleague_name ] . values ( ) :
teams + = division_list
return teams
else :
print ( " League not found. " )
return None
def teams_in_division ( self , subleague_name , division_name ) :
if subleague_name in self . league . keys ( ) and division_name in self . league [ subleague_name ] . keys ( ) :
return self . league [ subleague_name ] [ division_name ]
else :
print ( " Division in that league not found. " )
return None
def make_matchups ( self ) :
matchups = [ ]
batch_subleagues = [ ] #each sub-array is all teams in each subleague
subleague_max = 1
for subleague in self . league . keys ( ) :
teams = self . teams_in_subleague ( subleague )
if subleague_max < len ( teams ) :
subleague_max = len ( teams )
batch_subleagues . append ( teams )
for subleague in batch_subleagues :
while len ( subleague ) < subleague_max :
subleague . append ( " OFF " )
for i in range ( 0 , self . constraints [ " inter_league_games " ] ) : #generates inter-league matchups
unmatched_indices = [ i for i in range ( 0 , len ( batch_subleagues ) ) ]
for subleague_index in range ( 0 , len ( batch_subleagues ) ) :
if subleague_index in unmatched_indices :
unmatched_indices . pop ( unmatched_indices . index ( subleague_index ) )
match_with_index = random . choice ( unmatched_indices )
unmatched_indices . pop ( unmatched_indices . index ( match_with_index ) )
league_a = batch_subleagues [ subleague_index ] . copy ( )
league_b = batch_subleagues [ match_with_index ] . copy ( )
random . shuffle ( league_a )
random . shuffle ( league_b )
a_home = True
for team_a , team_b in zip ( league_a , league_b ) :
if a_home :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_b . name , team_a . name ] )
2021-01-11 11:17:14 +00:00
else :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_a . name , team_b . name ] )
2021-01-11 11:17:14 +00:00
a_home != a_home
for i in range ( 0 , self . constraints [ " inter_div_games " ] ) : #inter-division matchups
for subleague in self . league . keys ( ) :
division_max = 1
divisions = [ ]
for div in self . league [ subleague ] . keys ( ) :
if division_max < len ( self . league [ subleague ] [ div ] ) :
divison_max = len ( self . league [ subleague ] [ div ] )
divisions . append ( self . league [ subleague ] [ div ] )
last_div = None
if len ( divisions ) % 2 != 0 :
if division_max % 2 != 0 :
divisions . append ( [ " OFF " for i in range ( 0 , division_max ) ] )
else :
last_div = divisions . pop
divs_a = list ( chain ( divisions [ int ( len ( divisions ) / 2 ) : ] ) ) [ 0 ]
if last_div is not None :
divs_a . extend ( last_div [ int ( len ( last_div ) / 2 ) : ] )
random . shuffle ( divs_a )
divs_b = list ( chain ( divisions [ : int ( len ( divisions ) / 2 ) ] ) ) [ 0 ]
if last_div is not None :
divs_a . extend ( last_div [ : int ( len ( last_div ) / 2 ) ] )
random . shuffle ( divs_b )
a_home = True
for team_a , team_b in zip ( divs_a , divs_b ) :
if a_home :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_b . name , team_a . name ] )
2021-01-11 11:17:14 +00:00
else :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_a . name , team_b . name ] )
2021-01-11 11:17:14 +00:00
a_home != a_home
for subleague in self . league . keys ( ) :
for division in self . league [ subleague ] . values ( ) : #generate round-robin matchups
if len ( division ) % 2 != 0 :
division . append ( " OFF " )
for i in range ( 0 , len ( division ) - 1 ) :
teams_a = division [ int ( len ( division ) / 2 ) : ]
teams_b = division [ : int ( len ( division ) / 2 ) ]
teams_b . reverse ( )
for team_a , team_b in zip ( teams_a , teams_b ) :
for j in range ( 0 , self . constraints [ " division_games " ] ) :
if i % 2 == 0 :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_b . name , team_a . name ] )
2021-01-11 11:17:14 +00:00
else :
2021-01-13 09:23:48 +00:00
matchups . append ( [ team_a . name , team_b . name ] )
2021-01-11 11:17:14 +00:00
division . insert ( 1 , division . pop ( ) )
return matchups
def generate_schedule ( self ) :
matchups = self . make_matchups ( )
random . shuffle ( matchups )
for game in matchups :
scheduled = False
day = 1
while not scheduled :
found = False
2021-01-14 00:20:58 +00:00
if str ( day ) in self . schedule . keys ( ) :
for game_on_day in self . schedule [ str ( day ) ] :
2021-01-11 11:17:14 +00:00
for team in game :
if team in game_on_day :
found = True
if not found :
2021-01-14 00:20:58 +00:00
self . schedule [ str ( day ) ] . append ( game )
2021-01-11 11:17:14 +00:00
scheduled = True
else :
2021-01-14 00:20:58 +00:00
self . schedule [ str ( day ) ] = [ game ]
2021-01-11 11:17:14 +00:00
scheduled = True
day + = 1
2021-01-02 06:10:52 +00:00
2021-01-15 04:39:08 +00:00
def division_standings ( self , division , standings ) :
def sorter ( team_in_list ) :
if team_in_list [ 2 ] == 0 and team_in_list [ 1 ] == 0 :
return ( 0 , team_in_list [ 3 ] )
return ( team_in_list [ 1 ] / ( team_in_list [ 1 ] + team_in_list [ 2 ] ) , team_in_list [ 3 ] )
teams = division . copy ( )
for index in range ( 0 , len ( teams ) ) :
this_team = teams [ index ]
teams [ index ] = [ this_team , standings [ teams [ index ] . name ] [ " wins " ] , standings [ teams [ index ] . name ] [ " losses " ] , standings [ teams [ index ] . name ] [ " run_diff " ] , 0 ]
teams . sort ( key = sorter , reverse = True )
return teams
2021-01-14 00:20:58 +00:00
def standings_embed ( self ) :
this_embed = Embed ( color = Color . purple ( ) , title = self . name )
standings = { }
for team_name , wins , losses , run_diff in league_db . get_standings ( self . name ) :
standings [ team_name ] = { " wins " : wins , " losses " : losses , " run_diff " : run_diff }
for subleague in iter ( self . league . keys ( ) ) :
this_embed . add_field ( name = " Subleague: " , value = f " ** { subleague } ** " , inline = False )
for division in iter ( self . league [ subleague ] . keys ( ) ) :
this_embed . add_field ( name = " Division: " , value = f " ** { division } ** " , inline = False )
2021-01-15 04:39:08 +00:00
teams = self . division_standings ( self . league [ subleague ] [ division ] , standings )
2021-01-14 00:20:58 +00:00
2021-01-15 04:39:08 +00:00
for index in range ( 0 , len ( teams ) ) :
if index == self . constraints [ " division_leaders " ] - 1 :
teams [ index ] [ 4 ] = " - "
else :
games_behind = ( ( teams [ self . constraints [ " division_leaders " ] - 1 ] [ 1 ] - teams [ index ] [ 1 ] ) + ( teams [ index ] [ 2 ] - teams [ self . constraints [ " division_leaders " ] - 1 ] [ 2 ] ) ) / 2
teams [ index ] [ 4 ] = games_behind
2021-01-14 00:20:58 +00:00
for this_team in teams :
2021-01-15 04:39:08 +00:00
if this_team [ 2 ] != 0 or this_team [ 1 ] != 0 :
this_embed . add_field ( name = this_team [ 0 ] . name , value = f " { this_team [ 1 ] } - { this_team [ 2 ] } WR: { round ( this_team [ 1 ] / ( this_team [ 1 ] + this_team [ 2 ] ) , 3 ) } GB: { this_team [ 4 ] } " , inline = False )
else :
this_embed . add_field ( name = this_team [ 0 ] . name , value = f " { this_team [ 1 ] } - { this_team [ 2 ] } WR: - GB: { this_team [ 4 ] } " , inline = False )
2021-01-14 00:20:58 +00:00
this_embed . set_footer ( text = f " Standings as of day { self . day - 1 } " )
return this_embed
2021-01-15 04:39:08 +00:00
def wildcard_embed ( self ) :
this_embed = Embed ( color = Color . purple ( ) , title = f " { self . name } Wildcard Race " )
standings = { }
for team_name , wins , losses , run_diff in league_db . get_standings ( self . name ) :
standings [ team_name ] = { " wins " : wins , " losses " : losses , " run_diff " : run_diff }
for subleague in iter ( self . league . keys ( ) ) :
this_embed . add_field ( name = " Subleague: " , value = f " ** { subleague } ** " , inline = False )
subleague_array = [ ]
for division in iter ( self . league [ subleague ] . keys ( ) ) :
this_div = [ this_team for this_team , wins , losses , diff , gb in self . division_standings ( self . league [ subleague ] [ division ] , standings ) [ self . constraints [ " division_leaders " ] : ] ]
subleague_array + = this_div
teams = self . division_standings ( subleague_array , standings )
for index in range ( 0 , len ( teams ) ) :
if index == self . constraints [ " wild_cards " ] - 1 :
teams [ index ] [ 4 ] = " - "
else :
games_behind = ( ( teams [ self . constraints [ " wild_cards " ] - 1 ] [ 1 ] - teams [ index ] [ 1 ] ) + ( teams [ index ] [ 2 ] - teams [ self . constraints [ " wild_cards " ] - 1 ] [ 2 ] ) ) / 2
teams [ index ] [ 4 ] = games_behind
for this_team in teams :
if this_team [ 2 ] != 0 or this_team [ 1 ] != 0 :
this_embed . add_field ( name = this_team [ 0 ] . name , value = f " { this_team [ 1 ] } - { this_team [ 2 ] } WR: { round ( this_team [ 1 ] / ( this_team [ 1 ] + this_team [ 2 ] ) , 3 ) } GB: { this_team [ 4 ] } " , inline = False )
else :
this_embed . add_field ( name = this_team [ 0 ] . name , value = f " { this_team [ 1 ] } - { this_team [ 2 ] } WR: - GB: { this_team [ 4 ] } " , inline = False )
this_embed . set_footer ( text = f " Wildcard standings as of day { self . day - 1 } " )
return this_embed
2021-01-03 06:04:51 +00:00
class tournament ( object ) :
2021-01-04 04:18:45 +00:00
def __init__ ( self , name , team_dic , series_length = 5 , finals_series_length = 7 , max_innings = 9 , id = None , secs_between_games = 300 , secs_between_rounds = 600 ) :
2021-01-03 11:06:51 +00:00
self . name = name
2021-01-11 11:36:13 +00:00
self . teams = team_dic #key: team object, value: wins
2021-01-03 11:06:51 +00:00
self . bracket = None
self . results = None
self . series_length = series_length
2021-01-04 04:18:45 +00:00
self . finals_length = finals_series_length
2021-01-03 11:09:27 +00:00
self . game_length = max_innings
2021-01-03 11:06:51 +00:00
self . active = False
2021-01-04 04:18:45 +00:00
self . delay = secs_between_games
self . round_delay = secs_between_rounds
self . finals = False
self . id = id
if id is None :
self . id = random . randint ( 1111 , 9999 )
else :
self . id = id
2021-01-03 06:04:51 +00:00
2021-01-11 11:17:14 +00:00
def build_bracket ( self , random_sort = False , by_wins = False , manual = False ) :
2021-01-03 11:06:51 +00:00
teams_list = list ( self . teams . keys ( ) ) . copy ( )
2021-01-03 06:04:51 +00:00
2021-01-03 11:06:51 +00:00
if random_sort :
2021-01-03 06:04:51 +00:00
def sorter ( team_in_list ) :
return random . random ( )
elif by_wins :
def sorter ( team_in_list ) :
2021-01-03 11:56:40 +00:00
return self . teams [ team_in_list ] [ " wins " ] #sorts by wins
2021-01-03 06:04:51 +00:00
2021-01-03 11:06:51 +00:00
else : #sort by average stars
2021-01-03 06:04:51 +00:00
def sorter ( team_in_list ) :
return team_in_list . average_stars ( )
2021-01-11 11:17:14 +00:00
if not manual :
teams_list . sort ( key = sorter , reverse = True )
2021-01-04 04:18:45 +00:00
2021-01-03 06:04:51 +00:00
2021-01-03 11:06:51 +00:00
bracket_layers = int ( math . ceil ( math . log ( len ( teams_list ) , 2 ) ) )
2021-01-03 06:04:51 +00:00
empty_slots = int ( math . pow ( 2 , bracket_layers ) - len ( teams_list ) )
for i in range ( 0 , empty_slots ) :
teams_list . append ( None )
2021-01-03 11:06:51 +00:00
previous_bracket_layer = teams_list . copy ( )
2021-01-03 11:56:40 +00:00
for i in range ( 0 , bracket_layers - 1 ) :
2021-01-03 11:06:51 +00:00
this_layer = [ ]
for pair in range ( 0 , int ( len ( previous_bracket_layer ) / 2 ) ) :
if pair % 2 == 0 : #if even number
this_layer . insert ( 0 + int ( pair / 2 ) , [ previous_bracket_layer . pop ( 0 ) , previous_bracket_layer . pop ( - 1 ) ] ) #every other pair goes at front of list, moving forward
else :
2021-01-04 04:18:45 +00:00
this_layer . insert ( 0 - int ( ( 1 + pair ) / 2 ) , [ previous_bracket_layer . pop ( int ( len ( previous_bracket_layer ) / 2 ) - 1 ) , previous_bracket_layer . pop ( int ( len ( previous_bracket_layer ) / 2 ) ) ] ) #every other pair goes at end of list, moving backward
2021-01-03 11:06:51 +00:00
previous_bracket_layer = this_layer
self . bracket = bracket ( previous_bracket_layer , bracket_layers )
2021-01-04 04:18:45 +00:00
def round_check ( self ) :
if self . bracket . depth == 1 :
self . finals = True
return True
else :
return False
2021-01-03 11:06:51 +00:00
class bracket ( object ) :
2021-01-04 04:18:45 +00:00
this_bracket = [ ]
2021-01-03 11:06:51 +00:00
def __init__ ( self , bracket_list , depth ) :
self . this_bracket = bracket_list
self . depth = depth
self . bottom_row = [ ]
def get_bottom_row ( self ) :
2021-01-04 04:18:45 +00:00
self . depth = 1
2021-01-03 11:06:51 +00:00
self . bottom_row = [ ]
self . dive ( self . this_bracket )
return self . bottom_row
def dive ( self , branch ) :
if not isinstance ( branch [ 0 ] , list ) : #if it's a pair of games
self . bottom_row . append ( branch )
else :
2021-01-04 04:18:45 +00:00
self . depth + = 1
return self . dive ( branch [ 0 ] ) , self . dive ( branch [ 1 ] )
def set_winners_dive ( self , winners_list , index = 0 , branch = None , parent = None ) :
if branch is None :
branch = self . this_bracket . copy ( )
if not isinstance ( branch [ 0 ] , list ) : #if it's a pair of games
if branch [ 0 ] . name in winners_list or branch [ 1 ] is None :
winner = branch [ 0 ]
if parent is not None :
parent [ index ] = winner
elif branch [ 1 ] . name in winners_list :
winner = branch [ 1 ]
if parent is not None :
parent [ index ] = winner
else :
self . set_winners_dive ( winners_list , index = 0 , branch = branch [ 0 ] , parent = branch )
self . set_winners_dive ( winners_list , index = 1 , branch = branch [ 1 ] , parent = branch )
if parent is None :
self . this_bracket = branch
2021-01-13 09:23:48 +00:00
return branch
def save_league ( this_league ) :
if not league_db . league_exists ( this_league . name ) :
league_db . init_league_db ( this_league )
2021-01-13 11:32:28 +00:00
with open ( os . path . join ( data_dir , league_dir , this_league . name , f " { this_league . name } .league " ) , " w " ) as league_file :
2021-01-13 09:23:48 +00:00
league_json_string = jsonpickle . encode ( this_league . league , keys = True )
json . dump ( league_json_string , league_file , indent = 4 )
2021-01-14 05:25:24 +00:00
league_db . save_league ( this_league )
2021-01-13 09:23:48 +00:00
def load_league_file ( league_name ) :
if league_db . league_exists ( league_name ) :
state = league_db . state ( league_name )
this_league = league_structure ( league_name )
2021-01-13 11:32:28 +00:00
with open ( os . path . join ( data_dir , league_dir , league_name , f " { this_league . name } .league " ) ) as league_file :
2021-01-13 09:23:48 +00:00
this_league . league = jsonpickle . decode ( json . load ( league_file ) , keys = True , classes = team )
2021-01-13 11:32:28 +00:00
with open ( os . path . join ( data_dir , league_dir , league_name , f " { this_league . name } .state " ) ) as state_file :
2021-01-13 09:23:48 +00:00
state_dic = json . load ( state_file )
2021-01-14 00:20:58 +00:00
this_league . day = state_dic [ " day " ]
this_league . schedule = state_dic [ " schedule " ]
2021-01-15 04:39:08 +00:00
this_league . constraints = state_dic [ " constraints " ]
2021-01-14 00:20:58 +00:00
this_league . game_length = state_dic [ " game_length " ]
this_league . series_length = state_dic [ " series_length " ]
this_league . owner = state_dic [ " owner " ]
2021-01-14 05:20:43 +00:00
this_league . games_per_hour = state_dic [ " games_per_hour " ]
this_league . historic = state_dic [ " historic " ]
this_league . season = state_dic [ " season " ]
2021-01-13 09:23:48 +00:00
return this_league