Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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}
Beispiel #5
0
    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