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
166
leagues.py
166
leagues.py
|
@ -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:
|
||||
|
@ -55,6 +208,7 @@ class tournament(object):
|
|||
def sorter(team_in_list):
|
||||
return team_in_list.average_stars()
|
||||
|
||||
if not manual:
|
||||
teams_list.sort(key=sorter, reverse=True)
|
||||
|
||||
|
||||
|
|
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 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"])
|
||||
|
|
Loading…
Reference in New Issue
Block a user