from otree.api import * doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'simple_bribe_v2' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 2 ENDOWMENT = 0 EXTERNALITY_AMOUNT = cu(3) MAX_PUNISHMENT = 42 OFFER_ROLE = 'Proposer' RESPONDER_ROLE = 'Responder' OFFER_ACCEPT = 85 OFFER_REJECT = 60 DONTOFFER_REJECT = 60 class Subsession(BaseSubsession): pass class Group(BaseGroup): ''' Most of the model objects are group objects so that some pages apply tosome players, not all. This is in reference to the trust game in the otree website. ''' offer = models.BooleanField( choices = [ [True, 'Offer'], [False, "Don't Offer"] ], label = 'Will you offer to collude?', widget = widgets.RadioSelect ) response = models.BooleanField( choices = [ [True, 'Accept'], [False, 'Reject'] ], label = 'Will you accept an offer to collude?', widget = widgets.RadioSelect ) class Player(BasePlayer): ''' A player's database fields relates to their beliefs: i) tokens_for_offered is the belief (probability) a player attach to his or her partner having offered to collude ii)tokens_for_notoffered is th belief a player attach to his or her partner having NOT offered to collude iii)token_payoff is 2ZAR times by the number of tokens placed correctly. For example if the parner offers to collude, and the responder attach probability 8 out of 10 tokens that her partner offered, then the responder payoff is 16 ZAR ''' token_payoff = models.IntegerField() tokens_for_offered = models.IntegerField( min=0, max=10, initial=0, label='My partner made an offer') tokens_for_notoffered = models.IntegerField( min=0, max=10, initial=0, label='My partner did not make an offer') #FUNCTIONS def get_payoffs_dict(): return { (True, True): C.OFFER_ACCEPT, (True, False): C.OFFER_REJECT, (False, True): C.DONTOFFER_REJECT, (False, False): C.DONTOFFER_REJECT } def creating_session(subsession): ''' The bribe game has two rounds. Suppose that the game has ten players. In the second round. In the first round, I let otree group the players using its algorithm. In the second round, I shuffle the players so that player 1 is matched with player 10, player 2 with 8, and so forth. ''' if subsession.round_number == 2: matrix = subsession.get_group_matrix() role_one_players = [row[0] for row in matrix] role_two_players = [row[1] for row in matrix] role_two_players.reverse() new_matrix = list(zip(role_two_players, role_one_players)) subsession.set_group_matrix(new_matrix) def set_payoffs(groups): ''' I set payoffs by using payoff_dict and then I reduce every other player's payoff, except the current proposer and responder by an externality amount. ''' externality_amount = min(C.EXTERNALITY_AMOUNT, int(C.MAX_PUNISHMENT / (len(groups) - 1))) payoffs_dict = get_payoffs_dict() for a_group in groups: players = a_group.get_players() a_player = players[0] other_player = players[1] if a_player.role == C.OFFER_ROLE: a_player.payoff += payoffs_dict[(a_player.group.offer, other_player.group.response)] other_player.payoff += payoffs_dict[(a_player.group.offer, other_player.group.response)] else: a_player.payoff += payoffs_dict[(other_player.group.offer, a_player.group.response)] other_player.payoff += payoffs_dict[(other_player.group.offer, a_player.group.response)] if a_group.offer and a_group.response: reduce_payoffs(a_group, groups, externality_amount) return def set_bribe_game_payoff(players): ''' I show all the payoffs in the last app of the sequence called 'results'. I make data to persist by setting the bribe game payoff into the participant field I call bribe_game_payoff. The bribe_game_payoff participant-field in stored in the settings file ''' for a_player in players: bribe_game_payoff_ls = [] for a_round in [1, 2]: bribe_game_payoff_ls.append(a_player.in_round(a_round).payoff) a_player.participant.bribe_game_payoff = bribe_game_payoff_ls def reduce_payoffs(curr_group, groups, externality_amount): ''' Whenever two players decide to collude, all other players suffers an externality. In this function, I implement this by reducing the payoff of all other players in all other groups except the current group. ''' for a_group in groups: if a_group != curr_group: players = a_group.get_players() for a_player in players: a_player.payoff -= externality_amount return def set_bribe_game_token_payoff(players): ''' I set the bribe game payoffs from the token so that it perists throughout all apps. In each two rounds of the game, I store the token_payoffs in a player's model. Then, at the end of the bribe game, I access those token payoffs and store them in a participant field bribe_game_token_payoff. This function sets the player's token payoff and immediately sets the particpant's payoff. ''' for a_player in players: for a_round in [1, 2]: other_player = a_player.get_others_in_group()[0] if a_player.role == C.RESPONDER_ROLE: if a_player.group.offer: a_player.token_payoff = 2 * a_player.tokens_for_offered a_player.participant.bribe_game_token_payoff = a_player.token_payoff else: a_player.token_payoff = 2 * a_player.tokens_for_notoffered a_player.participant.bribe_game_token_payoff = a_player.token_payoff # PAGES class Offer(Page): ''' I define the form_model to be group because I would like to hide some pages from some players. Group works for ths sort of behaviour. ''' form_model = 'group' form_fields = ['offer'] @staticmethod def is_displayed(player): return player.role == C.OFFER_ROLE class OfferWaitPage(WaitPage): pass class Response(Page): form_model = 'group' form_fields = ['response'] @staticmethod def is_displayed(player): return player.role == C.RESPONDER_ROLE class ResponseWaitPage(WaitPage): pass class ResultsWaitPage(WaitPage): wait_for_all_groups = True @staticmethod def after_all_players_arrive(subsession: Subsession): groups = subsession.get_groups() players = subsession.get_players() set_payoffs(groups) set_bribe_game_token_payoff(players) #set participant offer and response fields for a_player in players: participant = a_player.participant if a_player.role == C.OFFER_ROLE: participant.offer = a_player.group.offer else: participant.response = a_player.group.response if subsession.round_number == 2: set_bribe_game_payoff(players) class Beliefs(Page): form_model = 'player' form_fields = ['tokens_for_offered', 'tokens_for_notoffered' ] def is_displayed(player: Player): return player.role == C.RESPONDER_ROLE @staticmethod def error_message(player, values): # The number of distributed tokens should sum up to 10. if values['tokens_for_offered'] + values['tokens_for_notoffered'] != 10: return 'The tokens should sum up to 10' page_sequence = [Offer, ResponseWaitPage, Response, OfferWaitPage, Beliefs, ResultsWaitPage, ]