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:
parent
20a13fdf2e
commit
31930aab63
170
leagues.py
170
leagues.py
|
@ -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:
|
||||||
|
@ -54,8 +207,9 @@ class tournament(object):
|
||||||
else: #sort by average stars
|
else: #sort by average stars
|
||||||
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)))
|
||||||
|
|
161
the_prestige.py
161
the_prestige.py
|
@ -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"])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user