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 import time, asyncio, jsonpickle, random, math
from itertools import chain
from games import team, game from games import team, game
from discord import Embed, Color from discord import Embed, Color
import database as db import database as db
@ -7,13 +8,165 @@ import database as db
class league(object): class league_structure(object):
def __init__(self, name, subleagues_dic): def __init__(self, name, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2):
self.subleagues = {} #key: name, value: [divisions] self.league = league_dic #key: subleague, value: {division, teams}
self.max_days 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.day = 1
self.name = name 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): class division(object):
def __init__(self): def __init__(self):
@ -40,7 +193,7 @@ class tournament(object):
self.id = id 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() teams_list = list(self.teams.keys()).copy()
if random_sort: if random_sort:
@ -55,7 +208,8 @@ class tournament(object):
def sorter(team_in_list): def sorter(team_in_list):
return team_in_list.average_stars() 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))) 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 database as db
import onomancer as ono import onomancer as ono
import random
from the_draft import Draft, DRAFT_ROUNDS from the_draft import Draft, DRAFT_ROUNDS
from flask import Flask from flask import Flask
from uuid import uuid4 from uuid import uuid4
@ -757,6 +756,7 @@ commands = [
client = discord.Client() client = discord.Client()
gamesarray = [] gamesarray = []
active_tournaments = [] active_tournaments = []
active_leagues = []
setupmessages = {} setupmessages = {}
thread1 = threading.Thread(target=main_controller.update_loop) thread1 = threading.Thread(target=main_controller.update_loop)
@ -1382,4 +1382,161 @@ def get_team_fuzzy_search(team_name):
team = teams[0] team = teams[0]
return team 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"]) client.run(config()["token"])