from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import numpy as np author = 'TZ' doc = """ Repeated PD game """ class Constants(BaseConstants): name_in_url = 'phase_2' players_per_group = 2 num_rounds = 400 class Subsession(BaseSubsession): session_vars_dump = models.StringField() # mostly for testing purposes random_seed = models.IntegerField(initial=-1) game = models.IntegerField(initial=-10) # which supergame configuration is being played in the round cutoff = models.IntegerField(initial=-10) # relevant delta for the current supergame configuration # payoff matrix for current supergame configuration cc = models.IntegerField(initial=-10) dd = models.IntegerField(initial=-10) cd = models.IntegerField(initial=-10) dc = models.IntegerField(initial=-10) match_number = models.IntegerField(initial=-10) # which match this round corresponds to period_number = models.IntegerField(initial=-10) # tracks the current period of the current match dice_roll = models.IntegerField(initial=-10) # random number for this round last_period = models.IntegerField(initial=-10) # 1 if last period of a match and 0 otherwise first_period = models.BooleanField(initial=False) # True if it is the first period of a match stop_session = models.BooleanField(initial=False) # True if the session is over def creating_session(self): # importing parameters from settings file random_seed = self.session.config['random_seed'] # allows for manual setting of random seed num_supergames = self.session.config['num_supergames'] # number of supergame configurations num_blocks = self.session.config['num_blocks'] # number of blocks of supergames num_matches = num_supergames * num_blocks if self.round_number == 1: # allows for recording and manual setting of random seed if random_seed > 0: np.random.seed(random_seed) else: # draws a random seed between 1 and 10000000 and then records it seed = np.random.choice(10000000, 1)[0] np.random.seed(seed) self.random_seed = seed self.group_randomly() self.period_number = 1 current_match = 1 # draws a random number between 1 and 100 for every round of the session dice_rolls = np.random.randint(1, 101, Constants.num_rounds).tolist() self.session.vars['dice_rolls'] = dice_rolls # creates random list of supergame configurations self.session.vars['games'] = list(np.random.permutation(range(1, num_supergames + 1))) self.game = self.session.vars['games'].pop() # chooses a supergame configuration for first match # creates lists that store cutoff/payoff settings for each supergame configuration self.session.vars['delta_dic'] = [] self.session.vars['game_dic'] = [] for i in range(1, num_supergames + 1): self.session.vars['delta_dic'].append(self.session.config['pd_' + str(i) + '_delta']) self.session.vars['game_dic'].append( [self.session.config['pd_' + str(i) + '_cc'], self.session.config['pd_' + str(i) + '_dd'], self.session.config['pd_' + str(i) + '_cd'], self.session.config['pd_' + str(i) + '_dc']] ) self.session.vars['match_to_game'] = {} self.session.vars['match_lengths'] = [1] self.session.vars['last_rounds'] = [1] self.session.vars['match_info'] = {} for i in range(1, num_matches + 1): self.session.vars['match_info'][str(i)] = { 'first_round': 1, 'last_round': 1, 'game': 0, 'delta': 0, 'cc_payoff': 0, } if self.round_number > 1: if not self.in_round(self.round_number - 1).stop_session: prev_roll = self.in_round(self.round_number - 1).dice_roll # dice roll from previous round # for convenience (incoming match number and supergame configuration) incoming_match = self.in_round(self.round_number - 1).match_number incoming_game = self.in_round(self.round_number - 1).game incoming_cutoff = self.in_round(self.round_number - 1).cutoff # if match from last round continued if prev_roll <= incoming_cutoff: self.group_like_round(self.round_number - 1) incoming_period = self.in_round(self.round_number - 1).period_number self.period_number = incoming_period + 1 # increments period number self.game = incoming_game # records cutoff/payoff settings current_match = incoming_match # if match from last round ended else: if self.in_round(self.round_number - 1).match_number < num_matches: self.first_period = True current_match = incoming_match + 1 # rematches subjects subjects = np.random.permutation(self.get_players()).tolist() new_match = [subjects[x:x + 2] for x in range(0, len(subjects) - 1, 2)] self.set_group_matrix(new_match) # resets period number to 1 self.period_number = 1 # repopulates block of supergame configurations if a new block has started if (current_match - 1) % num_supergames == 0: self.session.vars['games'] = list(np.random.permutation(range(1, num_supergames + 1))) self.game = self.session.vars['games'].pop() else: self.stop_session = True else: self.stop_session = True if not self.stop_session: # records dice roll for current round dice_rolls = self.session.vars['dice_rolls'] dice_roll = dice_rolls[self.round_number - 1] self.dice_roll = dice_roll self.match_number = current_match self.cutoff = self.session.vars['delta_dic'][self.game - 1] self.cc = self.session.vars['game_dic'][self.game - 1][0] self.dd = self.session.vars['game_dic'][self.game - 1][1] self.cd = self.session.vars['game_dic'][self.game - 1][2] self.dc = self.session.vars['game_dic'][self.game - 1][3] if self.round_number == 1: self.session.vars['match_info']['1'] = { 'first_round': 1, 'last_round': 1, 'game': self.game, 'delta': self.cutoff, 'cc_payoff': self.cc, } if self.first_period: self.session.vars['match_lengths'].append(self.period_number) self.session.vars['last_rounds'].append(self.round_number) self.session.vars['match_info'][str(self.match_number)]['first_round'] = self.round_number self.session.vars['match_info'][str(self.match_number)]['game'] = self.game self.session.vars['match_info'][str(self.match_number)]['delta'] = self.cutoff self.session.vars['match_info'][str(self.match_number)]['cc_payoff'] = self.cc if self.dice_roll <= self.cutoff: self.last_period = 0 else: self.last_period = 1 self.session.vars['match_lengths'][self.match_number - 1] = self.period_number self.session.vars['last_rounds'][self.match_number - 1] = self.round_number self.session.vars['longest_match'] = [max(self.session.vars['match_lengths']), np.argmax(self.session.vars['match_lengths']) + 1] if self.match_number == 27: self.session.vars['last_round'] = self.round_number for i in range(1, num_matches + 1): self.session.vars['match_info'][str(i)]['last_round'] = self.session.vars['last_rounds'][i - 1] def vars_for_admin_report(self): return dict( match_number=self.match_number, period_number=self.period_number, final_round=self.session.vars['last_rounds'][self.match_number - 1], match_length=self.session.vars['match_lengths'][self.match_number - 1], last_round=self.session.vars['last_round'], longest_match=self.session.vars['longest_match'][0], longest_match_id=self.session.vars['longest_match'][1], match_info=self.session.vars['match_info'], min_duration=round((5 * self.session.vars['last_round']) / 60, 1), estimated_duration=round(15 * self.session.vars['last_round'] / 60, 1) ) class Group(BaseGroup): pass # def set_point(self): # actions = [p.action for p in self.get_players()] # sum_action = sum(actions) # for p in self.get_players(): # q = p.get_others_in_group()[0] # p.others_action = q.action # if sum_action == 2: # p.points = self.subsession.cc # if sum_action == 0: # p.points = self.subsession.dd # if sum_action == 1: # if p.action == 1: # p.points = self.subsession.cd # else: # p.points = self.subsession.dc # # p.participant.vars['pd_points'] += p.points class Player(BasePlayer): action = models.IntegerField(initial=-1) points = models.IntegerField(initial=-5) others_action = models.IntegerField(initial=-1) points_set = models.BooleanField(initial=False) def set_points(self): if not self.points_set: q = self.get_others_in_group()[0] self.others_action = q.action if self.action == 1: if q.action == 1: self.points = self.subsession.cc else: self.points = self.subsession.cd else: if q.action == 1: self.points = self.subsession.dc else: self.points = self.subsession.dd self.points_set = True def set_dollars(self): past_players = self.in_rounds(1, self.round_number - 1) points = [p.points for p in past_players] pd_points = sum(points) dollars = round(pd_points * self.session.config['pd_conversion'], 2) self.participant.vars['pd_points'] = pd_points self.payoff = dollars