Ejemplo n.º 1
0
    def messages(self, gamedb: Database) -> List[SMSEventMessage]:
        player_messages = []
        losing_teams = gamedb.stream_teams(from_tribe=self.losing_tribe)
        winning_teams = gamedb.stream_teams(from_tribe=self.winning_tribe)

        for team in losing_teams:
            losing_players = []

            # TODO(brandon): optimize this.
            for player in gamedb.list_players(from_team=team):
                losing_players.append(player)

            options_map = messages.players_as_formatted_options_map(
                players=losing_players)

            for player in losing_players:
                gamedb.ballot(player_id=player.id,
                              options=options_map.options,
                              challenge_id=None)

            # NOTE: UX isn't perfect here because we'll show the player's own name
            # as an option to vote out. For MVP this helps with scale because the alternative
            # requires sending a different message to every player (as opposed to every team)
            # which is about a 5x cost increase for SMS.
            player_messages.append(
                SMSEventMessage(
                    content=messages.
                    NOTIFY_MULTI_TRIBE_COUNCIL_EVENT_LOSING_MSG_FMT.format(
                        header=messages.game_sms_header(gamedb=gamedb),
                        tribe=self.losing_tribe.name,
                        time=self.game_options.game_schedule.
                        localized_time_string(self.game_options.game_schedule.
                                              daily_tribal_council_end_time),
                        options=options_map.formatted_string),
                    recipient_phone_numbers=[
                        p.phone_number for p in losing_players
                    ]))

        winning_player_phone_numbers = []
        for team in winning_teams:
            winning_players = gamedb.list_players(from_team=team)
            winning_player_phone_numbers.extend(
                [p.phone_number for p in winning_players])

        player_messages.append(
            SMSEventMessage(
                content=messages.
                NOTIFY_MULTI_TRIBE_COUNCIL_EVENT_WINNING_MSG_FMT.format(
                    header=messages.game_sms_header(gamedb=gamedb),
                    winning_tribe=self.winning_tribe.name,
                    losing_tribe=self.losing_tribe.name,
                    time=self.game_options.game_schedule.localized_time_string(
                        self.game_options.game_schedule.
                        daily_tribal_council_end_time),
                    challenge_time=self.game_options.game_schedule.
                    localized_time_string(self.game_options.game_schedule.
                                          daily_challenge_start_time)),
                recipient_phone_numbers=winning_player_phone_numbers))
        return player_messages
Ejemplo n.º 2
0
    def _merge_teams(self, target_team_size: int, tribe: Tribe, gamedb: Database, engine: Engine):
        # team merging is only necessary when the size of the team == 2
        # once a team size == 2, it should be merged with another team. the optimal
        # choice is to keep team sizes as close to the intended size as possible

        # find all teams with size = 2, these players need to be merged
        small_teams = gamedb.stream_teams(
            from_tribe=tribe, team_size_predicate_value=2)
        merge_candidates = Queue()

        for team in small_teams:
            log_message("Found team of 2. Deacticating team {}.".format(team))

            # do not deactivate the last active team in the tribe
            if gamedb.count_teams(from_tribe=tribe, active_team_predicate_value=True) > 1:
                gamedb.deactivate_team(team)

            for player in gamedb.list_players(from_team=team):
                log_message("Adding merge candidate {}.".format(player))
                merge_candidates.put(player)

        sorted_teams = gamedb.stream_teams(
            from_tribe=tribe, order_by_size=True, descending=False)

        log_message("Redistributing merge candidates...")
        # round robin redistribution strategy
        # simplest case, could use more thought.
        visited = {}
        while not merge_candidates.empty() and sorted_teams:
            for team in sorted_teams:
                other_options_available = team.id not in visited
                visited[team.id] = True

                if (team.size >= target_team_size and other_options_available):
                    log_message("Team {} has size >= target {} and other options are available. "
                                "Continuing search...".format(team, target_team_size))
                    continue

                player = merge_candidates.get()
                if player.team_id == team.id:
                    continue

                log_message("Merging player {} from team {} into team {}.".format(
                    player, player.team_id, team.id))
                player.team_id = team.id
                team.size = team.size + 1
                gamedb.save(team)
                gamedb.save(player)

                # notify player of new team assignment
                engine.add_event(events.NotifyTeamReassignmentEvent(game_id=self._game_id, game_options=self._options, player=player,
                                                                    team=team))
Ejemplo n.º 3
0
    def _run_multi_tribe_council(self, winning_tribe: Tribe,
                                 losing_tribe: Tribe, gamedb: Database,
                                 engine: Engine):
        self._wait_for_tribal_council_start_time()

        # fraction of teams in losing tribe must vote
        non_immune_teams = list()
        for team in gamedb.stream_teams(from_tribe=losing_tribe):
            log_message(message="Found losing team {}.".format(team),
                        game_id=self._game_id)
            immunity_granted = random.random(
            ) < self._options.multi_tribe_team_immunity_likelihood
            if not immunity_granted:
                non_immune_teams.append(team)
            else:
                engine.add_event(
                    events.NotifyImmunityAwardedEvent(
                        game_id=self._game_id,
                        game_options=self._options,
                        team=team))

        # announce winner and tribal council for losing tribe
        gamedb.clear_votes()
        engine.add_event(
            events.NotifyMultiTribeCouncilEvent(game_id=self._game_id,
                                                game_options=self._options,
                                                winning_tribe=winning_tribe,
                                                losing_tribe=losing_tribe))
        self._wait_for_tribal_council_end_time()

        # count votes
        for team in non_immune_teams:
            log_message(
                message="Counting votes for non-immune team {}.".format(team),
                game_id=self._game_id)
            voted_out_player = self._get_voted_out_player(team=team,
                                                          gamedb=gamedb)
            if voted_out_player:
                gamedb.deactivate_player(player=voted_out_player)
                log_message(
                    message="Deactivated player {}.".format(voted_out_player),
                    game_id=self._game_id)
                engine.add_event(
                    events.NotifyPlayerVotedOutEvent(
                        game_id=self._game_id,
                        game_options=self._options,
                        player=voted_out_player))

        # notify all players of what happened at tribal council
        engine.add_event(
            events.NotifyTribalCouncilCompletionEvent(
                game_id=self._game_id, game_options=self._options))
Ejemplo n.º 4
0
    def _run_multi_tribe_council(self, winning_tribe: Tribe, losing_tribe: Tribe, gamedb: Database, engine: Engine):
        non_immune_teams = list()

        for team in gamedb.stream_teams(from_tribe=losing_tribe):
            log_message("Found losing team {}.".format(team))
            immunity_granted = random.random() < self._options.multi_tribe_team_immunity_likelihood
            if not immunity_granted:
                non_immune_teams.append(team)
            else:
                engine.add_event(events.NotifyImmunityAwardedEvent(
                    game_id=self._game_id, game_options=self._options, team=team))

        # announce winner and tribal council for losing tribe
        tribal_council_start_timestamp = _unixtime()
        gamedb.clear_votes()
        engine.add_event(events.NotifyMultiTribeCouncilEvent(game_id=self._game_id, game_options=self._options,
                                                             winning_tribe=winning_tribe, losing_tribe=losing_tribe))

        # wait for votes
        while (((_unixtime() - tribal_council_start_timestamp)
                < self._options.multi_tribe_council_time_sec) and not self._stop.is_set()):
            log_message("Waiting for tribal council to end.")
            time.sleep(self._options.game_wait_sleep_interval_sec)

        # count votes
        for team in non_immune_teams:
            log_message("Counting votes for non-immune team {}.".format(team))
            voted_out_player = self._get_voted_out_player(
                team=team, gamedb=gamedb)
            if voted_out_player:
                gamedb.deactivate_player(player=voted_out_player)
                log_message("Deactivated player {}.".format(voted_out_player))
                engine.add_event(events.NotifyPlayerVotedOutEvent(game_id=self._game_id, game_options=self._options,
                                                                  player=voted_out_player))

        # notify all players of what happened at tribal council
        engine.add_event(events.NotifyTribalCouncilCompletionEvent(
            game_id=self._game_id, game_options=self._options))
Ejemplo n.º 5
0
    def _merge_teams(self, target_team_size: int, tribe: Tribe,
                     gamedb: Database, engine: Engine):
        with engine:
            # team merging is only necessary when the size of the team == 2
            # once a team size == 2, it should be merged with another team, because
            # a self preservation vote lands in a deadlock. in general, the optimal
            # choice is to keep team sizes as close to the intended size as possible
            # up until a merge becomes necessary.

            # find all teams with size == 2, these players need to be merged
            small_teams = gamedb.stream_teams(from_tribe=tribe,
                                              team_size_predicate_value=2)
            merge_candidates = Queue()

            for team in small_teams:
                # do not deactivate the last active team in the tribe
                count_teams = gamedb.count_teams(
                    from_tribe=tribe, active_team_predicate_value=True)
                if count_teams > 1:
                    log_message(
                        message="Found team of 2. Deactivating team {}.".
                        format(team),
                        game_id=self._game_id)
                    gamedb.deactivate_team(team)
                for player in gamedb.list_players(from_team=team):
                    log_message(
                        message="Adding merge candidate {}.".format(player),
                        game_id=self._game_id)
                    merge_candidates.put(player)

            sorted_teams = gamedb.stream_teams(from_tribe=tribe,
                                               order_by_size=True,
                                               descending=False)

            log_message(message="Redistributing merge candidates...",
                        game_id=self._game_id)
            # round robin redistribution strategy
            # simplest case, could use more thought.
            visited = {}
            while not merge_candidates.empty() and sorted_teams:
                for team in itertools.cycle(sorted_teams):
                    team.count_players = _team_count_players(team, gamedb)
                    other_options_available = team.id not in visited
                    visited[team.id] = True

                    if (team.count_players >= target_team_size
                            and other_options_available):
                        log_message(
                            message=
                            "Team {} has size >= target {} and other options are available. "
                            "Continuing search...".format(
                                team, target_team_size),
                            game_id=self._game_id)
                        continue

                    player = None
                    try:
                        player = merge_candidates.get_nowait()
                    except Empty:
                        log_message(message="Merge candidates empty.")
                        return

                    if player.team_id == team.id:
                        log_message(
                            message=
                            f"Player {str(player)} already on team {str(team)}. Continuing."
                        )
                        continue

                    log_message(
                        message="Merging player {} from team {} into team {}.".
                        format(player, player.team_id, team.id),
                        game_id=self._game_id)
                    player.team_id = team.id
                    team.count_players += 1
                    gamedb.save(team)
                    gamedb.save(player)

                    # notify player of new team assignment
                    engine.add_event(
                        events.NotifyTeamReassignmentEvent(
                            game_id=self._game_id,
                            game_options=self._options,
                            player=player,
                            team=team))