from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random, datetime #,django.db.models import numpy as np from . import myfunctions as mp author = 'Coralio Ballester and Marc Vorsatz' doc = """ A beauty contest played in networks """ class Constants(BaseConstants): name_in_url = 'BCNets' players_per_group = None num_stages = 4 rounds_per_stage = 2 min_stage_dur = 30 max_stage_dur = 90 test_stage_time = 8 stage_times = [test_stage_time]*num_stages time_gap = 4 num_rounds = num_stages * rounds_per_stage approx_time = 2 winner_payoff = c(100) default_guess = 0 min_guess = 0 max_guess = 100 decimal_places = 0 max_digits = 5 old_games_list = [ { "num_players": 4, "num_anchors": 2, "labels":["A", "B","C","D"], "anchors":[80,20], "weights": [[0,0,0,0.5,0.5,0],[0.5,0,0,0.5,0,0],[0,0.5,0,0,0,0.5],[0,0.5,0.5,0,0,0]], "verbose": ["El jugador A debe adivinar la media entre 80 y D", "El jugador B debe adivinar la media entre A y D", "El jugador C debe adivinar la media entre B y 20", "El jugador D debe adivinar la media entre B y C"] }, { "num_players": 2, "num_anchors": 1, "labels":["A", "B"], "anchors":[90], "weights": [[0, 0.5, 0], [0.5, 0, 0.5]], "verbose": ["El jugador A debe adivinar la mitad de B", "El jugador B debe adivinar la media entre 90 y A"] }, { "num_players": 3, "num_anchors": 2, "labels": ["A", "B","C"], "anchors": [100,50], "weights": [[0, 0.5, 0,0.5,0], [0,0,0.25, 0.25,0],[1/3,1/3,0, 0,1/3]], "verbose": ["El jugador A debe adivinar la media entre 100 y B", "El jugador B debe adivinar la mitad de la media entre 100 y C", "El jugador C debe adivinar la media entre 50, A and B"] } ] games_list = [ { "num_players": 4, "num_anchors": 1, "labels": ["A", "B", "C", "D"], "anchors": [80], "weights": [[0,0.5,0,0,0],[0,0,0.5,0,0.5],[0,0,0,1,0],[0.5,0,0,0,0.5]], "verbose": ["El jugador A debe adivinar la mitad de lo que declare B.", "El jugador B debe adivinar la media entre 80 y lo que declare C.", "El jugador C debe adivinar lo que declare D.", "El jugador D debe adivinar la media entre 80 y lo que declare A."], "image_path": "g4.png" }, { "num_players": 4, "num_anchors": 1, "labels": ["A", "B", "C", "D"], "anchors": [80], "weights": [[0, 1 / 4, 0, 0, 0], [0, 0, 1 / 4, 0, 1 / 4], [0, 0, 0, 1 / 4, 0], [0, 0, 1 / 4, 0, 1 / 4]], "verbose": ["El jugador A debe adivinar la cuarta parte de lo que declaren B.", "El jugador B debe adivinar la mitad de la media entre 80 y C.", "El jugador C debe adivinar la cuarta parte de lo que declaren D.", "El jugador D debe adivinar la mitad de la media entre 80 y C."], "image_path": "g6.png" }, { "num_players": 4, "num_anchors": 1, "labels": ["A", "B", "C", "D"], "anchors": [80], "weights": [[0, 0, 0, 1 / 4, 0], [1 / 2, 0, 0, 0, 1 / 2],[1 / 2, 0, 0, 0, 0], [1 / 2, 0, 0, 0, 1 / 2]], "verbose": ["El jugador A debe adivinar la cuarta parte de lo que declare D.", "El jugador B debe adivinar la media entre 80 y lo que declare A.", "El jugador C debe adivinar la mitad de lo que declare A.", "El jugador D debe adivinar la media entre 80 y lo que declare A."], "image_path": "g7.png" }, { "num_players": 4, "num_anchors": 1, "labels": ["A", "B", "C", "D"], "anchors": [80], "weights": [[0, 1 / 3, 1 / 3, 1 / 3, 0], [1 / 4, 0, 1 / 4, 1 / 4, 1 / 4],[1 / 3, 1 / 3, 0, 1 / 3, 0], [1 / 4, 1 / 4, 1 / 4, 0, 1 / 4]], "verbose": ["El jugador A debe adivinar la media de lo que declaren B, C y D.", "El jugador B debe adivinar la media entre 80, A, C y D.", "El jugador C debe adivinar la media de lo que declaren A, B y D.", "El jugador D debe adivinar la media entre 80 A, B y C."], "image_path": "g9.png" } ] games_sample = [1,0] dummy_game = 0 class Subsession(BaseSubsession): def set_from_conf(self, name, evaluate=False): if name in self.session.config: if evaluate: self.session.vars[name] = eval(self.session.config[name]) else: self.session.vars[name] = self.session.config[name] def before_session_starts(self): # INITIALIZATION FROM CONFIG # and assign players to groups and games if self.round_number == 1: #initialize session vars #self.set_from_conf(name='games_sample', evaluate=True) #self.set_from_conf(name='num_stages') #self.set_from_conf(name='test_stage_time') #self.set_from_conf(name='rounds_per_stage') #self.session.vars('stage_times') = [self.session.vars('test_stage_time')] * self.session.vars('num_stages') # player set and shuffle players = self.get_players() # random.shuffle(players) dist = mp.DistributeInGroups( atoms = players, groups = range(len(Constants.games_list)), sample = Constants.games_sample, capacities = [g['num_players'] for g in Constants.games_list] ) self.session.vars['dist_groups'] = [d['group'] for d in dist] player_list = [it['atoms'] for it in dist] self.set_group_matrix(player_list) else: self.group_like_round(1) for j, g in enumerate(self.get_groups()): dist_groups = self.session.vars['dist_groups'] group_id = dist_groups[j] g.stage = ((g.round_number - 1) // Constants.rounds_per_stage) + 1 g.decision_num = ((g.round_number - 1) % Constants.rounds_per_stage) + 1 if group_id != -1: g.game_played = group_id g.is_dummy_group = False else: g.game_played = Constants.dummy_game g.is_dummy_group = True class Group(BaseGroup): test = models.CharField() game_played = models.IntegerField() stage = models.IntegerField() decision_num = models.IntegerField() is_dummy_group = models.BooleanField() pay_stage = models.BooleanField() def get_game_features(self): return Constants.games_list[self.game_played] def set_payoffs(self): pay_stage_num = random.randint(1, Constants.num_stages) self.get_players()[0].participant.vars['pay_stage_num'] = pay_stage_num for k in range(1, Constants.num_stages + 1): group = self.in_round(k * Constants.rounds_per_stage) group.pay_stage = (group.stage == pay_stage_num) game = Constants.games_list[group.game_played] actions = [float(p.stage_guess) for p in group.get_players()] allvec = np.array(actions + game['anchors']) xvec = np.array(actions) hmatrix = np.array(game['weights']) targets = hmatrix.dot(allvec) dist = np.absolute(xvec - targets) bound=10000000000000 winners_ind = [] for i,p in enumerate(group.get_players()): p.is_winner = False p.distance = dist[i] p.target = targets[i] if dist[i] < bound: winners_ind = [i] bound = dist[i] elif dist[i] == bound: winners_ind += [i] for i in winners_ind: group.get_players()[i].is_winner = True class Player(BasePlayer): is_winner = models.BooleanField() decision_list = models.CharField() chosen_by_otree = models.BooleanField() guess_value = models.IntegerField( initial = None, min = Constants.min_guess, max = Constants.max_guess ) stage_guess = models.IntegerField( initial=None, min=Constants.min_guess, max=Constants.max_guess ) target = models.FloatField() distance = models.FloatField() def role(self): game = Constants.games_list[self.group.game_played] return game['labels'][self.id_in_group - 1] def time_remaining(self): dif = (datetime.datetime.now() - self.participant.vars['start_time']).total_seconds() i = 0 sum = 0 while (sum < dif and i < len(Constants.stage_times)): sum += Constants.stage_times[i] + Constants.time_gap i += 1 t_left = sum - dif #This if is useful to exit a refresh-loop in the last wait page if (t_left < 0): i += 1 return { 'stage': i, 'time': max(t_left,0) #'is_gap': (t_left > 0) and (t_left < Constants.time_gap) }