From e77323d8a15e550a7f7342dcf56168581d60a318 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Fri, 26 Jul 2013 23:01:48 -0400 Subject: [PATCH 1/5] pep8 --- tim.py | 208 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/tim.py b/tim.py index 61d1d76..e412f28 100644 --- a/tim.py +++ b/tim.py @@ -6,135 +6,135 @@ import pprint def ResistanceGame(n_players): - full_set = [("G1", True), ("G2", True), ("G3", True), ("E1", False), ("E2", False), - ("G4", True), ("E3", False), ("G5", True), ("G6", True), ("E4", False)] - return full_set[:n_players] + full_set = [("G1", True), ("G2", True), ("G3", True), + ("E1", False), ("E2", False), ("G4", True), + ("E3", False), ("G5", True), ("G6", True), ("E4", False)] + return full_set[:n_players] class DeceptionGame(object): - def __init__(self, player_array): - self.player_array = player_array - self.all_permutations = list(itertools.permutations(player_array)) - self.deck_var = mc.DiscreteUniform("deal", 0, len(self.all_permutations) - 1) - self.n_players = len(player_array) - self.model = None + def __init__(self, player_array): + self.player_array = player_array + self.all_permutations = list(itertools.permutations(player_array)) + self.deck_var = mc.DiscreteUniform("deal", 0, + len(self.all_permutations) - 1) + self.n_players = len(player_array) + self.model = None + self.player_alliance_vars = [] + self.player_role_vars = [] + self.role_ids = {} + self.role_alliance = {} + i = 0 - self.player_alliance_vars = [] - self.player_role_vars = [] - self.role_ids = {} - self.role_alliance = {} - i = 0 + for card in player_array: + role, good = card + self.role_ids[role] = i + self.role_alliance[i] = good + i += 1 - for card in player_array: - role, good = card - self.role_ids[role] = i - self.role_alliance[i] = good - i += 1 + def player_alliance(x, role): + return self.role_alliance[role] - def player_alliance(x, role): - return self.role_alliance[role] + def player_role(x, deck_var=self.deck_var): + role_str = self.all_permutations[deck_var][x][0] + return self.role_ids[role_str] - def player_role(x, deck_var = self.deck_var): - role_str = self.all_permutations[deck_var][x][0] - return self.role_ids[role_str] + for x in range(self.n_players): + role = mc.Deterministic(eval=functools.partial(player_role, x), + name="player_role_%d" % x, + parents={"deck_var": self.deck_var}, + doc="Who is player %d?" % x, + dtype=str, + trace=True, + plot=False) + self.player_role_vars.append(role) - for x in range(self.n_players): - role = mc.Deterministic(eval = functools.partial(player_role, x), - name = "player_role_%d" % x, - parents = { "deck_var" : self.deck_var }, - doc = "Who is player %d?" % x, - dtype = str, - trace = True, - plot = False) - self.player_role_vars.append(role) + alliance = mc.Deterministic(eval=functools.partial( + player_alliance, x), + name="player_alliance_%d" % x, + parents={"role": role}, + doc="Is player %d good?" % x, + dtype=bool, + trace=True, + plot=False) - alliance = mc.Deterministic(eval = functools.partial(player_alliance, x), - name = "player_alliance_%d" % x, - parents = { "role" : role}, - doc = "Is player %d good?" % x, - dtype = bool, - trace = True, - plot = False) + self.player_alliance_vars.append(alliance) - self.player_alliance_vars.append(alliance) + self.observations = [] + self.tid = 0 - self.observations = [] - self.tid = 0 + def add_known_alliance(self, player_id, is_good): + transaction = [] + obs = mc.Bernoulli("player_seen_tid%d" % self.tid, + self.player_alliance_vars[player_id], + value=is_good, + observed=True) + transaction.append(obs) + self.observations.append(transaction) + self.tid += 1 - def add_known_alliance(self, player_id, is_good): - transaction = [] - obs = mc.Bernoulli("player_seen_tid%d" % self.tid, - self.player_alliance_vars[player_id], - value = is_good, - observed = True) - transaction.append(obs) - self.observations.append(transaction) - self.tid += 1 + def add_known_role(self, player_id, role_str): + transaction = [] + obs = mc.Bernoulli("player_seen_role_tid%d" % self.tid, + self.player_role_vars[player_id], + value=self.role_ids[role_str], + observed=True) + transaction.append(obs) + self.observations.append(transaction) + self.tid += 1 - def add_known_role(self, player_id, role_str): - transaction = [] - obs = mc.Bernoulli("player_seen_role_tid%d" % self.tid, - self.player_role_vars[player_id], - value = self.role_ids[role_str], - observed = True) - transaction.append(obs) - self.observations.append(transaction) - self.tid += 1 + def eval(self, length=40000): + mcmc = mc.MCMC(self._build_model_list()) + mcmc.sample(length, 2000) + self.model = mcmc + def report(self): + if self.model is None: + self.eval() + out = [] + for i in range(self.n_players): + out.append(self.get_player_data(i)) + return out - def eval(self, length=40000): - mcmc = mc.MCMC(self._build_model_list()) - mcmc.sample(length, 2000) - self.model = mcmc + def get_player_data(self, i): + out = {} + role_key = "player_role_%d" % i + alliance_key = "player_alliance_%d" % i - def report(self): - if self.model is None: - self.eval() - out = [] - for i in range(self.n_players): - out.append(self.get_player_data(i)) - return out + temp_role = self._aggregate(list(self.model.trace(role_key))) + out["role"] = {} + for k, v in temp_role.iteritems(): + out["role"][self.player_array[int(k)][0]] = v + out["alliance"] = self._aggregate(list(self.model.trace(alliance_key))) + return out - def get_player_data(self, i): - out = {} - role_key = "player_role_%d" % i - alliance_key = "player_alliance_%d" % i + def _aggregate(self, l): + out = dd(float) + size = len(l) * 1.0 + for x in l: + out[x] += 1 / size + return dict(out) - temp_role = self._aggregate(list(self.model.trace(role_key))) - out["role"] = {} - for k, v in temp_role.iteritems(): - out["role"][self.player_array[int(k)][0]] = v - out["alliance"] = self._aggregate(list(self.model.trace(alliance_key))) - return out + def print_report(self): + pp = pprint.PrettyPrinter(indent=4) + pp.pprint(self.report()) - def _aggregate(self, l): - out = dd(float) - size = len(l) * 1.0 - for x in l: - out[x] += 1 / size - return dict(out) - - - def print_report(self): - pp = pprint.PrettyPrinter(indent = 4) - pp.pprint(self.report()) - - def _build_model_list(self): - out = [] - out.append(self.deck_var) - out.extend(self.player_alliance_vars[:]) - out.extend(self.player_role_vars[:]) - flattened = [item for transaction in self.observations for item in transaction] - out.extend(flattened[:]) - return list(set(out)) + def _build_model_list(self): + out = [] + out.append(self.deck_var) + out.extend(self.player_alliance_vars[:]) + out.extend(self.player_role_vars[:]) + flattened = [item for transaction in self.observations + for item in transaction] + out.extend(flattened[:]) + return list(set(out)) base_game = DeceptionGame(ResistanceGame(5)) -base_game.eval(10000) +base_game.eval(20000) base_game.add_known_role(0, "G1") base_game.add_known_alliance(1, False) base_game.eval() base_game.print_report() - From 9f02e5d123940a5c67dca8b7104655456077a8c7 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Sat, 27 Jul 2013 12:10:28 -0400 Subject: [PATCH 2/5] change alliance to side --- tim.py | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/tim.py b/tim.py index e412f28..2186879 100644 --- a/tim.py +++ b/tim.py @@ -12,6 +12,13 @@ def ResistanceGame(n_players): return full_set[:n_players] +class Player(object): + def __init__(self, id, card, game, side): + self.id_num = id + self.role_str, _ = card + self.game = game + + class DeceptionGame(object): def __init__(self, player_array): self.player_array = player_array @@ -21,20 +28,20 @@ class DeceptionGame(object): self.n_players = len(player_array) self.model = None - self.player_alliance_vars = [] + self.player_side_vars = [] self.player_role_vars = [] self.role_ids = {} - self.role_alliance = {} + self.role_side = {} i = 0 for card in player_array: role, good = card self.role_ids[role] = i - self.role_alliance[i] = good + self.role_side[i] = good i += 1 - def player_alliance(x, role): - return self.role_alliance[role] + def player_side(x, role): + return self.role_side[role] def player_role(x, deck_var=self.deck_var): role_str = self.all_permutations[deck_var][x][0] @@ -50,24 +57,26 @@ class DeceptionGame(object): plot=False) self.player_role_vars.append(role) - alliance = mc.Deterministic(eval=functools.partial( - player_alliance, x), - name="player_alliance_%d" % x, - parents={"role": role}, - doc="Is player %d good?" % x, - dtype=bool, - trace=True, - plot=False) + side = mc.Deterministic(eval=functools.partial( + player_side, x), + name="player_side_%d" % x, + parents={"role": role}, + doc="Is player %d good?" % x, + dtype=bool, + trace=True, + plot=False) - self.player_alliance_vars.append(alliance) + self.player_side_vars.append(side) + self.players.append(Player(x, + player_array[x])) self.observations = [] self.tid = 0 - def add_known_alliance(self, player_id, is_good): + def add_known_side(self, player_id, is_good): transaction = [] obs = mc.Bernoulli("player_seen_tid%d" % self.tid, - self.player_alliance_vars[player_id], + self.player_side_vars[player_id], value=is_good, observed=True) transaction.append(obs) @@ -100,13 +109,13 @@ class DeceptionGame(object): def get_player_data(self, i): out = {} role_key = "player_role_%d" % i - alliance_key = "player_alliance_%d" % i + side_key = "player_side_%d" % i temp_role = self._aggregate(list(self.model.trace(role_key))) out["role"] = {} for k, v in temp_role.iteritems(): out["role"][self.player_array[int(k)][0]] = v - out["alliance"] = self._aggregate(list(self.model.trace(alliance_key))) + out["side"] = self._aggregate(list(self.model.trace(side_key))) return out def _aggregate(self, l): @@ -123,7 +132,7 @@ class DeceptionGame(object): def _build_model_list(self): out = [] out.append(self.deck_var) - out.extend(self.player_alliance_vars[:]) + out.extend(self.player_side_vars[:]) out.extend(self.player_role_vars[:]) flattened = [item for transaction in self.observations for item in transaction] @@ -134,7 +143,7 @@ class DeceptionGame(object): base_game = DeceptionGame(ResistanceGame(5)) base_game.eval(20000) base_game.add_known_role(0, "G1") -base_game.add_known_alliance(1, False) +base_game.add_known_side(1, False) base_game.eval() base_game.print_report() From 4358836270df0a1663a82557f98635bd0c7c9667 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Sat, 27 Jul 2013 16:06:17 -0400 Subject: [PATCH 3/5] wip --- tim.py | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 204 insertions(+), 20 deletions(-) diff --git a/tim.py b/tim.py index 2186879..a33806c 100644 --- a/tim.py +++ b/tim.py @@ -3,6 +3,8 @@ import functools import itertools from collections import defaultdict as dd import pprint +import numpy +from numpy.random import random def ResistanceGame(n_players): @@ -12,11 +14,109 @@ def ResistanceGame(n_players): return full_set[:n_players] +def BuildNbyNBeliefMatrix(id, n_players): + all_vars = [] + matrix = [] + for player_id in range(n_players): + vec = mc.Uniform("%d_trust_%d" % (id, player_id), + 0.0, 1.0, + trace=False, + size=n_players) + matrix.append(vec) + all_vars.append(vec) + + # Enforce that there's only one role for each player. + for i, player_vec in enumerate(matrix): + det_var = sum(vec) + all_vars.append(det_var) + obs = mc.Bernoulli( + "player_%d_one_role_constraint_%d" % (id, i), + det_var, + value=1.0, + trace=False, + observed=True) + all_vars.append(obs) + + # Enforce that there's only one player per role. + for i in range(n_players): + det_var = sum([vec[i] for vec in matrix]) + all_vars.append(det_var) + obs = mc.Bernoulli( + "player_%d_one_player_constraint_%d" % (id, i), + det_var, + value=1.0, + trace=False, + observed=True) + all_vars.append(obs) + return matrix, all_vars + class Player(object): - def __init__(self, id, card, game, side): - self.id_num = id - self.role_str, _ = card + ''' Representing all that the player is and knows ''' + def __init__(self, id, game, side_var, role_var): + self.id = id self.game = game + self.side_var = side_var + self.role_var = role_var + self.all_vars = [] + self.build_belief_matrix() + + def side(self): + return self.side_var + + def role(self): + return self.role_var + + def build_belief_matrix(self): + self.matrix = [] + self.matrix, self.all_vars = \ + BuildNbyNBeliefMatrix(self.id, self.game.n_players) + + # Enforce that the player completely knows her own role. + def player_knows_self(self_role=self.role_var): + return self.matrix[self.id][self_role] + + knows = mc.Deterministic(eval=player_knows_self, + name="player_knows_self_%d" % self.id, + parents={"self_role": self.role_var}, + doc="Player knows self %d" % self.id, + dtype=float, + trace=True, + plot=False) + self.all_vars.append(knows) + obs = mc.Bernoulli( + "player_knows_self_constraint_%d" % self.id, + knows, + value=1.0, + observed=True) + self.all_vars.append(obs) + + # Convenience vars for trust + self.side_belief = [] + + def side_belief(player_id, role_vec): + out = 0.0 + for i, role in enumerate(role_vec): + if self.game.role_id_is_good(i): + out += role + out = out / len(role_vec) + return out + + for i in range(self.game.n_players): + is_good = mc.Deterministic(eval=functools.partial(side_belief, i), + name="player%d_trust_%d" % (self.id, i), + parents={"role_vec": self.matrix[i]}, + doc="Does player trust %d?" % i, + dtype=float, + trace=True, + plot=False) + self.side_belief.append(is_good) + self.all_vars.append(is_good) + + def get_good_belief_for(self, player_id): + return self.side_belief[player_id] + + def get_role_belief_for(self, player_id, role_id): + return self.matrix[player_id][role_id] class DeceptionGame(object): @@ -32,6 +132,7 @@ class DeceptionGame(object): self.player_role_vars = [] self.role_ids = {} self.role_side = {} + i = 0 for card in player_array: @@ -41,18 +142,22 @@ class DeceptionGame(object): i += 1 def player_side(x, role): - return self.role_side[role] + if self.role_side[role]: + return True + return False def player_role(x, deck_var=self.deck_var): role_str = self.all_permutations[deck_var][x][0] - return self.role_ids[role_str] + return int(self.role_ids[role_str]) + + self.players = [] for x in range(self.n_players): role = mc.Deterministic(eval=functools.partial(player_role, x), name="player_role_%d" % x, parents={"deck_var": self.deck_var}, doc="Who is player %d?" % x, - dtype=str, + dtype=int, trace=True, plot=False) self.player_role_vars.append(role) @@ -67,37 +172,107 @@ class DeceptionGame(object): plot=False) self.player_side_vars.append(side) - self.players.append(Player(x, - player_array[x])) + self.players.append(Player(x, self, side, role)) + + for x in range(50000): + self.deck_var.random() self.observations = [] self.tid = 0 + def get_role_id_for(self, name): + return self.role_ids[name] + + def role_id_is_good(self, role_id): + return self.role_side[role_id] + def add_known_side(self, player_id, is_good): transaction = [] - obs = mc.Bernoulli("player_seen_tid%d" % self.tid, - self.player_side_vars[player_id], - value=is_good, - observed=True) + + def player_is_good(side_var): + if side_var == is_good: + return 1.0 + return 0.0 + + det = mc.Deterministic( + eval=player_is_good, + name="player_seen_det_tid%d" % self.tid, + parents={"side_var": self.player_side_vars[player_id]}, + doc="Det TID%d" % self.tid, + dtype=float, + trace=False, + plot=False) + + transaction.append(det) + + obs = mc.Degenerate("player_seen_tid%d" % self.tid, + det, + value=1.0, + observed=True) transaction.append(obs) self.observations.append(transaction) self.tid += 1 def add_known_role(self, player_id, role_str): transaction = [] - obs = mc.Bernoulli("player_seen_role_tid%d" % self.tid, - self.player_role_vars[player_id], - value=self.role_ids[role_str], - observed=True) + known_role_id = self.get_role_id_for(role_str) + + def bool_stoch_logp(value, in_val): + if value == in_val: + return -numpy.log(1) + else: + return -numpy.inf + + def bool_stoch_rand(in_val): + return bool(numpy.round(random())) + + def is_known_role(role_id): + if role_id == known_role_id: + return True + return False + + det = mc.Deterministic( + eval=is_known_role, + name="player_seen_role_det_tid%d" % self.tid, + parents={"role_id": self.player_role_vars[player_id]}, + doc="Det TID%d" % self.tid, + dtype=bool, + trace=True, + plot=False) + + transaction.append(det) + + obs = mc.Stochastic( + logp=bool_stoch_logp, + doc="Boolean stochastic observation", + name="player_seen_role_tid%d" % self.tid, + parents={"in_val": det}, + random=bool_stoch_rand, + trace=True, + value=True, + dtype=bool, + observed=True, + cache_depth=2, + plot=False, + verbose=0) + + #obs = mc.Uniform("player_seen_role_tid%d" % self.tid, + #0, det, + #value=1, + #observed=True) transaction.append(obs) self.observations.append(transaction) self.tid += 1 - def eval(self, length=40000): + def eval(self, length=40000, burn=10000): mcmc = mc.MCMC(self._build_model_list()) - mcmc.sample(length, 2000) + mcmc.sample(length, burn) self.model = mcmc + def eval_model_sans_players(self, length=40000, burn=500): + mcmc = mc.MCMC(self._build_restricted_model_list()) + mcmc.sample(length, burn) + def report(self): if self.model is None: self.eval() @@ -129,19 +304,28 @@ class DeceptionGame(object): pp = pprint.PrettyPrinter(indent=4) pp.pprint(self.report()) + def _build_restricted_model_list(self): + out = [] + out.append(self.deck_var) + out.extend(self.player_side_vars[:]) + out.extend(self.player_role_vars[:]) + return out + def _build_model_list(self): out = [] out.append(self.deck_var) out.extend(self.player_side_vars[:]) out.extend(self.player_role_vars[:]) + for player in self.players: + out.extend(player.all_vars[:]) flattened = [item for transaction in self.observations for item in transaction] out.extend(flattened[:]) return list(set(out)) -base_game = DeceptionGame(ResistanceGame(5)) -base_game.eval(20000) +base_game = DeceptionGame(ResistanceGame(10)) +base_game.eval_model_sans_players() base_game.add_known_role(0, "G1") base_game.add_known_side(1, False) base_game.eval() From ef9ba1aafe598ecc00a266a2815cddedf98cd6ea Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Sun, 28 Jul 2013 13:13:42 -0400 Subject: [PATCH 4/5] using potentials --- tim.py | 301 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 162 insertions(+), 139 deletions(-) diff --git a/tim.py b/tim.py index a33806c..9f852ab 100644 --- a/tim.py +++ b/tim.py @@ -4,7 +4,6 @@ import itertools from collections import defaultdict as dd import pprint import numpy -from numpy.random import random def ResistanceGame(n_players): @@ -20,43 +19,40 @@ def BuildNbyNBeliefMatrix(id, n_players): for player_id in range(n_players): vec = mc.Uniform("%d_trust_%d" % (id, player_id), 0.0, 1.0, - trace=False, - size=n_players) + trace=True, + size=n_players, + value=(numpy.ones(n_players) * (1.0 / n_players))) matrix.append(vec) all_vars.append(vec) # Enforce that there's only one role for each player. for i, player_vec in enumerate(matrix): - det_var = sum(vec) - all_vars.append(det_var) - obs = mc.Bernoulli( - "player_%d_one_role_constraint_%d" % (id, i), - det_var, - value=1.0, - trace=False, - observed=True) - all_vars.append(obs) + @mc.potential + def obs_one_role(player_vec=player_vec): + return mc.distributions.normal_like( + sum(player_vec), + 1.0, + 100) + all_vars.append(obs_one_role) # Enforce that there's only one player per role. for i in range(n_players): - det_var = sum([vec[i] for vec in matrix]) - all_vars.append(det_var) - obs = mc.Bernoulli( - "player_%d_one_player_constraint_%d" % (id, i), - det_var, - value=1.0, - trace=False, - observed=True) - all_vars.append(obs) + @mc.potential + def obs_one_per_role(matrix=matrix): + return mc.distributions.normal_like( + sum([vec[i] for vec in matrix]), + 1.0, + 100) + all_vars.append(obs_one_per_role) + return matrix, all_vars + class Player(object): ''' Representing all that the player is and knows ''' - def __init__(self, id, game, side_var, role_var): + def __init__(self, id, game): self.id = id self.game = game - self.side_var = side_var - self.role_var = role_var self.all_vars = [] self.build_belief_matrix() @@ -67,53 +63,18 @@ class Player(object): return self.role_var def build_belief_matrix(self): - self.matrix = [] self.matrix, self.all_vars = \ BuildNbyNBeliefMatrix(self.id, self.game.n_players) # Enforce that the player completely knows her own role. - def player_knows_self(self_role=self.role_var): - return self.matrix[self.id][self_role] - - knows = mc.Deterministic(eval=player_knows_self, - name="player_knows_self_%d" % self.id, - parents={"self_role": self.role_var}, - doc="Player knows self %d" % self.id, - dtype=float, - trace=True, - plot=False) + @mc.potential + def knows(deck_var=self.game.deck_var, matrix=self.matrix): + role_id = self.game.player_role_for_deck_var(deck_var, self.id) + return mc.distributions.normal_like( + matrix[self.id][role_id], + 1.0, + 100) self.all_vars.append(knows) - obs = mc.Bernoulli( - "player_knows_self_constraint_%d" % self.id, - knows, - value=1.0, - observed=True) - self.all_vars.append(obs) - - # Convenience vars for trust - self.side_belief = [] - - def side_belief(player_id, role_vec): - out = 0.0 - for i, role in enumerate(role_vec): - if self.game.role_id_is_good(i): - out += role - out = out / len(role_vec) - return out - - for i in range(self.game.n_players): - is_good = mc.Deterministic(eval=functools.partial(side_belief, i), - name="player%d_trust_%d" % (self.id, i), - parents={"role_vec": self.matrix[i]}, - doc="Does player trust %d?" % i, - dtype=float, - trace=True, - plot=False) - self.side_belief.append(is_good) - self.all_vars.append(is_good) - - def get_good_belief_for(self, player_id): - return self.side_belief[player_id] def get_role_belief_for(self, player_id, role_id): return self.matrix[player_id][role_id] @@ -172,10 +133,15 @@ class DeceptionGame(object): plot=False) self.player_side_vars.append(side) - self.players.append(Player(x, self, side, role)) + #self.players.append(Player(x, self)) - for x in range(50000): - self.deck_var.random() + self.lady_will_duck = mc.Bernoulli("lady_will_duck", 0.5) + self.team_duck = [None] * 5 + self.team_duck[0] = mc.Bernoulli("team_duck_1", 0.8) + self.team_duck[1] = mc.Bernoulli("team_duck_2", 0.6) + self.team_duck[2] = mc.Bernoulli("team_duck_3", 0.4) + self.team_duck[3] = mc.Bernoulli("team_duck_4", 0.2) + self.team_duck[4] = mc.Bernoulli("team_duck_5", 0.0) self.observations = [] self.tid = 0 @@ -186,29 +152,54 @@ class DeceptionGame(object): def role_id_is_good(self, role_id): return self.role_side[role_id] + def player_good_for_deck_var(self, index, player): + return self.all_permutations[index][player][1] + + def player_role_for_deck_var(self, index, player): + return self.get_role_id_for(self.all_permutations[index][player][0]) + + def player_sees_player_and_claims(self, player_view, player_give, claim): + transaction = [] + total_len = len(self.all_permutations) + for i in range(len(self.all_permutations)): + self.deck_var.value = (5119 * i) % total_len + try: + @mc.potential + def claims(deck_var=self.deck_var, will_duck=self.lady_will_duck): + if self.player_good_for_deck_var(deck_var, player_view) or will_duck: + if self.player_good_for_deck_var(deck_var, player_give) == claim: + return 0.0 + else: + return -numpy.inf + else: + if self.player_good_for_deck_var(deck_var, player_give) == claim: + return -numpy.inf + else: + return 0.0 + + return -numpy.inf + except mc.ZeroProbability: + continue + + transaction.append(claims) + self.observations.append(transaction) + self.tid += 1 + def add_known_side(self, player_id, is_good): transaction = [] + total_len = len(self.all_permutations) + for i in range(len(self.all_permutations)): + self.deck_var.value = (5119 * i) % total_len + try: + @mc.potential + def obs(deck_var=self.deck_var): + x = float(self.player_good_for_deck_var(deck_var, player_id)) - \ + float(is_good) + return mc.distributions.normal_like(x, 0.0, 10000) + except mc.ZeroProbability: + continue + break - def player_is_good(side_var): - if side_var == is_good: - return 1.0 - return 0.0 - - det = mc.Deterministic( - eval=player_is_good, - name="player_seen_det_tid%d" % self.tid, - parents={"side_var": self.player_side_vars[player_id]}, - doc="Det TID%d" % self.tid, - dtype=float, - trace=False, - plot=False) - - transaction.append(det) - - obs = mc.Degenerate("player_seen_tid%d" % self.tid, - det, - value=1.0, - observed=True) transaction.append(obs) self.observations.append(transaction) self.tid += 1 @@ -216,62 +207,88 @@ class DeceptionGame(object): def add_known_role(self, player_id, role_str): transaction = [] known_role_id = self.get_role_id_for(role_str) + total_len = len(self.all_permutations) + for i in range(len(self.all_permutations)): + self.deck_var.value = (5119 * i) % total_len + try: + @mc.potential + def role(deck_var=self.deck_var): + x = self.player_role_for_deck_var(deck_var, player_id) \ + - known_role_id + return mc.distributions.normal_like(x, 0.0, 10000) - def bool_stoch_logp(value, in_val): - if value == in_val: - return -numpy.log(1) - else: - return -numpy.inf + except mc.ZeroProbability: + continue + break - def bool_stoch_rand(in_val): - return bool(numpy.round(random())) - - def is_known_role(role_id): - if role_id == known_role_id: - return True - return False - - det = mc.Deterministic( - eval=is_known_role, - name="player_seen_role_det_tid%d" % self.tid, - parents={"role_id": self.player_role_vars[player_id]}, - doc="Det TID%d" % self.tid, - dtype=bool, - trace=True, - plot=False) - - transaction.append(det) - - obs = mc.Stochastic( - logp=bool_stoch_logp, - doc="Boolean stochastic observation", - name="player_seen_role_tid%d" % self.tid, - parents={"in_val": det}, - random=bool_stoch_rand, - trace=True, - value=True, - dtype=bool, - observed=True, - cache_depth=2, - plot=False, - verbose=0) - - #obs = mc.Uniform("player_seen_role_tid%d" % self.tid, - #0, det, - #value=1, - #observed=True) - transaction.append(obs) + transaction.append(role) self.observations.append(transaction) self.tid += 1 - def eval(self, length=40000, burn=10000): + def build_team(self, team, votes, required_success): + transaction = [] + + for voter in range(self.n_players): + total_len = len(self.all_permutations) + for i in range(len(self.all_permutations)): + self.deck_var.value = (5119 * i) % total_len + try: + @mc.potential + def build_team(deck_var=self.deck_var): + voter_is_good = self.player_good_for_deck_var(deck_var, voter) + if voter_is_good: + return mc.distributions.normal_like(x - n_successes, 0.0, 1000) + else: + return mc.distributions.normal_like(x - required_success, len(team) - + team_votes.name = "voter_%d_tid%d" % (voter, self.tid) + + except mc.ZeroProbability: + continue + break + + transaction.append(team_votes) + self.observations.append(transaction) + self.tid += 1 + def team_and_successes(self, team, n_successes, mandatory, r): + transaction = [] + + total_len = len(self.all_permutations) + for i in range(len(self.all_permutations)): + self.deck_var.value = (5119 * i) % total_len + try: + @mc.potential + def team_votes(deck_var=self.deck_var, team_duck=self.team_duck[r - 1]): + team_allegience = [self.player_good_for_deck_var(deck_var, x) + for x in team] + for i, al in enumerate(team_allegience): + if al is True: + continue + else: + if not mandatory and team_duck: + print "Ducking" + team_allegience[i] = True + + x = sum([float(x) for x in team_allegience]) + return mc.distributions.normal_like(x - n_successes, 0.0, 1000) + team_votes.name = "team_votes_tid%d" % self.tid + + except mc.ZeroProbability: + continue + break + + transaction.append(team_votes) + self.observations.append(transaction) + self.tid += 1 + + def eval(self, length=60000, burn=30): mcmc = mc.MCMC(self._build_model_list()) mcmc.sample(length, burn) self.model = mcmc - def eval_model_sans_players(self, length=40000, burn=500): + def eval_model_sans_players(self, length=40000, burn=0): mcmc = mc.MCMC(self._build_restricted_model_list()) - mcmc.sample(length, burn) + #mcmc.use_step_method(mc.DiscreteMetropolis, self.deck_var, proposal_distribution='Prior') + mcmc.sample(length, burn, tune_throughout=False) def report(self): if self.model is None: @@ -314,8 +331,11 @@ class DeceptionGame(object): def _build_model_list(self): out = [] out.append(self.deck_var) + out.append(self.lady_will_duck) out.extend(self.player_side_vars[:]) out.extend(self.player_role_vars[:]) + for duck in self.team_duck: + out.append(duck) for player in self.players: out.extend(player.all_vars[:]) flattened = [item for transaction in self.observations @@ -324,10 +344,13 @@ class DeceptionGame(object): return list(set(out)) -base_game = DeceptionGame(ResistanceGame(10)) -base_game.eval_model_sans_players() -base_game.add_known_role(0, "G1") -base_game.add_known_side(1, False) +base_game = DeceptionGame(ResistanceGame(5)) +#base_game.add_known_role(0, "G1") +base_game.build_team([0, 1, 2], [1, 0, 1, 1, 0], 1) +base_game.team_and_successes([0, 1, 2], 2, 1, False) +#base_game.team_and_successes([0, 1, 2], 2, 2, False) +#base_game.player_sees_player_and_claims(0, 1, False) +#base_game.add_known_side(1, False) base_game.eval() base_game.print_report() From 6c617656243b03c902e0c08213a03889c72f58b4 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Mon, 29 Jul 2013 02:42:57 -0400 Subject: [PATCH 5/5] Haskell implementation --- LICENSE | 30 ++++++ Setup.hs | 2 + Tim.hs | 274 ++++++++++++++++++++++++++++++++++++++++++++++++ tim-the-enchanter.cabal | 22 ++++ 4 files changed, 328 insertions(+) create mode 100644 LICENSE create mode 100644 Setup.hs create mode 100644 Tim.hs create mode 100644 tim-the-enchanter.cabal diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2d8a16f --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2013, Barak Michener + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Barak Michener nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/Tim.hs b/Tim.hs new file mode 100644 index 0000000..3291e2b --- /dev/null +++ b/Tim.hs @@ -0,0 +1,274 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DoAndIfThenElse #-} + +import qualified Numeric.Probability.Distribution as Dist +import Numeric.Probability.Distribution as Probs +--import Control.Monad (liftM2, replicateM, ap) +--import Control.Monad (ap) +import Control.Monad.Parallel (replicateM) +import Control.Monad (replicateM, ap) +import Data.List (permutations, isPrefixOf) +import Data.List.Split (splitOn) +import qualified Numeric.Probability.Random as Rnd +import Numeric.Probability.Trace (Trace) + +type Prob = Double +type Dist a = Dist.T Prob a + +data Side = Good | Evil + deriving (Show, Eq, Ord) + +otherSide Good = Evil +otherSide Evil = Good + +data Role = Role { roleName :: String + , roleSide :: Side } + deriving (Show, Eq, Ord) + +type Deal = [Role] + +good1 = Role "G1" Good +good2 = Role "G2" Good +good3 = Role "G3" Good +good4 = Role "G4" Good +good5 = Role "G5" Good +good6 = Role "G6" Good +bad1 = Role "E1" Evil +bad2 = Role "E2" Evil +bad3 = Role "E3" Evil +bad4 = Role "E4" Evil + +defaultGame :: Deal +defaultGame = [good1, good2, good3, bad1, bad2, + good4, bad3, good5, good6, bad4 ] + +gameSize n = take n defaultGame + +allDecks n = permutations $ take n defaultGame + + +bernoulli :: Double -> Dist Bool +bernoulli x = fromFreqs [(True, x), (False, (1 - x))] + +deals :: Int -> Dist Deal +deals = Dist.uniform . allDecks + +stateSpace :: Int -> Dist Game +stateSpace nPlayers = return Game `ap` + deals nPlayers `ap` -- trueRoles + bernoulli 0.5 `ap` + makeVoteDucks `ap` + makeProposalIgnorance `ap` + makeProposalDucks `ap` + makeDecay + + --`ap` -- ladyWillLie + --playerBeliefVar (1 / 3.0) nPlayers + +data Game = Game { trueRoles :: Deal + , ladyWillLie :: Bool + , willDuckOnRound :: [Bool] + , proposalIgnorant :: Double + , proposalDucks :: Double + , decay :: Double + } + deriving (Show, Eq, Ord) + + + +beliefVar :: Double -> Dist Double +beliefVar precision = Dist.uniform [0.0, precision..1.0] + +playerBeliefVar :: Double -> Int -> Dist [Double] +playerBeliefVar precision n_players = constraint ?=<< var + where var = Control.Monad.replicateM n_players $ beliefVar precision + constraint in_var = + sum in_var == (sum $ Prelude.map (\x -> if roleSide x == Good then 1.0 else 0.0) $ gameSize n_players) + +makeVoteDucks = mapM bernoulli [0.8, 0.6, 0.4] +makeProposalIgnorance = certainly 0.8 +makeProposalDucks = certainly 0.8 +makeDecay = certainly 0.8 + +isGood :: Int -> Game -> Bool +isGood player game = roleSide (trueRoles game !! player) == Good +isEvil player game = roleSide (trueRoles game !! player) == Evil + +playerNisRole :: Int -> String -> Dist.Event Game +playerNisRole n name game = roleName (trueRoles game !! n) == name + +seePlayerN n role dist = playerNisRole n role ?=<< dist + +playerNisSide :: Int -> Side -> Dist.Event Game +playerNisSide n side game = getSide n game == side + +-- trustLevel n game = tableTrust game !! n + +playerSeesPlayerAndClaims :: Int -> Int -> Side -> Dist.Event Game +playerSeesPlayerAndClaims p1 p2 claim game = + if isGood p1 game + then playerNisSide p2 claim game + else if not (ladyWillLie game) then playerNisSide p2 claim game + else playerNisSide p2 (otherSide claim) game + +getSide x game = roleSide (trueRoles game !! x) + +getRoundIgnorance round game = proposalIgnorant game * (decay game ** (round - 1)) > 0.5 + +getProposalDucks round game = proposalDucks game * (decay game ** (round - 1)) > 0.5 + + +teamIsGood [] game = True +teamIsGood (x:xs) game = getSide x game == Good && (teamIsGood xs game) + +doProposal team votes round game = + foldl (||) False $ Prelude.map (proposalConstraints game) $ zip [0,1..] votes + where + proposalConstraints game (player, vote) = + if vote == 1 then + if getSide player game == Good then + if teamIsGood team game then True + else if getRoundIgnorance round game then True + else False + else + if not (teamIsGood team game) then True + else if getProposalDucks round game then True + else False + else + if getSide player game == Good then + if not (teamIsGood team game) then True + else if getRoundIgnorance round game then True + else False + else + if teamIsGood team game then True + else if getProposalDucks round game then True + else False + + + +-- doVote :: [Int] -> Int -> Dist.Event Game +doVote team successes round game = + foldl (||) False $ Prelude.map (makeConstraints game) $ [(successes, x) | x <- permutations team] + where + makeConstraints game (0, []) = True + makeConstraints game (0, (x:xs)) = getSide x game == Evil && (makeConstraints game (0,xs)) + makeConstraints game (n, (x:xs)) = (getSide x game == Good || (ducks round game && (getSide x game == Evil))) && (makeConstraints game ((n - 1), xs)) + ducks round game = if round > 2 then False else ((willDuckOnRound game) !! round) + + + + + +assertOnGame x game = x ?=<< game + +assertions = [(doVote [0, 2] 1 1), (playerSeesPlayerAndClaims 0 1 Evil)] + +applyAssertions = foldl (\x y -> (assertOnGame y x)) + + +dropNth i list = (take i list) ++ (drop (i+1) (list)) +{-seePlayerN n role dist = do-} + {-val <- dist-} + {-Dist.filter (playerNisRole n role) val-} + {-return val-} + +--main = putStrLn $ show (seePlayerN 0 good1 deals) + +playerReport n dist = + "Player " ++ show n ++ ":\n" ++ + " Is Good: " ++ show (playerNisSide n Good ?? dist) ++ "\n" ++ + " Is Evil: " ++ show (playerNisSide n Evil ?? dist) ++ "\n" + +ladyLoop nPlayers stateSpace assertions command ls = + let continueLoop = mainLoop nPlayers stateSpace assertions ls in + let args = splitOn " " command in + if length args < 4 then do + putStrLn $ show args + continueLoop + else do + putStrLn "Got it" + let arg1 = read (args !! 1) :: Int + let arg2 = read (args !! 2) :: Int + let arg3 = if (read (args !! 3) :: Int) == 1 then Good else Evil + mainLoop nPlayers stateSpace (assertions ++ [(playerSeesPlayerAndClaims arg1 arg2 arg3)]) (ls ++ [command]) + +assertLoop nPlayers stateSpace assertions command ls = + let continueLoop = mainLoop nPlayers stateSpace assertions ls in + let args = splitOn " " command in + if length args < 3 then do + putStrLn $ show args + continueLoop + else do + putStrLn "Got it" + let arg1 = read (args !! 1) :: Int + let arg2 = read (args !! 2) :: Int + let side = if arg2 == 0 then Evil else Good in + mainLoop nPlayers stateSpace (assertions ++ [(playerNisSide arg1 side)]) (ls ++ [command]) + +propLoop nPlayers stateSpace assertions command ls = + let continueLoop = mainLoop nPlayers stateSpace assertions ls in + let args = splitOn " " command in + if length args < 4 then do + putStrLn $ show args + continueLoop + else do + putStrLn "Got it" + let arg1 = Prelude.map (\x -> (read x :: Int)) $ drop 1 $ splitOn "" (args !! 1) + let arg2 = Prelude.map (\x -> (read x :: Int)) $ drop 1 $ splitOn "" (args !! 2) + let arg3 = read (args !! 3) :: Double + putStrLn $ show arg1 + putStrLn $ show arg2 + mainLoop nPlayers stateSpace (assertions ++ [(doProposal arg1 arg2 (arg3 - 1))]) (ls ++ [command]) + +voteLoop nPlayers stateSpace assertions command ls = + let continueLoop = mainLoop nPlayers stateSpace assertions ls in + let args = splitOn " " command in + if length args < 4 then do + putStrLn $ show args + continueLoop + else do + putStrLn "Got it" + let arg1 = Prelude.map (\x -> (read x :: Int)) $ drop 1 $ splitOn "" (args !! 1) + let arg2 = read (args !! 2) :: Int + let arg3 = read (args !! 3) :: Int + putStrLn $ show arg1 + putStrLn $ show arg2 + mainLoop nPlayers stateSpace (assertions ++ [(doVote arg1 arg2 (arg3 - 1))]) (ls ++ [command]) + +mainLoop nPlayers stateSpace assertions ls = do + let continueLoop = mainLoop nPlayers stateSpace assertions ls + putStrLn $ "Tim " ++ show nPlayers ++ "> " + command <- getLine + if command == "quit" + then return () + else if isPrefixOf "lol" command then + ladyLoop nPlayers stateSpace assertions command ls + else if isPrefixOf "ass" command then + assertLoop nPlayers stateSpace assertions command ls + else if isPrefixOf "ls" command then do + putStrLn $ foldl (\x y -> x ++ (show $ fst y) ++ ": " ++ (snd y) ++ "\n") "" $ zip [0,1..] ls + continueLoop + else if isPrefixOf "deass" command then do + let args = splitOn " " command + let arg1 = read (args !! 1) :: Int + mainLoop nPlayers stateSpace (dropNth arg1 assertions) (dropNth arg1 ls) + else if isPrefixOf "vot" command then + voteLoop nPlayers stateSpace assertions command ls + else if isPrefixOf "pro" command then + propLoop nPlayers stateSpace assertions command ls + else if command == "eval" then do + trace <- Control.Monad.Parallel.replicateM 100 (Rnd.run $ Rnd.pick $ applyAssertions stateSpace assertions) + let traceDist = Dist.uniform trace + putStrLn $ foldl (++) "" $ Prelude.map (flip playerReport traceDist) [0..(nPlayers - 1)] + continueLoop + else do + putStrLn "Unknown" + continueLoop + +main = do + putStrLn "N players? " + players <- getLine + let nplayers = read players :: Int + mainLoop nplayers (stateSpace nplayers) [] [] + diff --git a/tim-the-enchanter.cabal b/tim-the-enchanter.cabal new file mode 100644 index 0000000..d5129d5 --- /dev/null +++ b/tim-the-enchanter.cabal @@ -0,0 +1,22 @@ +-- Initial tim-the-enchanter.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: tim-the-enchanter +version: 0.1.0.0 +-- synopsis: +-- description: +license: BSD3 +license-file: LICENSE +author: Barak Michener +maintainer: me@barakmich.com +-- copyright: +category: Game +build-type: Simple +cabal-version: >=1.8 + +executable tim-the-enchanter + -- main-is: + Main-is: Tim.hs + ghc-options: -O3 -threaded -rtsopts + -- other-modules: + build-depends: base ==4.6.*, probability, monad-parallel, split