this should add all the infrastructure needed to run a league's games (and only *run* them, no data yet). untested

This commit is contained in:
Sakimori 2021-01-11 06:17:14 -05:00
parent 20a13fdf2e
commit 31930aab63
2 changed files with 321 additions and 10 deletions

View File

@ -1,4 +1,5 @@
import time, asyncio, jsonpickle, random, math
from itertools import chain
from games import team, game
from discord import Embed, Color
import database as db
@ -7,13 +8,165 @@ import database as db
class league(object):
def __init__(self, name, subleagues_dic):
self.subleagues = {} #key: name, value: [divisions]
self.max_days
class league_structure(object):
def __init__(self, name, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2):
self.league = league_dic #key: subleague, value: {division, teams}
self.constraints = {
"division_games" : division_games,
"inter_div_games" : inter_division_games,
"inter_league_games" : inter_league_games
}
self.season_length = 0
self.day = 1
self.name = name
self.subleagues = subleagues_dic
self.schedule = {}
self.series_length = 3 #can be changed
self.game_length = None
self.active = False
self.games_per_hour = games_per_hour
def last_series_check(self):
return day + 1 in self.schedule.keys()
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:
matchups.append([team_b, team_a])
else:
matchups.append([team_a, team_b])
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:
matchups.append([team_b, team_a])
else:
matchups.append([team_a, team_b])
a_home != a_home
self.season_length = self.constraints["division_games"]*(division_max) + self.constraints["inter_div_games"] + self.constraints["inter_league_games"]
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:
matchups.append([team_b, team_a])
else:
matchups.append([team_a, team_b])
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
if day in self.schedule.keys():
for game_on_day in self.schedule[day]:
for team in game:
if team in game_on_day:
found = True
if not found:
self.schedule[day].append(game)
scheduled = True
else:
self.schedule[day] = [game]
scheduled = True
day += 1
class division(object):
def __init__(self):
@ -40,7 +193,7 @@ class tournament(object):
self.id = id
def build_bracket(self, random_sort = False, by_wins = False):
def build_bracket(self, random_sort = False, by_wins = False, manual = False):
teams_list = list(self.teams.keys()).copy()
if random_sort:
@ -54,8 +207,9 @@ class tournament(object):
else: #sort by average stars
def sorter(team_in_list):
return team_in_list.average_stars()
teams_list.sort(key=sorter, reverse=True)
if not manual:
teams_list.sort(key=sorter, reverse=True)
bracket_layers = int(math.ceil(math.log(len(teams_list), 2)))

View File

@ -1,7 +1,6 @@
import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues
import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues, datetime
import database as db
import onomancer as ono
import random
from the_draft import Draft, DRAFT_ROUNDS
from flask import Flask
from uuid import uuid4
@ -757,6 +756,7 @@ commands = [
client = discord.Client()
gamesarray = []
active_tournaments = []
active_leagues = []
setupmessages = {}
thread1 = threading.Thread(target=main_controller.update_loop)
@ -1382,4 +1382,161 @@ def get_team_fuzzy_search(team_name):
team = teams[0]
return team
async def start_league_day(channel, league):
current_games = []
if league.schedule is {}:
league.generate_schedule()
games_to_start = league.schedule[math.ceil(league.day/league.series_length)]
if league.game_length is None:
game_length = games.config()["default_length"]
else:
game_length = league.game_length
for pair in games_to_start:
if pair[0] is not None and pair[1] is not None:
this_game = games.game(pair[0].prepare_for_save().finalize(), pair[1].prepare_for_save().finalize(), length = game_length)
this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True
series_string = f"Series score:"
state_init["title"] = f"{series_string} 0 - 0"
discrim_string = league.name
id = str(uuid4())
current_games.append((this_game, id))
main_controller.master_games_dic[id] = (this_game, state_init, discrim_string)
ext = "?league=" + urllib.parse.quote_plus(league.name)
if league.last_series_check(): #if finals
await channel.send(f"The final series of the {league.name} is starting now, at {config()['simmadome_url']+ext}")
last = True
else:
await channel.send(f"The next series of the {league.name} is starting now, at {config()['simmadome_url']+ext}")
last = False
await league_day_watcher(channel, league, current_games, config()['simmadome_url']+ext, last)
async def league_day_watcher(channel, league, games_list, filter_url, last = False):
league.active = True
active_leagues.append(league)
wins_in_series = {}
losses_in_series = {}
while league.active:
queued_games = []
while len(games_list) > 0:
try:
for i in range(0, len(games_list)):
game, key = games_list[i]
if game.over and main_controller.master_games_dic[key][1]["end_delay"] <= 8:
if game.teams['home'].name not in wins_in_series.keys():
wins_in_series[game.teams["home"].name] = 0
if game.teams['home'].name not in losses_in_series.keys():
losses_in_series[game.teams["home"].name] = 0
if game.teams['away'].name not in wins_in_series.keys():
wins_in_series[game.teams["away"].name] = 0
if game.teams['away'].name not in losses_in_series.keys():
losses_in_series[game.teams["away"].name] = 0
winner_name = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name
loser_name = game.teams['away'].name if game.teams['home'].score > game.teams['away'].score else game.teams['home'].name
wins_in_series[winner_name] += 1
losses_in_series[loser_name] += 1
final_embed = game_over_embed(game)
await channel.send(f"A {league.name} game just ended!")
await channel.send(embed=final_embed)
if wins_in_series[winner_name] + losses_in_series[winner_name] < league.series_length:
queued_games.append(game)
games_list.pop(i)
break
except:
print("something went wrong in league_day_watcher")
await asyncio.sleep(4)
if len(queued_games) > 0:
now = datetime.datetime.now()
validminutes = [int((60 * div)/league.games_per_hour) for div in range(0,league.games_per_hour)]
for i in range(0, len(validminutes)):
if now.minute > validminutes[i]:
if i < len(validminutes)-1:
delta = datetime.timedelta(minutes= (validminutes[i+1] - now.minute))
else:
delta = datetime.timedelta(minutes= (60 - now.minute))
next_start = (now + delta).replace(seconds=0, microsecond=0)
wait_seconds = (next_start - now).seconds
await channel.send(f"The next batch of games for the {league.name} will start in {int(wait_seconds/60)} minutes.")
await asyncio.sleep(wait_seconds)
await channel.send(f"A {league.name} series is continuing now at {filter_url}")
games_list = await continue_league_series(league, queued_games, games_list, wins_in_series)
else:
league.active = False
if last: #if this series was the last
#needs some kind of notification that it's over here
active_leagues.pop(active_leagues.index(league))
return
now = datetime.datetime.now()
validminutes = [int((60 * div)/league.games_per_hour) for div in range(0,league.games_per_hour)]
for i in range(0, len(validminutes)):
if now.minute > validminutes[i]:
if i < len(validminutes)-1:
delta = datetime.timedelta(minutes= (validminutes[i+1] - now.minute))
else:
delta = datetime.timedelta(minutes= (60 - now.minute))
next_start = (now + delta).replace(seconds=0, microsecond=0)
wait_seconds = (next_start - now).seconds
await channel.send(f"""This {league.name} series is now complete! The next series will be starting in {int(wait_seconds/60)} minutes.""")
await asyncio.sleep(wait_seconds)
league.day += 1
await start_league_day(channel, league)
async def continue_league_series(tourney, queue, games_list, wins_in_series):
for oldgame in queue:
away_team = games.get_team(oldgame.teams["away"].name)
home_team = games.get_team(oldgame.teams["home"].name)
this_game = games.game(away_team.finalize(), home_team.finalize(), length = tourney.game_length)
this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True
series_string = f"Series score:"
state_init["title"] = f"{series_string} {wins_in_series[away_team.name]} - {wins_in_series[home_team.name]}"
discrim_string = league.name
id = str(uuid4())
games_list.append((this_game, id))
main_controller.master_games_dic[id] = (this_game, state_init, discrim_string)
return games_list
league = leagues.league_structure("test", {
"nL" : {
"nL west" : ["a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"],
"nL east" : ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]
},
"aL" : {
"aL west" : ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"],
"aL east" : ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"]
}
}, division_games=6, inter_division_games=3, inter_league_games=3)
league.generate_schedule()
#print(league.schedule[2])
client.run(config()["token"])