def process_game(root, game): try: hidden_state = reconstruct_hidden_state(game) if len(hidden_state) >= 7: return print game['id'] possible = possible_hidden_states(set(hidden_state), num_players=len(hidden_state)) perspectives = [ perspective_from_hidden_states( starting_hidden_states(player, hidden_state, possible)) for player, _ in enumerate(hidden_state) ] tree_roots = [ root.setdefault((player, perspective), { 'move_counts': {}, 'transitions': {} }) for player, perspective in enumerate(perspectives) ] state = AvalonState.start_state(len(hidden_state)) for round_ in game['log']: tree_roots, state = handle_round(tree_roots, state, hidden_state, round_) except AssertionError: print game['id'], 'is bad'
def run_large_tournament(bots_classes, roles, games_per_matching=50): print "Running {}".format(' '.join(map(lambda c: c.__name__, bots_classes))) start_state = AvalonState.start_state(len(roles)) result = [] all_hidden_states = possible_hidden_states(set(roles), num_players=len(roles)) seen_hidden_states = set([]) for hidden_state in itertools.permutations(roles): if hidden_state in seen_hidden_states: continue seen_hidden_states.add(hidden_state) beliefs = [ starting_hidden_states(player, hidden_state, all_hidden_states) for player in range(len(hidden_state)) ] seen_bot_orders = set([]) for bot_order in itertools.permutations(bots_classes): bot_order_str = tuple([bot_cls.__name__ for bot_cls in bot_order]) if bot_order_str in seen_bot_orders: continue seen_bot_orders.add(bot_order_str) for _ in range(games_per_matching): bots = [ bot_cls.create_and_reset(start_state, player, role, beliefs[player]) for player, ( bot_cls, role) in enumerate(zip(bot_order, hidden_state)) ] values, game_end = run_game(start_state, hidden_state, bots) game_stat = { 'winner': game_end[0], 'win_type': game_end[1], } for player, (bot_cls, role) in enumerate(zip(bot_order, hidden_state)): game_stat['bot_{}'.format(player)] = bot_cls.__name__ game_stat['bot_{}_role'.format(player)] = role game_stat['bot_{}_payoff'.format(player)] = values[player] result.append(game_stat) df = pd.DataFrame(result, columns=sorted(result[0].keys())) df['winner'] = df['winner'].astype('category') df['win_type'] = df['win_type'].astype('category') for player in range(len(roles)): df['bot_{}'.format(player)] = df['bot_{}'.format(player)].astype( 'category') df['bot_{}_role'.format(player)] = df['bot_{}_role'.format( player)].astype('category') return df
def run_learning_tournament(bot_classes, winrate_track=None, winrate_window=10000000): bots = [(bot_class(), bot_class.__name__, num == winrate_track) for num, bot_class in enumerate(bot_classes)] hidden_state = ['merlin', 'servant', 'servant', 'assassin', 'minion'] all_hidden_states = possible_hidden_states(set(hidden_state), num_players=len(hidden_state)) beliefs_for_hidden_state = {} start_state = AvalonState.start_state(len(hidden_state)) wins = [] game_num = 0 while True: game_num += 1 random.shuffle(hidden_state) random.shuffle(bots) bot_ids = [bot_name for _, bot_name, _ in bots] if tuple(hidden_state) not in beliefs_for_hidden_state: beliefs_for_hidden_state[tuple(hidden_state)] = [ starting_hidden_states(player, tuple(hidden_state), all_hidden_states) for player in range(len(hidden_state)) ] beliefs = beliefs_for_hidden_state[tuple(hidden_state)] track_num = None bot_objs = [] for i, (bot, bot_name, track) in enumerate(bots): if track: track_num = i bot.reset(start_state, i, hidden_state[i], beliefs[i]) bot.set_bot_ids(bot_ids) bot_objs.append(bot) results, _ = run_game(start_state, tuple(hidden_state), bot_objs) for i, (bot, bot_name, track) in enumerate(bots): bot.show_roles(hidden_state, bot_ids) if track_num is not None: wins.append(int(results[track_num] > 0)) if len(wins) > winrate_window: wins.pop(0) if game_num % 10 == 0 and winrate_track is not None: print "Winrate: {}%".format(100 * float(sum(wins)) / len(wins))
def read_data_load_counts(): global counts if counts is not None: return counts if counts is None and os.path.isfile(STRATEGY_DATAFILE): with open(STRATEGY_DATAFILE, 'r') as f: counts = pickle.load(f) return counts print "Creating counts data..." counts = { 'propose': defaultdict(zeros_10), 'vote': defaultdict(zeros_2), 'run': defaultdict(zeros_2), 'merlin': defaultdict(zeros_5) } human_games = load_human_json() for game in human_games: hidden_state = reconstruct_hidden_state(game) if len(hidden_state) != 5 or frozenset(hidden_state) != frozenset( ['merlin', 'assassin', 'minion', 'servant']): continue print game['id'] start_state = AvalonState.start_state(len(hidden_state)) fails = [] perspectives = [ get_python_perspective(hidden_state, player) for player in range(len(hidden_state)) ] for state, moves in game_state_generator(start_state, game, hidden_state): moving_players = state.moving_players() for player, move in zip(moving_players, moves): legal_actions = state.legal_actions(player, hidden_state) perspective_bucket = get_hidden_states_bucket( perspectives[player], fails) index = legal_actions.index(move) counts[state.status][(state.as_key(), perspectives[player], perspective_bucket)][index] += 1 if state.status == 'run' and any([move.fail for move in moves]): fails.append((state.proposal, len([move for move in moves if move.fail]))) print "Writing counts data..." with open(STRATEGY_DATAFILE, 'w') as f: pickle.dump(counts, f) return counts
def check_config(config): role_counts = defaultdict(lambda: 0) start_state = AvalonState.start_state(len(config)) for bot in config: # Count roles role_counts[bot['role']] += 1 assert 'merlin' in role_counts assert 'assassin' in role_counts for role, count in role_counts.items(): assert role == 'servant' or role == 'minion' or count == 1 assert role in GOOD_ROLES or role in EVIL_ROLES
def determine_reachable(base_bot, roles, num_players): hidden_state_to_index = { hs: i for i, hs in enumerate(possible_hidden_states(roles, num_players)) } possible = possible_hidden_states(roles, num_players) start_state = AvalonState.start_state(num_players) cache = {} for hidden_state in hidden_state_to_index: for player in range(num_players): starting_belief = get_starting_belief(hidden_state, player, hidden_state_to_index) determine_reachable_from_belief_state(cache, start_state, starting_belief, player, hidden_state_to_index) print len(cache)
def run_large_tournament_parallel(pool, bots_classes, roles, games_per_matching=50): print "Running {}".format(' '.join(map(lambda c: c.__name__, bots_classes))) start_state = AvalonState.start_state(len(roles)) async_results = [] all_hidden_states = possible_hidden_states(set(roles), num_players=len(roles)) seen_hidden_states = set([]) for hidden_state in itertools.permutations(roles): if hidden_state in seen_hidden_states: continue seen_hidden_states.add(hidden_state) beliefs = [ starting_hidden_states(player, hidden_state, all_hidden_states) for player in range(len(hidden_state)) ] seen_bot_orders = set([]) for bot_order in itertools.permutations(bots_classes): bot_order_str = tuple([bot_cls.__name__ for bot_cls in bot_order]) if bot_order_str in seen_bot_orders: continue seen_bot_orders.add(bot_order_str) for _ in range(games_per_matching): async_result = pool.apply_async( large_tournament_parallel_helper, (bot_order, hidden_state, beliefs, start_state)) async_results.append(async_result) result = [async_result.get() for async_result in async_results] df = pd.DataFrame(result, columns=sorted(result[0].keys())) df['winner'] = df['winner'].astype('category') df['win_type'] = df['win_type'].astype('category') for player in range(len(roles)): df['bot_{}'.format(player)] = df['bot_{}'.format(player)].astype( 'category') df['bot_{}_role'.format(player)] = df['bot_{}_role'.format( player)].astype('category') return df
def run_single_threaded_tournament(config, num_games=1000, granularity=100): tournament_statistics = { 'bots': [{ 'bot': bot['bot'].__name__, 'role': bot['role'], 'wins': 0, 'total': 0, 'win_percent': 0, 'payoff': 0.0 } for bot in config], 'end_types': {} } hidden_state = tuple([bot['role'] for bot in config]) all_hidden_states = possible_hidden_states(set(hidden_state), num_players=len(config)) beliefs = [ starting_hidden_states(player, hidden_state, all_hidden_states) for player in range(len(config)) ] start_state = AvalonState.start_state(len(hidden_state)) bots = [bot['bot']() for bot in config] # pool = multiprocessing.Pool() results = [] for i in range(num_games): if i % granularity == 0: print i for player, (bot, c) in enumerate(zip(bots, config)): bot.reset(start_state, player, c['role'], beliefs[player]) payoffs, end_type = run_game(start_state, hidden_state, bots) tournament_statistics['end_types'][ end_type] = 1 + tournament_statistics['end_types'].get( end_type, 0) for b, payoff in zip(tournament_statistics['bots'], payoffs): b['wins'] += 1 if payoff > 0.0 else 0 b['payoff'] += payoff b['total'] += 1 for b in tournament_statistics['bots']: b['win_percent'] = float(b['wins']) / float(b['total']) return tournament_statistics
def process_game(game, bot_class, stats, verbose=True, num_players=None, max_num_players=7, min_game_id=0, max_game_id=50000, roles=None): try: hidden_state = reconstruct_hidden_state(game) if num_players is not None: if len(hidden_state) != num_players: return else: if len(hidden_state) >= max_num_players: return if game['id'] >= max_game_id or game['id'] < min_game_id: return if roles is not None: if not all(role in roles for role in hidden_state): return if verbose: print game['id'] print hidden_state possible = possible_hidden_states(set(hidden_state), num_players=len(hidden_state)) perspectives = [ starting_hidden_states(player, hidden_state, possible) for player, _ in enumerate(hidden_state) ] state = AvalonState.start_state(len(hidden_state)) bots = [ bot_class.create_and_reset(state, player, role, perspectives[player]) for player, role in enumerate(hidden_state) ] for round_ in game['log']: state = handle_round(game, state, hidden_state, bots, round_, stats) except AssertionError: if verbose: print game['id'], 'is bad'
def get_bot_merlin_prediction(bot_class, game): hidden_state = reconstruct_hidden_state(game) state = AvalonState.start_state(len(hidden_state)) possible = possible_hidden_states(set(hidden_state), num_players=len(hidden_state)) perspectives = [ starting_hidden_states(player, hidden_state, possible) for player, _ in enumerate(hidden_state) ] assassin_player = hidden_state.index('assassin') assassin_perspective = perspectives[assassin_player] assassin_bot = bot_class.create_and_reset(state, assassin_player, 'assassin', assassin_perspective) for round_ in game['log']: state = handle_round(game, state, hidden_state, assassin_bot, assassin_player, round_) final_round = game['log'][-1] assert 'findMerlin' in final_round find_merlin = round_['findMerlin'] assert find_merlin['assassin'] == assassin_player legal_moves = state.legal_actions(assassin_player, hidden_state) move_probs = assassin_bot.get_move_probabilities(state, legal_moves) return { 'human_guess': find_merlin['merlin_guess'], 'bot_human_prob': move_probs[find_merlin['merlin_guess']], 'correct_guess': hidden_state.index('merlin'), 'bot_correct_prob': move_probs[hidden_state.index('merlin')], 'top_pick': np.argmax(move_probs), 'top_pick_prob': np.max(move_probs), 'game': game['id'], 'merlin': game['players'][hidden_state.index('merlin')]['player_id'], 'assassin': game['players'][hidden_state.index('assassin')]['player_id'] }
def predict_evil_using_voting_on_game(game, vote_same_if_same_prob, vote_same_if_diff_prob): try: dataframe_data = [] hidden_state = reconstruct_hidden_state(game) assert len(hidden_state) <= 7 team = tuple([role in EVIL_ROLES for role in hidden_state]) avalon_start = AvalonState.start_state(len(hidden_state)) game_generator = human_game_state_generator(avalon_start, game, hidden_state) same_team_prob = np.ones((len(hidden_state), len(hidden_state))) / 2 vote_count = 0 mission_num = 0 who_failed = [] for old_state, new_state, observation in game_generator: if old_state.status == 'run': mission_num += 1 if old_state.fails > new_state.fails: who_failed.append((old_state.proposal, observation)) if old_state.status == 'vote': vote_count += 1 update_pairings(observation, vote_same_if_same_prob, vote_same_if_diff_prob, same_team_prob) (nll_picked, pick), nll_correct = most_likely_team(same_team_prob, len(hidden_state), who_failed, team) dataframe_data.append({ 'game': game['id'], 'num_players': len(hidden_state), 'vsis_prob': vote_same_if_same_prob, 'vsid_prob': vote_same_if_diff_prob, 'mission': mission_num, 'vote_count': vote_count, 'nll_correct': nll_correct, 'nll_picked': nll_picked, 'correct': pick == team, }) sys.stdout.flush() return dataframe_data except ValueError: return [] except AssertionError: return []
def predict_evil_over_human_game(game, as_bot, tremble): try: print game['id'] sys.stdout.flush() hidden_state = reconstruct_hidden_state(game) assert frozenset(hidden_state) == frozenset(['merlin', 'assassin', 'minion', 'servant']) avalon_start = AvalonState.start_state(len(hidden_state)) game_generator = human_game_state_generator(avalon_start, game, hidden_state) nll, particles = get_hidden_state_nll_for_game(avalon_start, game_generator, hidden_state, as_bot, tremble) data = { 'bot': as_bot.__name__, 'game': game['id'], 'num_players': len(hidden_state), 'trembling_hand_prob': tremble, 'nll': nll } for p in range(len(hidden_state)): data['player_{}'.format(p)] = game['players'][p]['player_id'] data['role_{}'.format(p)] = hidden_state[p] return data, particles except AssertionError: return None, None
def test_calculate(): roles = ['merlin', 'assassin', 'minion', 'servant'] # bot_classes = [ RandomBot, RandomBot, ObserveBot, RandomBot, RandomBot ] bot_classes = [ HumanLikeBot ] * 5 # observation_history = [ # # Round 1 # ('propose', (1, 2)), # ('vote', (True, True, False, False, False)), # ('propose', (0, 2)), # ('vote', (False, True, True, True, False)), # ('run', 1), # # Round 2 # ('propose', (2, 3, 4)), # ('vote', (False, True, True, True, False)), # ('run', 0), # # Round 3 # ('propose', (2, 3)), # ('vote', (False, True, True, True, False)), # ('run', 0), # # Round 4 # ('propose', (2, 3, 4)), # ('vote', (False, True, True, True, False)), # ('run', 1), # # Round 5 # ('propose', (2, 3, 4)), # ('vote', (False, True, True, False, False)), # ('propose', (1, 2, 3)), # ('vote', (True, True, False, False, False)), # ('propose', (0, 1, 3)), # ('vote', (True, False, False, False, True)), # ] # observation_history = [ # # Round 1 # ('propose', (0, 4)), # ('vote', (True, True, True, True, False)), # ('run', 1), # # Round 2 # ('propose', (1, 3, 4)), # ('vote', (True, True, False, True, False)), # ('run', 1), # # Round 3 # ('propose', (1, 2)), # ('vote', (True, True, False, False, False)), # ('propose', (2, 3)), # ('vote', (False, False, True, True, True)), # ('run', 0), # # Round 4 # ('propose', (2, 3, 4)), # ('vote', (False, False, True, True, True)), # ('run', 0), # # # Round 5 # ('propose', (0, 1, 3)), # ('vote', (True, True, False, False, False)), # ('propose', (1, 3, 4)), # ('vote', (True, True, False, False, False)), # ('propose', (0, 2, 3)), # ('vote', (True, True, False, False, False)), # ('propose', (2, 3, 4)), # ('vote', (False, False, False, True, True)), # ('propose', (2, 3, 4)), # ('vote', (True, True, True, False, True)) # ] observation_history = [ # Round 1 ('propose', (0, 4)), ('vote', (True, True, True, True, False)), ('run', 1), # Round 2 ('propose', (1, 3, 4)), ('vote', (True, True, False, True, False)), ('run', 1), # Round 3 ('propose', (1, 2)), ('vote', (True, True, False, False, False)), ('propose', (2, 3)), ('vote', (False, False, True, False, True)), ('propose', (2, 4)), ('vote', (False, False, True, True, True)), ('run', 0), # Round 4 ('propose', (2, 3, 4)), ('vote', (False, False, True, True, True)), ('run', 0), # # Round 5 # person 1 ('propose', (0, 1, 3)), ('vote', (True, True, False, False, False)), # person 2 ('propose', (1, 3, 4)), ('vote', (True, True, False, False, False)), # person 3 ('propose', (0, 2, 3)), ('vote', (True, True, False, False, False)), ] hidden_states, lls = calculate_subgame_ll(roles, 5, bot_classes, observation_history, tremble=1e-8) lls -= np.max(lls) probs = np.exp(lls) probs /= np.sum(probs) multiple = np.max(probs) / 50 for hidden_state, prob in zip(hidden_states, probs): print "{: >10} {: >10} {: >10} {: >10} {: >10}: {prob}".format(*hidden_state, prob='#' * int(prob / multiple)) print "Solving subgame" state = AvalonState(proposer=3, propose_count=3, succeeds=2, fails=2, status='propose', proposal=None, game_end=None, num_players=5) solve_subgame(hidden_states, lls, state, iterations=100000)
def calculate_observation_ll(hidden_state, bot_classes, observation_history, tremble=0.0): all_hidden_states = possible_hidden_states(set(hidden_state), num_players=len(hidden_state)) beliefs = [ starting_hidden_states(player, hidden_state, all_hidden_states) for player in range(len(hidden_state)) ] state = AvalonState.start_state(len(hidden_state)) bots = [ bot() for bot in bot_classes ] for i, bot in enumerate(bots): bot.reset(state, i, hidden_state[i], beliefs[i]) log_likelihood = 0.0 for obs_type, obs in observation_history: assert obs_type == state.status, "Incorrect matchup {} != {}".format(obs_type, state.status) moving_players = state.moving_players() moves = [] if obs_type == 'propose': player = moving_players[0] legal_actions = state.legal_actions(player, hidden_state) move = ProposeAction(proposal=obs) index = legal_actions.index(move) moves.append(move) move_probs = bots[player].get_move_probabilities(state, legal_actions) move_probs = (1.0 - tremble) * move_probs + tremble * (np.ones(len(legal_actions))/len(legal_actions)) log_likelihood += np.log(move_probs[index]) elif obs_type == 'vote': for p, vote_up in zip(moving_players, obs): legal_actions = state.legal_actions(p, hidden_state) move = VoteAction(up=vote_up) index = legal_actions.index(move) moves.append(move) move_probs = bots[p].get_move_probabilities(state, legal_actions) move_probs = (1.0 - tremble) * move_probs + tremble * (np.ones(len(legal_actions))/len(legal_actions)) log_likelihood += np.log(move_probs[index]) elif obs_type == 'run': bad_guys_on_mission = [p for p in state.proposal if hidden_state[p] in EVIL_ROLES ] if len(bad_guys_on_mission) < obs: # Impossible - fewer bad than failed return np.log(0.0) player_fail_probability = {} for bad in bad_guys_on_mission: legal_actions = state.legal_actions(bad, hidden_state) move = MissionAction(fail=True) index = legal_actions.index(move) move_probs = bots[bad].get_move_probabilities(state, legal_actions) move_probs = (1.0 - tremble) * move_probs + tremble * (np.ones(len(legal_actions))/len(legal_actions)) player_fail_probability[bad] = move_probs[index] failure_prob = 0.0 moves = [ MissionAction(fail=False) ] * len(state.proposal) for bad_failers in itertools.combinations(bad_guys_on_mission, r=obs): specific_fail_prob = 1.0 for bad in bad_guys_on_mission: moves[state.proposal.index(bad)] = MissionAction(fail=True) if bad in bad_failers else MissionAction(fail=False) specific_fail_prob *= player_fail_probability[bad] if bad in bad_failers else (1.0 - player_fail_probability[bad]) failure_prob += specific_fail_prob log_likelihood += np.log(failure_prob) new_state, _, observation = state.transition(moves, hidden_state) for player, bot in enumerate(bots): if player in moving_players: move = moves[moving_players.index(player)] else: move = None bot.handle_transition(state, new_state, observation, move=move) state = new_state return log_likelihood
def run_game_and_create_bots(hidden_state, beliefs, config): start_state = AvalonState.start_state(len(hidden_state)) bots = [bot['bot']() for bot in config] for player, (bot, config) in enumerate(zip(bots, config)): bot.reset(start_state, player, config['role'], beliefs[player]) return run_game(start_state, hidden_state, bots)
def run_and_print_game(config): bot_counts = {} bot_names = [] for bot in config: base_name = bot['bot'].__name__ bot_counts[base_name] = bot_counts.get(base_name, 0) + 1 bot_names.append("{}_{}".format(base_name, bot_counts[base_name])) print " Role | Bot | Evil " print "----------------------------------------------" for name, bconf in zip(bot_names, config): print "{: >11} | {: >24} | {: >4}".format( bconf['role'], name, 'Yes' if bconf['role'] in EVIL_ROLES else '') hidden_state = tuple([bot['role'] for bot in config]) all_hidden_states = possible_hidden_states(set(hidden_state), num_players=len(config)) beliefs = [ starting_hidden_states(player, hidden_state, all_hidden_states) for player in range(len(config)) ] state = AvalonState.start_state(len(hidden_state)) bots = [bot['bot']() for bot in config] for i, bot in enumerate(bots): bot.reset(state, i, hidden_state[i], beliefs[i]) print "=============== Round 1 ================" while not state.is_terminal(): moving_players = state.moving_players() moves = [ bots[player].get_action(state, state.legal_actions(player, hidden_state)) for player in moving_players ] if state.status == 'propose': player = moving_players[0] legal_actions = state.legal_actions(player, hidden_state) move_probs = bots[player].get_move_probabilities( state, legal_actions) move_prob = move_probs[legal_actions.index(moves[0])] print "Proposal #{}. {} proposes ({:0.2f}):".format( state.propose_count + 1, bot_names[moving_players[0]], move_prob) for player in moves[0].proposal: print " - {}".format(bot_names[player]) elif state.status == 'vote': for player, move in zip(moving_players, moves): legal_actions = state.legal_actions(player, hidden_state) move_probs = bots[player].get_move_probabilities( state, legal_actions) move_prob = move_probs[legal_actions.index(move)] print "{: >24} votes {: <4} ({:0.2f})".format( bot_names[player], 'UP' if move.up else 'DOWN', move_prob) elif state.status == 'run': print "--- Mission results ---" for player, move in zip(moving_players, moves): legal_actions = state.legal_actions(player, hidden_state) move_probs = bots[player].get_move_probabilities( state, legal_actions) move_prob = move_probs[legal_actions.index(move)] print "{: >24}: {} ({:0.2f})".format( bot_names[player], 'FAIL' if move.fail else 'SUCCEED', move_prob) elif state.status == 'merlin': print "===== Final chance: pick merlin! =====" assassin = hidden_state.index('assassin') legal_actions = state.legal_actions(assassin, hidden_state) move_probs = bots[assassin].get_move_probabilities( state, legal_actions) move_prob = move_probs[legal_actions.index(moves[0])] assassin_pick = moves[assassin].merlin print '{} picked {} - {}! ({:0.2f})'.format( bot_names[assassin], bot_names[assassin_pick], 'CORRECT' if assassin_pick == hidden_state.index('merlin') else 'WRONG', move_prob) new_state, _, observation = state.transition(moves, hidden_state) for player, bot in enumerate(bots): if player in moving_players: move = moves[moving_players.index(player)] else: move = None bot.handle_transition(state, new_state, observation, move=move) if state.status == 'run' and new_state.status == 'propose': print "=============== Round {} ================".format( new_state.succeeds + new_state.fails + 1) state = new_state print state.game_end
def replay_game(game): roles = game['game_info']['roles'] roles = roles[:1] + roles[1:][::-1] hidden_state = [PRO_TO_HS[r] for r in roles] players = game['session_info']['players'] proposer = [ 'VHleader' in game['game_info']['voteHistory'][player][0][0] for player in players ].index(True) state = AvalonState( proposer=proposer, propose_count=0, succeeds=0, fails=0, status='propose', proposal=None, game_end=None, num_players=5 ) yield None, state, hidden_state while not state.is_terminal(): rnd = state.succeeds + state.fails if state.status != 'merlin': proposer = [ 'VHleader' in game['game_info']['voteHistory'][player][rnd][state.propose_count] for player in players ].index(True) assert proposer == state.proposer, "{} != {}".format(proposer, state.proposer) if state.status == 'propose': proposal = tuple(sorted([ players.index(player) for player in players if 'VHpicked' in game['game_info']['voteHistory'][player][rnd][state.propose_count] ])) actions = [ProposeAction(proposal=proposal)] elif state.status == 'vote': actions = [ VoteAction(up=( 'VHapprove' in game['game_info']['voteHistory'][player][rnd][state.propose_count] )) for player in players ] elif state.status == 'run': observed_fails = game['game_info']['numFailsHistory'][rnd] actions = [] for player in state.moving_players(): if hidden_state[player] in set(['merlin', 'servant']): actions.append(MissionAction(fail=False)) elif observed_fails == 0: actions.append(MissionAction(fail=False)) else: actions.append(MissionAction(fail=True)) observed_fails -= 1 assert observed_fails == 0 elif state.status == 'merlin': shot_player = players.index(game['game_info']['publicData']['roles']['assassinShotUsername']) actions = [PickMerlinAction(merlin=shot_player) for _ in range(5)] assert len(actions) == len(state.moving_players()) new_state, _, obs = state.transition(actions, hidden_state) yield state, new_state, obs state = new_state