Created
December 14, 2017 20:37
-
-
Save paul-english/25724607c262925fb6fbcbecc724acc1 to your computer and use it in GitHub Desktop.
Avalon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import random | |
| import numpy as np | |
| from sacred import Experiment | |
| ex = Experiment() | |
| @ex.config | |
| def config(): | |
| n_sims = 1000 | |
| verbose = False | |
| n_players = 5 | |
| n_bad_guys = { | |
| 5: 2, | |
| 6: 2, | |
| 7: 3, | |
| 8: 3, | |
| 9: 3, | |
| 10: 4, | |
| }[n_players] | |
| # TODO this depends on the n_players | |
| # turn_team_size = [3, 3, 4, 4, 3] | |
| # turn_mission_required_fails = [1, 1, 1, 2, 1] | |
| turn_team_size = [2, 3, 2, 3, 3] | |
| turn_mission_required_fails = [1, 1, 1, 1, 1] | |
| class Player: | |
| def __init__(self, player_id, is_merlin, is_bad_guy, bad_guys): | |
| self.player_id = player_id | |
| self.is_merlin = is_merlin | |
| self.is_bad_guy = is_bad_guy | |
| self.bad_guys = bad_guys | |
| def choose_team(self, n_players, team_size): | |
| # TODO players use belief of team to make choice | |
| return np.random.choice(n_players, team_size, replace=False) | |
| def vote_on_team(self, proposed_team): | |
| # TODO if we think the mission will fail | |
| return np.random.randint(2) | |
| def vote_mission_passes(self, mission_team): | |
| # TODO player can make strategic choice on how | |
| # they vote, e.g. if two bad votes in the | |
| if self.is_bad_guy: | |
| return np.random.randint(2) | |
| else: | |
| return 1 | |
| def vote_on_team_callback(self, proposed_team, player_votes): | |
| # team votes should inform you on player beliefs | |
| pass | |
| def mission_callback(self, mission_votes, mission_team): | |
| # mission success should inform you on player votes | |
| pass | |
| # TODO encapsulate game state | |
| class Avalon: | |
| def __init__(self, n_players): | |
| n_players | |
| @ex.capture | |
| def game(n_players, n_bad_guys, turn_team_size, turn_mission_required_fails, verbose): | |
| special_roles = np.random.choice(n_players, n_bad_guys+1, replace=False) | |
| merlin = special_roles[0] | |
| bad_guys = special_roles[1:] | |
| if verbose: | |
| print('Merlin', merlin) | |
| print('Bad guys', bad_guys) | |
| players = [ | |
| Player(i, i == merlin, i in bad_guys, bad_guys if i in special_roles else None) | |
| for i in range(n_players) | |
| ] | |
| turn = 0 | |
| mission_turn = 0 | |
| n_successes = 0 | |
| n_fails = 0 | |
| n_passes = 0 | |
| while True: | |
| if verbose: print('Turn %s, mission %s (s %s, f %s, p %s)' % (turn, mission_turn, n_successes, n_fails, n_passes)) | |
| team_size = turn_team_size[mission_turn] | |
| turn_required_success_count = team_size - turn_mission_required_fails[mission_turn] | |
| players_turn = turn % n_players | |
| current_player = players[players_turn] | |
| proposed_team = current_player.choose_team(n_players, team_size) | |
| if verbose: print('Proposed team', proposed_team) | |
| votes = [ | |
| player.vote_on_team(proposed_team) | |
| for player in players | |
| ] | |
| if verbose: print('Team votes', votes) | |
| team_vote_succeeds = np.mean(votes) > 0.5 | |
| for player in players: | |
| player.vote_on_team_callback(proposed_team, votes) | |
| if not team_vote_succeeds: | |
| if verbose: print('- Pass') | |
| n_passes += 1 | |
| else: | |
| mission_votes = [players[i].vote_mission_passes(proposed_team) for i in proposed_team] | |
| if verbose: print('- Mission votes', mission_votes) | |
| mission_success_count = sum(mission_votes) | |
| for player in players: | |
| player.mission_callback(mission_success_count, proposed_team) | |
| if mission_success_count > turn_required_success_count: | |
| if verbose: print('- Success') | |
| n_successes += 1 | |
| else: | |
| if verbose: print('- Fail') | |
| n_fails += 1 | |
| mission_turn += 1 | |
| if n_successes >= 3: | |
| # TODO allow an assasination of merlin | |
| return 1 | |
| elif n_fails >= 3: | |
| return 0 | |
| elif n_passes >= 5: | |
| return 0 | |
| turn += 1 | |
| @ex.automain | |
| def main(n_sims): | |
| games = [game() for i in range(n_sims)] | |
| avg_wins = np.mean(games) | |
| return avg_wins |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment