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]
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.")
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
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))
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))
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
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
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
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
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))
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))
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
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
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)
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
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)] ) ]
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
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]
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)
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))
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) ]) ]
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)
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))
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))
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)] ) ]
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)] ) ]
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)
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() ]) ]
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]) ]
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