from otree.api import * c = cu doc = '\nThis is a one-shot "Prisoner\'s Dilemma". Two players are asked separately\nwhether they want to cooperate or defect. Their choices directly determine the\npayoffs.\n' class C(BaseConstants): NAME_IN_URL = 'study' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 60 CONTINUATION_PROB = 0.8 # make 0.8 or so PAYOFF_P1G1 = (cu(4), cu(1), cu(6), cu(3)) PAYOFF_P2G1 = (cu(4), cu(1), cu(6), cu(3)) PAYOFF_P1G2 = (cu(4), cu(1), cu(3), cu(0)) PAYOFF_P2G2 = (cu(4), cu(1), cu(5), cu(2)) MIN_NUM_ROUNDS_PER_GAME = 12 ##make 30 or so INSTRUCTIONS_TEMPLATE = 'prisoner/instructions.html' class Subsession(BaseSubsession): reshuffle = models.BooleanField(initial=False) newgame = models.BooleanField(initial=False) game = models.IntegerField(initial=1) rounds1 = models.IntegerField(initial=1) stillon = models.BooleanField(initial=True) def creating_session(subsession: Subsession): session = subsession.session if subsession.round_number == 1: subsession.group_randomly() #set all to present in first round players = subsession.get_players() for p in players: p.dropped_out = False p.other_out = False else: import random ru = random.uniform(0,1) #reshuffle groups with prob 1-ru if ru>C.CONTINUATION_PROB: subsession.reshuffle = True # switch game if above min rounds if subsession.round_number > C.MIN_NUM_ROUNDS_PER_GAME: subsession.game = 3 else: subsession.game = 1 #reshuffle keeping roles fixed, ensure new matches M = subsession.in_round(subsession.round_number - 1).get_group_matrix() M = M + [M[0]] A = [] for i in range(0,len(M)-1): A.append([M[i][0], M[i+1][1]]) subsession.set_group_matrix(A) else: subsession.reshuffle = False subsession.group_like_round(subsession.round_number - 1) subsession.game = subsession.in_round(subsession.round_number - 1).game if subsession.game == 1: subsession.rounds1 = subsession.round_number else: subsession.rounds1 = subsession.in_round(subsession.round_number - 1).rounds1 if subsession.round_number > C.MIN_NUM_ROUNDS_PER_GAME + subsession.rounds1: subsession.stillon = False class Group(BaseGroup): pass def set_payoffs(group: Group): for p in group.get_players(): set_payoff(p) def get_ready(group: Group): if group.subsession.round_number > 1: for p in group.get_players(): p.dropped_out = p.in_round(p.round_number - 1).dropped_out for p in group.get_players(): p.other_out = other_player(p).dropped_out if group.subsession.stillon == False: for p in group.get_players(): p.stillon = False class Player(BasePlayer): cooperate = models.BooleanField(choices=[[True, 'option A'], [False, 'option B']], doc='This player s decision', widget=widgets.RadioSelect) prolific_id = models.StringField(default=str(" ")) dropped_out = models.BooleanField(default=False) other_out = models.BooleanField(default=False) stillon = models.BooleanField(default=True) def other_player(player: Player): return player.get_others_in_group()[0] def set_payoff(player: Player): game = player.subsession.game #check if other participant still in game: if other_player(player).dropped_out: player.payoff = 0 elif player.dropped_out: player.payoff = 0 else: if player.id_in_group == 1: if game == 1: payoffs = C.PAYOFF_P1G1 elif game == 2: payoffs = C.PAYOFF_P2G1 elif game == 3: payoffs = C.PAYOFF_P1G2 else: payoffs = C.PAYOFF_P2G2 if player.id_in_group == 2: if game == 1: payoffs = C.PAYOFF_P2G1 elif game == 2: payoffs = C.PAYOFF_P1G1 elif game == 3: payoffs = C.PAYOFF_P2G2 else: payoffs = C.PAYOFF_P1G2 payoff_matrix = { (True, True): payoffs[0], (True, False): payoffs[1], (False, True): payoffs[2], (False, False): payoffs[3], } other = other_player(player) player.payoff = payoff_matrix[(player.cooperate, other.cooperate)] class StartWaitPage(WaitPage): after_all_players_arrive = get_ready @staticmethod def app_after_this_page(player, upcoming_apps): if player.stillon == False: return upcoming_apps[-1] class NoMatch(Page): timeout_seconds = 10 @staticmethod def is_displayed(player: Player): return player.other_out and not player.dropped_out and player.subsession.stillon class DroppedOut(Page): timeout_seconds = 10 @staticmethod def is_displayed(player: Player): return player.dropped_out and player.subsession.stillon class Introduction(Page): form_model = 'player' timeout_seconds = 90 @staticmethod def before_next_page(self, timeout_happened): self.prolific_id = self.participant.label @staticmethod def is_displayed(player: Player): return player.round_number == 1 and not player.other_out and not player.dropped_out and player.subsession.stillon @staticmethod def vars_for_template(player: Player): game = player.subsession.game if player.round_number>1: newgame = game>player.in_round(player.round_number - 1).subsession.game else: newgame = False newmatch = player.subsession.reshuffle new = player.round_number == 1 if player.id_in_group == 1: if game == 1: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 2: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 3: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 else: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 if player.id_in_group == 2: if game == 1: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 2: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 3: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 else: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 return dict(cc0 = payoffs[0], cd0 = payoffs[1], dc0 = payoffs[2], dd0 = payoffs[3], cc1 = opayoffs[0], cd1 = opayoffs[1], dc1 = opayoffs[2], dd1 = opayoffs[3], newgame = newgame, newmatch = newmatch, new = new, ptype = player.id_in_group, optype = 3 - player.id_in_group) class NewMatch(Page): form_model = 'player' timeout_seconds = 30 @staticmethod def is_displayed(player: Player): game = player.subsession.game if player.round_number>1: newgame = game>player.in_round(player.round_number - 1).subsession.game #display if groups reshuffled but not new game return player.subsession.reshuffle and not newgame and not player.other_out and not player.dropped_out and player.subsession.stillon else: return False @staticmethod def vars_for_template(player: Player): game = player.subsession.game if player.id_in_group == 1: if game == 1: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 2: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 3: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 else: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 if player.id_in_group == 2: if game == 1: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 2: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 3: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 else: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 return dict(cc0 = payoffs[0], cd0 = payoffs[1], dc0 = payoffs[2], dd0 = payoffs[3], cc1 = opayoffs[0], cd1 = opayoffs[1], dc1 = opayoffs[2], dd1 = opayoffs[3]) class NewPay(Page): form_model = 'player' timeout_seconds = 60 @staticmethod def is_displayed(player: Player): game = player.subsession.game if player.round_number>1: #display if we have started a new game newgame = game>player.in_round(player.round_number - 1).subsession.game return newgame and not player.other_out and not player.dropped_out and player.subsession.stillon else: return False @staticmethod def vars_for_template(player: Player): game = player.subsession.game if player.id_in_group == 1: if game == 1: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 2: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 3: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 else: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 if player.id_in_group == 2: if game == 1: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 2: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 3: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 else: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 return dict(cc0 = payoffs[0], cd0 = payoffs[1], dc0 = payoffs[2], dd0 = payoffs[3], cc1 = opayoffs[0], cd1 = opayoffs[1], dc1 = opayoffs[2], dd1 = opayoffs[3]) class Decision(Page): form_model = 'player' form_fields = ['cooperate'] timeout_seconds = 30 #don't show if no partner @staticmethod def is_displayed(player: Player): return not player.other_out and not player.dropped_out and player.subsession.stillon #remove participant if timed out @staticmethod def before_next_page(player, timeout_happened): if timeout_happened: # you may want to fill a default value for any form fields, # because otherwise they may be left null. player.dropped_out = True @staticmethod def vars_for_template(player: Player): game = player.subsession.game if player.id_in_group == 1: if game == 1: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 2: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 3: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 else: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 if player.id_in_group == 2: if game == 1: payoffs = C.PAYOFF_P2G1 opayoffs = C.PAYOFF_P1G1 elif game == 2: payoffs = C.PAYOFF_P1G1 opayoffs = C.PAYOFF_P2G1 elif game == 3: payoffs = C.PAYOFF_P2G2 opayoffs = C.PAYOFF_P1G2 else: payoffs = C.PAYOFF_P1G2 opayoffs = C.PAYOFF_P2G2 return dict(cc0 = payoffs[0], cd0 = payoffs[1], dc0 = payoffs[2], dd0 = payoffs[3], cc1 = opayoffs[0], cd1 = opayoffs[1], dc1 = opayoffs[2], dd1 = opayoffs[3]) class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs #@staticmethod #def app_after_this_page(player, upcoming_apps): # if player.dropped_out: # return upcoming_apps[-1] class Results(Page): timeout_seconds = 20 form_model = 'player' @staticmethod def is_displayed(player: Player): return not player.other_out and not player.dropped_out and player.subsession.stillon and not other_player(player).dropped_out @staticmethod def vars_for_template(player: Player): opponent = other_player(player) return dict( opponent=opponent, same_choice=player.cooperate == opponent.cooperate, my_decision=player.field_display('cooperate'), opponent_decision=opponent.field_display('cooperate'), reshuffle=player.in_round(player.round_number + 1).subsession.reshuffle, ) class Next_round(Page): timeout_seconds = 20 form_model = 'player' @staticmethod def is_displayed(player: Player): return not player.other_out and not player.dropped_out and player.subsession.stillon and other_player(player).dropped_out class Results_dropped_out(Page): timeout_seconds = 10 #display only immediately after drop out @staticmethod def is_displayed(player: Player): return other_player(player).dropped_out and not player.other_out and player.subsession.stillon page_sequence = [StartWaitPage, NoMatch, DroppedOut, Introduction, NewMatch, NewPay, Decision, ResultsWaitPage, Results, Next_round, Results_dropped_out]