Exemple #1
0
    def _play_single_tribe(self, tribe: Tribe, gamedb: Database, engine: Engine) -> Team:
        while gamedb.count_teams(active_team_predicate_value=True) > 1:
            log_message("Teams remaining = {}.".format(
                gamedb.count_teams(active_team_predicate_value=True)))

            log_message("Getting new challenge.")
            challenge = self._get_challenge(gamedb=gamedb)

            log_message("Running challenge {}.".format(challenge))
            self._run_challenge(challenge=challenge,
                                gamedb=gamedb, engine=engine)

            log_message("Scoring entries.")
            winning_teams, losing_teams = self._score_entries_top_k_teams(k=self._options.single_tribe_top_k_threshold,
                                                                          tribe=tribe, challenge=challenge, gamedb=gamedb, engine=engine)

            log_message("Running single tribe council.")
            self._run_single_tribe_council(winning_teams=winning_teams, losing_teams=losing_teams,
                                           gamedb=gamedb, engine=engine)

            log_message("Merging teams.")
            self._merge_teams(target_team_size=self._options.target_team_size, tribe=tribe, gamedb=gamedb,
                              engine=engine)

        return gamedb.list_teams(active_team_predicate_value=True)[0]
Exemple #2
0
    def _get_voted_out_player(self, team: Team,
                              gamedb: Database) -> [Player, None]:
        high = 0
        candidates = []
        log_message(message="Counting votes from team {}.".format(team),
                    game_id=self._game_id)
        team_votes = gamedb.count_votes(from_team=team)
        log_message(message="Got votes {}.".format(pprint.pformat(team_votes)),
                    game_id=self._game_id)

        for _, votes in team_votes.items():
            if votes > high:
                high = votes

        for id, votes in team_votes.items():
            if votes == high:
                candidates.append(id)

        num_candidates = len(candidates)
        if num_candidates == 1:
            return gamedb.player_from_id(candidates[0])
        elif num_candidates > 1:
            return gamedb.player_from_id(candidates[random.randint(
                0, num_candidates - 1)])
        else:
            raise GameError("Unable to determine voted out player.")
Exemple #3
0
    def _run_finalist_tribe_council(self, finalists: List[Player], gamedb: Database, engine: Engine) -> Player:
        gamedb.clear_votes()

        engine.add_event(
            events.NotifyFinalTribalCouncilEvent(
                game_id=self._game_id, game_options=self._options, finalists=finalists))
        tribal_council_start_timestamp = _unixtime()

        # wait for votes
        while (((_unixtime() - tribal_council_start_timestamp)
                < self._options.final_tribal_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
        player_votes = gamedb.count_votes(is_for_win=True)
        max_votes = 0
        winner = None

        for player_id, votes in player_votes.items():
            if votes > max_votes:
                max_votes = votes
                winner = gamedb.player_from_id(id=player_id)

        # announce winner
        engine.add_event(events.NotifyWinnerAnnouncementEvent(
            game_id=self._game_id, game_options=self._options, winner=winner))
        return winner
Exemple #4
0
    def _run_single_tribe_council(self, winning_teams: List[Team], losing_teams: List[Team],
                                  gamedb: Database, engine: Engine):

        # announce winner and tribal council for losing teams
        gamedb.clear_votes()
        engine.add_event(events.NotifySingleTribeCouncilEvent(
            game_id=self._game_id, game_options=self._options,
            winning_teams=winning_teams, losing_teams=losing_teams))
        tribal_council_start_timestamp = _unixtime()

        # wait for votes
        while (((_unixtime() - tribal_council_start_timestamp)
                < self._options.single_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 losing_teams:
            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))
            else:
                log_message("For some reason no one got voted out...")
                log_message("Players = {}.".format(
                    pprint.pformat(gamedb.list_players(from_team=team))))

        # notify all players of what happened at tribal council
        engine.add_event(
            events.NotifyTribalCouncilCompletionEvent(game_id=self._game_id, game_options=self._options))
Exemple #5
0
    def _run_single_team_council(self, team: Team, losing_players: List[Player], gamedb: Database, engine: Engine):
        # announce winner and tribal council for losing teams
        gamedb.clear_votes()

        winning_player = [player for player in gamedb.list_players(
            from_team=team) if player not in losing_players][0]
        engine.add_event(events.NotifySingleTeamCouncilEvent(game_id=self._game_id, game_options=self._options,
                                                             winning_player=winning_player, losing_players=losing_players))
        tribal_council_start_timestamp = _unixtime()

        # wait for votes
        while (((_unixtime() - tribal_council_start_timestamp)
                < self._options.single_team_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
        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))
Exemple #6
0
    def _score_entries_tribe_aggregate(self, tribe: Tribe,
                                       challenge: Challenge, gamedb: Database,
                                       engine: Engine):
        score_dict = {'score': 0}
        players = gamedb.count_players(from_tribe=tribe)
        entries = gamedb.stream_entries(from_tribe=tribe,
                                        from_challenge=challenge)

        with ThreadPoolExecutor(max_workers=self._options.
                                engine_worker_thread_count) as executor:
            executor.submit(self._score_entries_tribe_aggregate_fn,
                            entries=entries,
                            challenge=challenge,
                            score_dict=score_dict,
                            gamedb=gamedb,
                            engine=engine)

        # tribe score = avg score of all tribe members
        log_message(message="_score_entries_tribe_agg = {}.".format(
            score_dict['score']),
                    game_id=self._game_id)

        if players > 0:
            return score_dict['score'] / players
        return 0
Exemple #7
0
    def _score_entries_top_k_players(self, team: Team, challenge: Challenge, gamedb: Database, engine: Engine) -> List[Player]:
        player_scores = {}
        top_scores = list()
        losing_players = list()
        entries = gamedb.stream_entries(
            from_team=team, from_challenge=challenge)

        with ThreadPoolExecutor(max_workers=self._options.engine_worker_thread_count) as executor:
            executor.submit(self._score_entries_top_k_players_fn,
                            entries=entries, challenge=challenge, score_dict=player_scores, gamedb=gamedb, engine=engine)

        for player_id, score in player_scores.items():
            heapq.heappush(top_scores, (score, player_id))

        # note that the default python heap pops in ascending order,
        # so the rank here is actually worst to best.
        num_scores = len(top_scores)
        if num_scores == 1:
            raise GameError(
                "Unable to rank losing players with team size = 1.")
        else:
            for rank in range(num_scores):
                score, player_id = heapq.heappop(top_scores)
                log_message("Player {} rank {} with score {}.".format(
                    player_id, rank, score))

                # all but the highest scorer lose
                if rank < (num_scores - 1):
                    losing_players.append(gamedb.player_from_id(player_id))

        return losing_players
Exemple #8
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     player_messages = []
     team = gamedb.team_from_id(id=self.player.team_id)
     teammate_phone_numbers = [p.phone_number for p in gamedb.list_players(
         from_team=team) if p.id != self.player.id]
     player_messages.append(
         SMSEventMessage(
             content=messages.NOTIFY_PLAYER_VOTED_OUT_TEAM_MSG_FMT.format(
                 header=messages.VIR_US_SMS_HEADER,
                 player=messages.format_tiktok_username(
                     self.player.tiktok),
                 time=self.game_options.game_schedule.localized_time_string(
                     self.game_options.game_schedule.daily_challenge_start_time
                 )),
             recipient_phone_numbers=teammate_phone_numbers
         )
     )
     player_messages.append(
         SMSEventMessage(
             content=messages.NOTIFY_PLAYER_VOTED_OUT_MSG_FMT.format(
                 header=messages.VIR_US_SMS_HEADER
             ),
             recipient_phone_numbers=[self.player.phone_number]
         )
     )
     return player_messages
Exemple #9
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)

            # 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.VIR_US_SMS_HEADER,
                        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=messages.players_as_formatted_options_list(
                            # TODO(brandon): exclude the voter from the options list of players
                            # to vote out.
                            players=losing_players
                        )
                    ),
                    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.VIR_US_SMS_HEADER,
                    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
Exemple #10
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))
Exemple #11
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))
Exemple #12
0
    def messages(self, gamedb: Database) -> List[SMSEventMessage]:
        player_messages = []
        for team in self.losing_teams:
            losing_players = []
            for losing_player in gamedb.list_players(from_team=team):
                losing_players.append(losing_player)

            # 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.
            recipient_phone_numbers = [p.phone_number for p in losing_players]
            options_map = messages.players_as_formatted_options_map(
                players=losing_players)
            # TODO(brandon): this is slow and expensive. batch the ballot writes to gamedb.
            for player in losing_players:
                gamedb.ballot(player_id=player.id,
                              options=options_map.options,
                              challenge_id=None)

            player_messages.append(
                SMSEventMessage(
                    content=messages.
                    NOTIFY_SINGLE_TRIBE_COUNCIL_EVENT_LOSING_MSG_FMT.format(
                        header=messages.game_sms_header(gamedb=gamedb),
                        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=recipient_phone_numbers))

        winning_player_phone_numbers = []
        for team in self.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_SINGLE_TRIBE_COUNCIL_EVENT_WINNING_MSG_FMT.format(
                    header=messages.game_sms_header(gamedb=gamedb),
                    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
Exemple #13
0
    def _score_entries_top_k_teams_fn(self, entries: Iterable,
                                      challenge: Challenge, score_dict: Dict,
                                      gamedb: Database, engine: Engine):
        entries_iter = iter(entries)
        while not self._stop_event.is_set():
            try:
                entry = next(entries_iter)
                log_message(message="Entry {}.".format(entry),
                            game_id=self._game_id)
                points = self._score_entry(entry=entry)
                player = gamedb.player_from_id(entry.player_id)
                engine.add_event(
                    events.NotifyPlayerScoreEvent(game_id=self._game_id,
                                                  game_options=self._options,
                                                  player=player,
                                                  challenge=challenge,
                                                  entry=entry,
                                                  points=points))

                if player.team_id not in score_dict:
                    score_dict[player.team_id] = points
                else:
                    score_dict[player.team_id] += points
            except StopIteration:
                break
Exemple #14
0
    def _run_challenge(self, challenge: Challenge, gamedb: Database,
                       engine: Engine):
        # wait for challenge to begin
        self._wait_for_challenge_start_time(challenge=challenge)

        # notify players
        engine.add_event(
            events.NotifyTribalChallengeEvent(game_id=self._game_id,
                                              game_options=self._options,
                                              challenge=challenge))

        # wait for challenge to end
        self._wait_for_challenge_end_time(challenge=challenge)

        challenge.complete = True
        gamedb.save(challenge)
Exemple #15
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     player_messages = []
     players = gamedb.stream_players(active_player_predicate_value=True)
     # TODO(brandon): parallelize
     # NOTE(brandon) this is going to be problematic at scale. we're sending personalized links to
     # every player in the game, which costs 1 API call / player within Twilio. If we can make these links
     # standard then a single Notify API call can address all users in a single game. Non-critical for MVP.
     for player in players:
         player_messages.append(
             SMSEventMessage(
                 content=messages.NOTIFY_TRIBAL_CHALLENGE_EVENT_MSG_FMT.
                 format(
                     header=messages.game_sms_header(gamedb=gamedb),
                     challenge=self.challenge.name,
                     # TODO(brandon) refactor into common routes location
                     link=
                     "{hostname}/challenge-submission/{player_id}/{game_id}/{challenge_id}"
                     .format(hostname=messages.VIR_US_HOSTNAME,
                             game_id=self.game_id,
                             player_id=player.id,
                             challenge_id=self.challenge.id),
                     time=self.game_options.game_schedule.
                     localized_time_string(self.game_options.game_schedule.
                                           daily_challenge_end_time)),
                 recipient_phone_numbers=[player.phone_number]))
     return player_messages
Exemple #16
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     return [
         SMSEventMessage(
             content=messages.NOTIFY_FINAL_TRIBAL_COUNCIL_EVENT_MSG_FMT.format(
                 header=messages.VIR_US_HOSTNAME,
                 players=len(self.finalists),
                 game=gamedb.game_from_id(id=self.game_id).hashtag,
                 time=self.game_options.game_schedule.localized_time_string(
                     self.game_options.game_schedule.daily_tribal_council_end_time
                 ),
                 options=messages.players_as_formatted_options_list(
                     players=self.finalists)
             ),
             recipient_phone_numbers=[p.phone_number for p in gamedb.stream_players(
                 active_player_predicate_value=True)]
         )
     ]
Exemple #17
0
    def _run_finalist_tribe_council(self, finalists: List[Player],
                                    gamedb: Database,
                                    engine: Engine) -> Player:
        gamedb.clear_votes()
        self._wait_for_tribal_council_start_time()

        engine.add_event(
            events.NotifyFinalTribalCouncilEvent(game_id=self._game_id,
                                                 game_options=self._options,
                                                 finalists=finalists))
        self._wait_for_tribal_council_end_time()

        # count votes
        player_votes = gamedb.count_votes(is_for_win=True)
        max_votes = 0
        winner = None

        for player_id, votes in player_votes.items():
            if votes > max_votes:
                max_votes = votes
                winner = gamedb.player_from_id(id=player_id)

        for player in gamedb.stream_players(
                active_player_predicate_value=True):
            if player.id != winner.id:
                gamedb.deactivate_player(player=player)

        # announce winner
        engine.add_event(
            events.NotifyWinnerAnnouncementEvent(game_id=self._game_id,
                                                 game_options=self._options,
                                                 winner=winner))
        return winner
Exemple #18
0
 def _get_challenge(self, gamedb: Database) -> Challenge:
     available_challenge_count = 0
     while available_challenge_count == 0 and not self._stop.is_set():
         log_message("Waiting for next challenge to become available.")
         time.sleep(self._options.game_wait_sleep_interval_sec)
         available_challenges = gamedb.list_challenges(
             challenge_completed_predicate_value=False)
         available_challenge_count = len(available_challenges)
     return available_challenges[0]
Exemple #19
0
    def _run_challenge(self, challenge: Challenge, gamedb: Database, engine: Engine):
        # wait for challenge to begin
        while (_unixtime() < challenge.start_timestamp) and not self._stop.is_set():
            log_message("Waiting {}s for challenge to {} to begin.".format(
                challenge.start_timestamp - _unixtime(), challenge))
            time.sleep(self._options.game_wait_sleep_interval_sec)

        # notify players
        engine.add_event(
            events.NotifyTribalChallengeEvent(game_id=self._game_id, game_options=self._options, challenge=challenge))

        # wait for challenge to end
        while (_unixtime() < challenge.end_timestamp) and not self._stop.is_set():
            log_message("Waiting {}s for challenge to {} to end.".format(
                challenge.end_timestamp - _unixtime(), challenge))
            time.sleep(self._options.game_wait_sleep_interval_sec)

        challenge.complete = True
        gamedb.save(challenge)
Exemple #20
0
    def _run_single_team_council(self, team: Team,
                                 losing_players: List[Player],
                                 gamedb: Database, engine: Engine):
        self._wait_for_tribal_council_start_time()

        # announce winner and tribal council for losing teams
        gamedb.clear_votes()

        winning_players = [
            player for player in gamedb.list_players(from_team=team)
            if player not in losing_players
        ]
        if len(winning_players) > 0:
            winning_player = winning_players[0]
        else:
            engine.stop()
            raise GameError(
                "Unable to determine a winning player for the challenge. Have any entries been submitted?"
            )

        engine.add_event(
            events.NotifySingleTeamCouncilEvent(game_id=self._game_id,
                                                game_options=self._options,
                                                winning_player=winning_player,
                                                losing_players=losing_players))
        self._wait_for_tribal_council_end_time()

        # count votes
        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))
Exemple #21
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     game_hashtag = gamedb.game_from_id(id=self.game_id).hashtag
     return [
         SMSEventMessage(
             content=messages.
             NOTIFY_WINNER_ANNOUNCEMENT_EVENT_WINNER_MSG_FMT.format(
                 header=messages.game_sms_header(gamedb=gamedb),
                 game=game_hashtag),
             recipient_phone_numbers=[self.winner.phone_number]),
         SMSEventMessage(
             content=messages.
             NOTIFY_WINNER_ANNOUNCEMENT_EVENT_GENERAL_MSG_FMT.format(
                 header=messages.game_sms_header(gamedb=gamedb),
                 player=messages.format_tiktok_username(self.winner.tiktok),
                 game=game_hashtag),
             recipient_phone_numbers=[
                 p.phone_number for p in gamedb.stream_players(
                     active_player_predicate_value=False)
             ])
     ]
Exemple #22
0
    def _play_single_team(self, team: Team, gamedb: Database, engine: Engine) -> List[Player]:
        while team.size > self._options.target_finalist_count:
            challenge = self._get_challenge(gamedb=gamedb)
            self._run_challenge(challenge=challenge,
                                gamedb=gamedb, engine=engine)
            losing_players = self._score_entries_top_k_players(
                team=team, challenge=challenge, gamedb=gamedb, engine=engine)
            self._run_single_team_council(
                team=team, losing_players=losing_players, gamedb=gamedb, engine=engine)

        return gamedb.list_players(from_team=team)
Exemple #23
0
    def _run_single_tribe_council(self, winning_teams: List[Team],
                                  losing_teams: List[Team], gamedb: Database,
                                  engine: Engine):
        self._wait_for_tribal_council_start_time()
        # keep top K teams

        # announce winner and tribal council for losing teams
        gamedb.clear_votes()
        engine.add_event(
            events.NotifySingleTribeCouncilEvent(game_id=self._game_id,
                                                 game_options=self._options,
                                                 winning_teams=winning_teams,
                                                 losing_teams=losing_teams))
        self._wait_for_tribal_council_end_time()

        # count votes
        for team in losing_teams:
            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))
            else:
                log_message(message="For some reason no one got voted out...",
                            game_id=self._game_id)
                log_message(message="Players = {}.".format(
                    pprint.pformat(gamedb.list_players(from_team=team))),
                            game_id=self._game_id)

        # notify all players of what happened at tribal council
        engine.add_event(
            events.NotifyTribalCouncilCompletionEvent(
                game_id=self._game_id, game_options=self._options))
Exemple #24
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))
Exemple #25
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     return [
         SMSEventMessage(
             content=messages.NOTIFY_IMMUNITY_AWARDED_EVENT_MSG_FMT.format(
                 header=messages.VIR_US_SMS_HEADER,
                 date=self.game_options.game_schedule.tomorrow_localized_string,
                 time=self.game_options.game_schedule.localized_time_string(
                     self.game_options.game_schedule.daily_challenge_start_time
                 )
             ),
             recipient_phone_numbers=[
                 p.phone_number for p in gamedb.list_players(from_team=self.team)]
         )
     ]
Exemple #26
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     return [
         SMSEventMessage(
             content=messages.NOTIFY_TRIBAL_COUNCIL_COMPLETION_EVENT_MSG_FMT.format(
                 header=messages.VIR_US_SMS_HEADER,
                 date=self.game_options.game_schedule.tomorrow_localized_string,
                 time=self.game_options.game_schedule.localized_time_string(
                     self.game_options.game_schedule.daily_challenge_start_time
                 )
             ),
             recipient_phone_numbers=[p.phone_number for p in gamedb.stream_players(
                 active_player_predicate_value=True)]
         )
     ]
Exemple #27
0
 def _get_next_challenge(self, gamedb: Database) -> Challenge:
     available_challenge_count = 0
     while available_challenge_count == 0 and not self._stop_event.is_set():
         log_message("Waiting for next challenge to become available.")
         time.sleep(self._options.game_wait_sleep_interval_sec)
         available_challenges = gamedb.list_challenges(
             challenge_completed_predicate_value=False)
         available_challenge_count = len(available_challenges)
     challenge = available_challenges[0]
     # return serializable challenge since this gets placed on the event queue.
     return Challenge(id=challenge.id,
                      name=challenge.name,
                      message=challenge.message,
                      complete=challenge.complete)
Exemple #28
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     options_map = messages.players_as_formatted_options_map(
         players=self.finalists)
     # TODO(brandon): this is slow and expensive, but it should work.
     for player in gamedb.stream_players():
         gamedb.ballot(player_id=player.id,
                       challenge_id=None,
                       options=options_map.options,
                       is_for_win=True)
     return [
         SMSEventMessage(
             content=messages.NOTIFY_FINAL_TRIBAL_COUNCIL_EVENT_MSG_FMT.
             format(
                 header=messages.game_sms_header(gamedb=gamedb),
                 players=len(self.finalists),
                 game=gamedb.game_from_id(id=self.game_id).hashtag,
                 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 gamedb.stream_players()
             ])
     ]
Exemple #29
0
 def messages(self, gamedb: Database) -> List[SMSEventMessage]:
     team_players = gamedb.list_players(from_team=self.team)
     return [
         SMSEventMessage(
             content=messages.NOTIFY_TEAM_REASSIGNMENT_EVENT_MSG_FMT.format(
                 header=messages.game_sms_header(gamedb=gamedb),
                 team=messages.players_as_formatted_list(
                     players=team_players),
                 date=self.game_options.game_schedule.
                 tomorrow_localized_string,
                 time=self.game_options.game_schedule.localized_time_string(
                     self.game_options.game_schedule.
                     daily_challenge_start_time)),
             recipient_phone_numbers=[self.player.phone_number])
     ]
Exemple #30
0
    def messages(self, gamedb: Database) -> List[SMSEventMessage]:
        player_messages = []
        count_players = gamedb.count_players(
            from_tribe=gamedb.tribe_from_id(self.winning_player.tribe_id))
        for player in self.losing_players:
            player_messages.append(
                SMSEventMessage(
                    content=messages.NOTIFY_SINGLE_TEAM_COUNCIL_EVENT_LOSING_MSG_FMT.format(
                        header=messages.VIR_US_SMS_HEADER,
                        winner=messages.format_tiktok_username(
                            self.winning_player.tiktok),
                        players=count_players,
                        time=self.game_options.game_schedule.localized_time_string(
                            self.game_options.game_schedule.daily_challenge_end_time),
                        options=messages.players_as_formatted_options_list(
                            players=self.losing_players, exclude_player=player)
                    ),
                    recipient_phone_numbers=player.phone_number
                )
            )

        player_messages.append(
            SMSEventMessage(
                content=messages.NOTIFY_SINGLE_TEAM_COUNCIL_EVENT_WINNING_MSG_FMT.format(
                    header=messages.VIR_US_SMS_HEADER,
                    players=count_players,
                    time=self.game_options.game_schedule.localized_time_string(
                        self.game_options.game_schedule.daily_challenge_end_time),
                    options=messages.players_as_formatted_options_list(
                        players=self.losing_players)
                ),
                recipient_phone_numbers=self.winning_player.phone_number
            )
        )

        return player_messages