from collections import defaultdict as dd import itertools import progressbar import random def ResistanceGame(n_players): full_set = [("G", True), ("G", True), ("G", True), ("E", False), ("E", False), ("G", True), ("E", False), ("G", True), ("G", True), ("E", False)] return full_set[:n_players] def AvalonGame(n_players): full_set = [("Merlin", True), ("Percival", True), ("G", True), ("Morgana", False), ("ELance", False), ("GLance", True), ("Mordred", False), ("G", True), ("G", True), ("Oberon", False)] return full_set[:n_players] class DeceptionGame(object): def __init__(self, player_array, model_class): self.player_array = player_array self.model = model_class(self) self.all_permutations = list(set(itertools.permutations(player_array))) self.quick_permutations = list(set(itertools.permutations( [("", p[1]) for p in player_array]))) self.n_players = len(player_array) self.n_good = len([x for x in player_array if x[1] is True]) self.trace = None self.trace_score = 0.0 self.observations = [] self.seen = [] self.tid = 0 self.lancelots_switch_at = [] def get_fail_req(self, rnd): if rnd is not 4: return 1 if self.n_players >= 7: return 2 return 1 def player_is_good(self, deal, player, round): if round is None: return deal[player][1] if self.player_role(deal, player) is not "GLance" and \ self.player_role(deal, player) is not "ELance": return deal[player][1] if len(self.lancelots_switch_at) == 0: return deal[player][1] if len(self.lancelots_switch_at) == 1: if round >= self.lancelots_switch_at[0]: return not deal[player][1] else: return deal[player][1] if len(self.lancelots_switch_at) == 2: if round >= self.lancelots_switch_at[0] and \ round < self.lancelots_switch_at[1]: return not deal[player][1] else: return deal[player][1] return deal[player][1] def player_role(self, deal, player): return deal[player][0] def add_known_alliance(self, player_id, is_good): transaction = [] def obs(deal): if self.player_is_good(deal, player_id, None) == is_good: return 1.0 else: return None transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "known_side", "player": player_id, "is good": is_good, "print_order": ["player", "is good"]}) self.tid += 1 def switch_lancelots(self, round): transaction = [] rnd = round - 1 def obs(deal): return 1.0 self.lancelots_switch_at.append(rnd) transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "switch", "round": rnd, "print_order": ["round"]}) self.tid += 1 def add_known_role(self, player_id, role_str): transaction = [] def obs(deal): if self.player_role(deal, player_id) == role_str: return 1.0 else: return None transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "known_role", "player": player_id, "role": role_str, "print_order": ["player", "role"]}) self.tid += 1 def player_sees_player_and_claims(self, p1, p2, claim, round): transaction = [] rnd = round - 1 def obs(deal): self.model.set_deal(deal) return self.model.player_sees_player_and_claims(p1, p2, claim, rnd) transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "lady", "p1": p1, "p2": p2, "is good": claim, "round": round, "print_order": ["p1", "p2", "round", "is good"]}) self.tid += 1 def do_mission(self, team, fails, must_fail, r): transaction = [] rnd = r - 1 def obs(deal): self.model.set_deal(deal) return self.model.mission(team, fails, must_fail, rnd) transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "mission", "team": team, "fails": fails, "round": r, "must fail": must_fail, "print_order": ["team", "fails", "must fail", "round"]}) self.tid += 1 def do_vote(self, team, votes, fail_req, r, v, proposer): transaction = [] rnd = r - 1 voten = v - 1 def obs(deal): self.model.set_deal(deal) return self.model.votes(team, votes, fail_req, rnd, voten, proposer) transaction.append(obs) self.observations.append(transaction) self.seen.append({"type": "vote", "team": team, "votes": votes, "round": r, "voten": v, "proposer": proposer, "fails required": fail_req, "print_order": ["team", "votes", "round"]}) self.tid += 1 def load_save(self, input_list): for statement in input_list: type = statement["type"] if type == "vote": self.do_vote(statement["team"], statement["votes"], statement["fails required"], statement["round"], statement["voten"], statement["proposer"]) if type == "switch": self.switch_lancelots(statement["round"]) elif type == "mission": self.do_mission(statement["team"], statement["fails"], statement["must fail"], statement["round"]) elif type == "lady": self.player_sees_player_and_claims(statement["p1"], statement["p2"], statement["is good"], statement.get("round", -1)) elif type == "known_side": self.add_known_alliance(statement["player"], statement["is good"]) elif type == "known_role": self.add_known_role(statement["player"], statement["role"]) def eval(self, length=10, quick=False): random.seed() if quick: deck = self.quick_permutations[:] else: deck = self.all_permutations[:] trace = {} progress = progressbar.ProgressBar( widgets=["Simulating games: ", progressbar.Bar(marker="*"), " ", progressbar.ETA()]) f_list = [] for obs in self.observations: for tid in obs: f_list.append(tid) total_score = 0.0 for deal in progress(deck): this_score = 1.0 for f in f_list: score = f(deal) if score is None: this_score = 0.0 break this_score = this_score * score total_score += this_score trace[deal] = this_score self.trace = trace self.trace_score = total_score def report(self): return self.get_player_data() def get_player_data(self): out = [] for i in range(self.n_players): out.append({}) out[i]["role"] = dd(float) out[i]["side"] = dd(float) top_k = [(0.0, None)] * 5 for deal, score in self.trace.items(): weighted_score = score / self.trace_score for i, card in enumerate(deal): role, side = card out[i]["role"][role] += weighted_score out[i]["side"][side] += weighted_score if weighted_score > top_k[-1][0]: top_k.append((weighted_score, deal)) top_k = sorted(top_k, reverse=True)[:-1] for i in range(self.n_players): out[i]["role"] = dict(out[i]["role"]) out[i]["side"] = dict(out[i]["side"]) return out, top_k def __str__(self): return "%d Player Game (%d constraints)" % (self.n_players, len(self.seen)) def disbelieve(self, i): self.observations = self.observations[:i] + self.observations[i + 1:] self.seen = self.seen[:i] + self.seen[i + 1:] self.trace = {}