async def ready_check(self, ctx: commands.Context, players, mismatch, game): """ Posts a message in the given context, pinging the 10 players, trying to start the game. If all 10 players accept the game, returns True. If not, returns False. """ self.games_in_ready_check.add(game.id) logging.info('Starting a game ready check') blue_win_chance = trueskill_blue_side_winrate({(team, role): game.participants[team, role].player for team, role in game.participants}) embed = Embed(title='Proposed game') embed.add_field(name='Team compositions', value=f'Blue side expected winrate is {blue_win_chance * 100:.1f}%.\n' f'```{game}```') if mismatch: embed.add_field(name='WARNING', value='According to TrueSkill, this game might be a slight mismatch.') discord_id_list = [p.discord_id for p in players.values()] ready_check_message = await ctx.send(f'A match has been found for {self.get_tags(discord_id_list)}.\n' 'All players have been dropped from queues they were in.\n' 'You can refuse the match and leave the queue by pressing ❎.\n' 'If you are ready, press ✅.', embed=embed) return_value, accepting_players = await self.checkmark_validation(ready_check_message, discord_id_list, 10) self.games_in_ready_check.remove(game.id) return return_value, accepting_players
async def ready_check(self, ctx: commands.Context, players, mismatch): """ Posts a message in the given context, pinging the 10 players, trying to start the game. If all 10 players accept the game, returns True. If not, returns False. """ logging.info("Starting a game ready check") blue_win_chance = trueskill_blue_side_winrate(players) # TODO Mix it with game.__str__ game_representation = tabulate( { team_column.capitalize(): [ players[team, role].name for (team, role) in sorted( players, key=lambda x: roles_list.index(x[1])) if team == team_column ] for team_column in ["blue", "red"] }, headers="keys", ) embed = Embed(title="Proposed game") embed.add_field( name="Team compositions", value= f"Blue side expected winrate is {blue_win_chance * 100:.1f}%.\n" f"```{game_representation}```", ) if mismatch: embed.add_field( name="WARNING", value= "According to TrueSkill, this game might be a slight mismatch." ) discord_id_list = [p.discord_id for p in players.values()] ready_check_message = await ctx.send( f"A match has been found for {self.get_tags(discord_id_list)}.\n" "All players have been dropped from queues they were in.\n" "You can refuse the match and leave the queue by pressing ❎.\n" "If you are ready, press ✅.", embed=embed, ) return_value, accepting_players = await self.checkmark_validation( ready_check_message, discord_id_list, 10, timeout=5 * 60, queue=True) return return_value, accepting_players
def find_best_game(self, channel_id) -> Tuple[dict, int]: """ Looks at the queue in the channel and returns the best match-made game (as a {team, role} -> Player dict). """ # Do not do anything if there’s not at least 2 players in queue per role for role in roles_list: if self.channel_queues[channel_id][role].__len__() < 2: logging.debug('Not enough players to start matchmaking') return {}, -1 logging.info('Starting matchmaking process') # Simply testing all permutations because it should be pretty lightweight # TODO Spot mirrored team compositions (full blue/red -> red/blue) to not calculate them twice role_permutations = [] for role in roles_list: role_permutations.append([ p for p in itertools.permutations( self.channel_queues[channel_id][role], 2) ]) # Very simple maximum search best_score = -1 best_players = {} for team_composition in itertools.product(*role_permutations): # players: [team, role] -> Player players = { ('red' if tuple_idx else 'blue', roles_list[role_idx]): players_tuple[tuple_idx] for role_idx, players_tuple in enumerate(team_composition) for tuple_idx in (0, 1) } # We check to make sure all 10 players are different if set(players.values()).__len__() != 10: continue # Defining the score as -|0.5-expected_blue_winrate| to be side-agnostic. score = -abs(0.5 - trueskill_blue_side_winrate(players)) if score > best_score: best_players = players best_score = score # If the game is seen as being below 51% winrate for one side, we simply stop there if best_score > -0.01: break logging.info( 'The best match found had a score of {}'.format(best_score)) return best_players, best_score
def __init__(self, players: dict): """ Creates a Game object and its GameParticipant children. :param players: [team, role] -> Player dictionary """ self.date = datetime.datetime.now() self.blue_side_predicted_winrate = trueskill_blue_side_winrate(players) self.participants = {(team, role): GameParticipant(self, team, role, players[team, role]) for team, role in players}
def find_best_game( channel_id) -> Tuple[Dict[Tuple[str, str], Player], int]: """ Looks at the queue in the channel and returns the best match-made game (as a {team, role} -> Player dict). """ # Getting players in queue session = get_session() players_in_queue = (session.query(QueuePlayer).options( joinedload(QueuePlayer.player)).filter( QueuePlayer.channel_id == channel_id).filter( QueuePlayer.ready_check == None).all()) queue = {} for role in roles_list: queue[role] = [ p.player for p in players_in_queue if p.role == role ] # Do not do anything if there’s not at least 2 players in queue per role for role in queue: if len(queue[role]) < 2: logging.debug("Not enough players to start matchmaking") return {}, -1 logging.info("Starting matchmaking process") # Simply testing all permutations because it should be pretty lightweight # TODO Spot mirrored team compositions (full blue/red -> red/blue) to not calculate them twice role_permutations = [] for role in roles_list: role_permutations.append( [p for p in itertools.permutations(queue[role], 2)]) # Very simple maximum search best_score = -1 best_players = {} for team_composition in itertools.product(*role_permutations): # players: [team, role] -> Player players = { ("red" if tuple_idx else "blue", roles_list[role_idx]): players_tuple[tuple_idx] for role_idx, players_tuple in enumerate(team_composition) for tuple_idx in (0, 1) } # We check to make sure all 10 players are different if set(players.values()).__len__() != 10: continue # Defining the score as -|0.5-expected_blue_winrate| to be side-agnostic. score = -abs(0.5 - trueskill_blue_side_winrate(players)) if score > best_score: best_players = players best_score = score # If the game is seen as being below 51% winrate for one side, we simply stop there if best_score > -0.01: break session.close() logging.info( "The best match found had a score of {}".format(best_score)) return best_players, best_score