finished implementing tournaments, slowed game ticks to 8 seconds

This commit is contained in:
Sakimori 2021-01-03 23:18:45 -05:00
parent 63db3ba1be
commit ff582d8709
4 changed files with 203 additions and 55 deletions

View File

@ -185,12 +185,11 @@ class team(object):
for this_player in self.rotation: for this_player in self.rotation:
for stat in this_player.game_stats.keys(): for stat in this_player.game_stats.keys():
this_player.game_stats[stat] = 0 this_player.game_stats[stat] = 0
return True return self
def finalize(self): def finalize(self):
if self.is_ready(): if self.is_ready():
if self.pitcher is None: self.set_pitcher()
self.set_pitcher()
while len(self.lineup) <= 4: while len(self.lineup) <= 4:
self.lineup.append(random.choice(self.lineup)) self.lineup.append(random.choice(self.lineup))
return self return self

View File

@ -20,14 +20,25 @@ class division(object):
self.teams = {} #key: team object, value: {wins; rd (run diff)} self.teams = {} #key: team object, value: {wins; rd (run diff)}
class tournament(object): class tournament(object):
def __init__(self, name, team_dic, series_length = 5, max_innings = 9): 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):
self.name = name self.name = name
self.teams = team_dic #same format as division, wins/losses will be used for seeding later self.teams = team_dic #same format as division, wins/losses will be used for seeding later
self.bracket = None self.bracket = None
self.results = None self.results = None
self.series_length = series_length self.series_length = series_length
self.finals_length = finals_series_length
self.game_length = max_innings self.game_length = max_innings
self.active = False self.active = False
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
def build_bracket(self, random_sort = False, by_wins = False): def build_bracket(self, random_sort = False, by_wins = False):
teams_list = list(self.teams.keys()).copy() teams_list = list(self.teams.keys()).copy()
@ -46,12 +57,12 @@ class tournament(object):
teams_list.sort(key=sorter, reverse=True) 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)))
empty_slots = int(math.pow(2, bracket_layers) - len(teams_list)) empty_slots = int(math.pow(2, bracket_layers) - len(teams_list))
for i in range(0, empty_slots): for i in range(0, empty_slots):
teams_list.append(None) teams_list.append(None)
print(teams_list)
previous_bracket_layer = teams_list.copy() previous_bracket_layer = teams_list.copy()
for i in range(0, bracket_layers - 1): for i in range(0, bracket_layers - 1):
@ -60,19 +71,27 @@ class tournament(object):
if pair % 2 == 0: #if even number 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 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: else:
this_layer.insert(0-int((1+pair)/2), [previous_bracket_layer.pop(0), previous_bracket_layer.pop(-1)]) #every other pair goes at end of list, moving backward 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
previous_bracket_layer = this_layer previous_bracket_layer = this_layer
print(previous_bracket_layer)
self.bracket = bracket(previous_bracket_layer, bracket_layers) self.bracket = bracket(previous_bracket_layer, bracket_layers)
self.bracket.get_bottom_row()
def round_check(self):
if self.bracket.depth == 1:
self.finals = True
return True
else:
return False
class bracket(object): class bracket(object):
this_bracket = []
def __init__(self, bracket_list, depth): def __init__(self, bracket_list, depth):
self.this_bracket = bracket_list self.this_bracket = bracket_list
self.depth = depth self.depth = depth
self.bottom_row = [] self.bottom_row = []
def get_bottom_row(self): def get_bottom_row(self):
self.depth = 1
self.bottom_row = [] self.bottom_row = []
self.dive(self.this_bracket) self.dive(self.this_bracket)
return self.bottom_row return self.bottom_row
@ -81,4 +100,28 @@ class bracket(object):
if not isinstance(branch[0], list): #if it's a pair of games if not isinstance(branch[0], list): #if it's a pair of games
self.bottom_row.append(branch) self.bottom_row.append(branch)
else: else:
self.depth += 1
return self.dive(branch[0]), self.dive(branch[1]) return self.dive(branch[0]), self.dive(branch[1])
#def set_winners(self, branch, winners_list):
#new_bracket =
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
return branch

View File

@ -153,4 +153,4 @@ def update_loop():
}) })
socketio.emit("states_update", data_to_send) socketio.emit("states_update", data_to_send)
time.sleep(6) time.sleep(8)

View File

@ -427,25 +427,38 @@ class AssignOwnerCommand(Command):
class StartTournamentCommand(Command): class StartTournamentCommand(Command):
name = "starttournament" name = "starttournament"
template = "m;starttournament" template = """m;starttournament
description = "We'll DM you and get your own tournament set up. Just follow our instructions and we'll be right as rain." [tournament name]
[list of teams, each on a new line]"""
description = "Starts a tournament with the teams given. Byes will be given to teams to allow for numbers other than powers of two. The current tournament format is:\nBest of 5 until the finals, which are Best of 7"
async def execute(self, msg, command): async def execute(self, msg, command):
test_bracket = { to_parse = command.split("\n")[0]
games.get_team("Milwaukee Lockpicks") : {"wins": 10, "rd": 0}, if "--rounddelay " in to_parse:
games.get_team("Madagascar Penguins") : {"wins": 2, "rd": 0}, try:
games.get_team("Twin Cities Evening") : {"wins": 1, "rd": 0}, round_delay = int(to_parse.split("--rounddelay ")[1].split(" ")[0])
games.get_team("Washington State Houses") : {"wins": 9, "rd": 0}, except ValueError:
games.get_team("Appalachian Underground") : {"wins": 8, "rd": 0}, await msg.channel.send("The delay between rounds should be a whole number.")
games.get_team("Pacific2 Rams") : {"wins": 3, "rd": 0}, return
games.get_team("New Jersey Radio") : {"wins": 45, "rd": 0}, if round_delay < 1 or round_delay > 120:
games.get_team("Moline Jolenes") : {"wins": 44, "rd": 0}, await msg.channel.send("The delay between rounds has to be between 1 and 120 minutes.")
games.get_team("California Commissioners") : {"wins": 41, "rd": 0}, else:
games.get_team("Pigeons Reckoning") : {"wins": 45, "rd": 0}, round_delay = 10
games.get_team("Kernow Technologists") : {"wins": 42, "rd": 0}
} tourney_name = command.split("\n")[1]
tourney = leagues.tournament("Test Tourney", test_bracket, max_innings=3) list_of_team_names = command.split("\n")[2:]
tourney.build_bracket(by_wins=True) team_dic = {}
for name in list_of_team_names:
team = get_team_fuzzy_search(name.strip())
if team == None:
await msg.channel.send(f"We couldn't find {name}. Try again?")
return
team_dic[team] = {"wins": 0}
id = random.randint(1111,9999)
tourney = leagues.tournament(tourney_name, team_dic, id=id, secs_between_rounds = round_delay * 60)
tourney.build_bracket(random_sort = True)
await start_tournament_round(msg.channel, tourney) await start_tournament_round(msg.channel, tourney)
@ -477,7 +490,7 @@ commands = [
client = discord.Client() client = discord.Client()
gamesarray = [] gamesarray = []
gamesqueue = [] active_tournaments = []
setupmessages = {} setupmessages = {}
thread1 = threading.Thread(target=main_controller.update_loop) thread1 = threading.Thread(target=main_controller.update_loop)
@ -684,7 +697,10 @@ async def watch_game(channel, newgame, user = None, league = None):
discrim_string = league discrim_string = league
state_init["is_league"] = True state_init["is_league"] = True
elif user is not None: elif user is not None:
discrim_string = f"Started by {user.name}" if isinstance(user, str):
discrim_string = f"Started by {user}"
else:
discrim_string = f"Started by {user.name}"
state_init["is_league"] = False state_init["is_league"] = False
else: else:
discrim_string = "Unclaimed game." discrim_string = "Unclaimed game."
@ -739,13 +755,12 @@ async def start_tournament_round(channel, tourney, seeding = None):
for pair in games_to_start: for pair in games_to_start:
if pair[0] is not None and pair[1] is not None: if pair[0] is not None and pair[1] is not None:
this_game = games.game(pair[0].finalize(), pair[1].finalize(), length = tourney.game_length) this_game = games.game(pair[0].prepare_for_save().finalize(), pair[1].prepare_for_save().finalize(), length = tourney.game_length)
this_game, state_init = prepare_game(this_game) this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True state_init["is_league"] = True
state_init["title"] = f"{tourney.name}: Round of {len(games_to_start)*2}" state_init["title"] = f"0 - 0"
discrim_string = tourney.name discrim_string = tourney.name
print(discrim_string)
timestamp = str(time.time() * 1000.0 + random.randint(0,3000)) timestamp = str(time.time() * 1000.0 + random.randint(0,3000))
current_games.append((this_game, timestamp)) current_games.append((this_game, timestamp))
@ -753,28 +768,100 @@ async def start_tournament_round(channel, tourney, seeding = None):
ext = "?league=" + urllib.parse.quote_plus(tourney.name) ext = "?league=" + urllib.parse.quote_plus(tourney.name)
await channel.send(f"{len(current_games)} games started for the {tourney.name} tournament, at {config()['simmadome_url']+ext}") if tourney.round_check(): #if finals
await tourney_watcher(channel, tourney, current_games) await channel.send(f"The {tourney.name} finals are starting now, at {config()['simmadome_url']+ext}")
finals = True
else:
await channel.send(f"{len(current_games)} games started for the {tourney.name} tournament, at {config()['simmadome_url']+ext}")
finals = False
await tourney_round_watcher(channel, tourney, current_games, config()['simmadome_url']+ext, finals)
async def continue_tournament_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)
async def tourney_watcher(channel, tourney, games_list): state_init["is_league"] = True
state_init["title"] = f"{wins_in_series[oldgame.teams['away'].name]} - {wins_in_series[oldgame.teams['home'].name]}"
discrim_string = tourney.name
timestamp = str(time.time() * 1000.0 + random.randint(0,3000))
games_list.append((this_game, timestamp))
main_controller.master_games_dic[timestamp] = (this_game, state_init, discrim_string)
return games_list
async def tourney_round_watcher(channel, tourney, games_list, filter_url, finals = False):
tourney.active = True tourney.active = True
while len(games_list) > 0: active_tournaments.append(tourney)
try: wins_in_series = {}
for i in range(0, len(games_list)): winner_list = []
game, key = games_list[i] while tourney.active:
if game.over and main_controller.master_games_dic[key][1]["end_delay"] <= 9: queued_games = []
final_embed = game_over_embed(game) while len(games_list) > 0:
await channel.send(f"A {tourney.name} game just ended!") try:
await channel.send(embed=final_embed) for i in range(0, len(games_list)):
games_list.pop(i) game, key = games_list[i]
break if game.over and main_controller.master_games_dic[key][1]["end_delay"] <= 9:
except: if game.teams['home'].name not in wins_in_series.keys():
print("something went wrong in tourney_watcher") wins_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
winner_name = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name
if winner_name in wins_in_series.keys():
wins_in_series[winner_name] += 1
else:
wins_in_series[winner_name] = 1
final_embed = game_over_embed(game)
await channel.send(f"A {tourney.name} game just ended!")
await channel.send(embed=final_embed)
if wins_in_series[winner_name] >= int((tourney.series_length+1)/2) and not finals:
winner_list.append(winner_name)
elif wins_in_series[winner_name] >= int((tourney.finals_length+1)/2):
winner_list.append(winner_name)
else:
queued_games.append(game)
games_list.pop(i)
break
except:
print("something went wrong in tourney_watcher")
await asyncio.sleep(4)
if len(queued_games) > 0:
await channel.send(f"The next batch of games for {tourney.name} will start in {int(tourney.delay/60)} minutes.")
await asyncio.sleep(tourney.delay)
await channel.send(f"{len(queued_games)} games for {tourney.name}, starting at {filter_url}")
games_list = await continue_tournament_series(tourney, queued_games, games_list, wins_in_series)
else:
tourney.active = False
if finals: #if this last round was finals
embed = discord.Embed(color = discord.Color.dark_purple(), title = f"{winner_list[0]} win the {tourney.name} finals!")
await channel.send(embed=embed)
active_tournaments.pop(active_tournaments.index(tourney))
return
tourney.bracket.set_winners_dive(winner_list)
winners_string = ""
for game in tourney.bracket.get_bottom_row():
winners_string += f"{game[0].name}\n{game[1].name}\n"
await channel.send(f"""
This round of games for {tourney.name} is now complete! The next round will be starting in {int(tourney.round_delay/60)} minutes.
Advancing teams:
{winners_string}""")
await asyncio.sleep(tourney.round_delay)
await start_tournament_round(channel, tourney)
await asyncio.sleep(4)
tourney.active = False
await channel.send(f"This round of games for {tourney.name} is now complete!")
async def team_delete_confirm(channel, team, owner): async def team_delete_confirm(channel, team, owner):
@ -843,8 +930,8 @@ def team_from_collection(newteam_json):
raise CommandError("We've given you 100 characters for the slogan. Discord puts limits on us and thus, we put limits on you. C'est la vie.") raise CommandError("We've given you 100 characters for the slogan. Discord puts limits on us and thus, we put limits on you. C'est la vie.")
if len(newteam_json["lineup"]) > 20: if len(newteam_json["lineup"]) > 20:
raise CommandError("20 players in the lineup, maximum. We're being really generous here.") raise CommandError("20 players in the lineup, maximum. We're being really generous here.")
if not len(newteam_json["rotation"]) == 1: if not len(newteam_json["rotation"]) > 8:
raise CommandError("One and only one pitcher per team, thanks.") raise CommandError("8 pitchers on the rotation, max. That's a *lot* of pitchers.")
for player in newteam_json["lineup"] + newteam_json["rotation"]: for player in newteam_json["lineup"] + newteam_json["rotation"]:
if len(player["name"]) > 70: if len(player["name"]) > 70:
raise CommandError(f"{player['name']} is too long, chief. 70 or less.") raise CommandError(f"{player['name']} is too long, chief. 70 or less.")
@ -1003,4 +1090,23 @@ def get_team_fuzzy_search(team_name):
team = teams[0] team = teams[0]
return team return team
#test_bracket = {
# "Milwaukee Lockpicks" : {"wins": 4, "rd": 0},
# "Madagascar Penguins" : {"wins": 2, "rd": 0},
# "Twin Cities Evening" : {"wins": 1, "rd": 0},
# "Washington State Houses" : {"wins": 9, "rd": 0},
# "Appalachian Underground" : {"wins": 8, "rd": 0},
# "Pacific2 Rams" : {"wins": 3, "rd": 0},
# "New Jersey Radio" : {"wins": 11, "rd": 0},
# "Moline Jolenes" : {"wins": 6, "rd": 0},
# "California Commissioners" : {"wins": 10, "rd": 0},
# "Pigeons Reckoning" : {"wins": 7, "rd": 0},
# "Kernow Technologists" : {"wins": 5, "rd": 0}
# }
#tourney = leagues.tournament("Test Tourney", test_bracket, max_innings=3)
#tourney.build_bracket(by_wins=True)
#tourney.bracket.set_winners_dive(['Twin Cities Evening','Madagascar Penguins', 'Pacific2 Rams'])
#print(tourney.bracket.this_bracket)
client.run(config()["token"]) client.run(config()["token"])