import random from otree.api import * doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'SN' PLAYERS_PER_GROUP = 4 NUM_ROUNDS = 16 FINE_PROB = .15 OWNER_ROLE = 'Owner' MANAGER_ROLE = 'Manager' FIRM1_ROLE = 'Firm1' FIRM2_ROLE = 'Firm2' PP = 'P and P' PR = 'P and R' RP = 'R and P' RR = 'R and R' AA = 'A and A' AB = 'A and B' BA = 'B and A' BB = 'B and B' OPPAA = cu(1250) OPPAB = cu(925) OPPBA = cu(1406) OPPBB = cu(1111) OPRAA = cu(1125) OPRAB = cu(702) OPRBA = cu(1225) OPRBB = cu(630) ORPAA = cu(1350) ORPAB = cu(1050) ORPBA = cu(1222) ORPBB = cu(846) ORRAA = cu(1200) ORRAB = cu(750) ORRBA = cu(1125) ORRBB = cu(800) MPPAA = cu(125) MPPAB = cu(93) MPPBA = cu(141) MPPBB = cu(111) MPRAA = cu(138) MPRAB = cu(95) MPRBA = cu(148) MPRBB = cu(88) MRPAA = cu(124) MRPAB = cu(94) MRPBA = cu(145) MRPBB = cu(107) MRRAA = cu(128) MRRAB = cu(83) MRRBA = cu(150) MRRBB = cu(108) OFPPAA = cu(225) OFPRAA = cu(213) OFRPAA = cu(255) OFRRAA = cu(240) MFPPAA = cu(23) MFPRAA = cu(26) MFRPAA = cu(23) MFRRAA = cu(26) NOFINE = cu(0) # Need to set up payoffs for owners and managers # # Need to set up fine for owners and managers # class Subsession(BaseSubsession): def creating_session(self): if self.round_number == 1: self.group_randomly() for g in self.get_groups(): for p in g.get_players(): if p.id_in_group == 1: p.type = C.OWNER_ROLE p.participant.vars['type'] = C.OWNER_ROLE p.firm = "1" elif p.id_in_group == 2: p.type = C.MANAGER_ROLE p.participant.vars['type'] = C.MANAGER_ROLE p.firm = "1" elif p.id_in_group == 3: p.type = C.OWNER_ROLE p.participant.vars['type'] = C.OWNER_ROLE p.firm = "2" elif p.id_in_group == 4: p.type = C.MANAGER_ROLE p.participant.vars['type'] = C.MANAGER_ROLE p.firm = "2" p.type = p.participant.vars['type'] else: self.group_like_round(1) # class Subsession(BaseSubsession): # pass # def creating_session(subsession: Subsession): # players = subsession.get_players() # owners: list[Player] = list() # managers: list[Player] = list() # for player in players: # if 'type' not in player.participant.vars: # player.participant.vars['type'] = C.OWNER_ROLE if player.id_in_subsession % 2 == 1 else C.MANAGER_ROLE # # if player.participant.vars['type'] == C.OWNER_ROLE: # owners.append(player) # else: # managers.append(player) # # # owners = [player for player in players if player.participant.vars['type'] == C.OWNER_ROLE] # random.shuffle(owners) # random.shuffle(managers) # # matrix = list() # while len(owners) > 0: # matrix.append([owners.pop().id_in_subsession, managers.pop().id_in_subsession, owners.pop().id_in_subsession, managers.pop().id_in_subsession]) # # # Setting the matrix # subsession.set_group_matrix(matrix) class Group(BaseGroup): # TODO save whether there was a fine in the group pass class Player(BasePlayer): firm = models.StringField() type = models.StringField() earnings = models.CurrencyField() fine = models.CurrencyField() contract = models.StringField() dollars = models.FloatField() other_owner = models.StringField() other_manager = models.StringField() owner_decision = models.BooleanField( choices=[[True, 'P'], [False, 'R']], label="What is your decision?", widget=widgets.RadioSelectHorizontal, ) manager_decision = models.BooleanField( choices=[[True, 'A'], [False, 'B']], label="What is your decision?", widget=widgets.RadioSelectHorizontal, ) # FUNCTIONS def custom_export(players): # header row yield ['session', 'participant_code', 'round_number', 'id_in_group', 'payoff'] for p in players: participant = p.participant session = p.session yield [session.code, participant.code, p.round_number, p.id_in_group, p.payoff] # Need to define payoffs according to owners' and managers' decisions # def same_owner(player: Player): other_players = player.get_others_in_group() for other_player in other_players: if other_player.type == C.OWNER_ROLE and other_player.firm == player.firm: return other_player def other_owner(player: Player): other_players = player.get_others_in_group() for other_player in other_players: if other_player.type == C.OWNER_ROLE and other_player.firm != player.firm: return other_player def same_manager(player: Player): other_players = player.get_others_in_group() for other_player in other_players: if other_player.type == C.MANAGER_ROLE and other_player.firm == player.firm: return other_player def other_manager(player: Player): other_players = player.get_others_in_group() for other_player in other_players: if other_player.type == C.MANAGER_ROLE and other_player.firm != player.firm: return other_player def competitor(player: Player): compete_players = player.get_others_in_group() for comp in compete_players: if comp.type == player.type and comp.firm != player.firm: return comp def myfirm_member(player: Player): compete_players = player.get_others_in_group() for comp in compete_players: if comp.firm == player.firm and comp.type != player.type: return comp def oppositefirm_member(player: Player): compete_players = player.get_others_in_group() for comp in compete_players: if comp.type != player.type and comp.firm != player.firm: return comp def set_owners_contract_combination(player: Player): owners_contract_combination_matrix = { (False, True): C.RP, (True, True): C.PP, (False, False): C.RR, (True, False): C.PR, } same_o = same_owner(player) other_o = other_owner(player) player.contract = owners_contract_combination_matrix[(same_o.owner_decision, other_o.owner_decision)] return player.contract def set_managers_decision_combination(player: Player): managers_decision_combination_matrix = { (False, True): C.BA, (True, True): C.AA, (False, False): C.BB, (True, False): C.AB, } same_m = same_manager(player) other_m = other_manager(player) player.decision = managers_decision_combination_matrix[(same_m.manager_decision, other_m.manager_decision)] def set_earnings(group: Group): players = group.get_players() should_fine_this_round = should_fine() for p in players: if p.type == C.OWNER_ROLE: earnings = calculate_ownerearnings(p) fine = calculate_ownerfine(p) if should_fine_this_round else cu(0) else: earnings = calculate_managerearnings(p) fine = calculate_managerfine(p) if should_fine_this_round else cu(0) # Save current round payoff in player model p.earnings = earnings p.fine = fine p.payoff = earnings - fine # Save history in participant p.participant.vars['earnings'] = p.participant.vars.get('earnings', list()) p.participant.vars['earnings'].append(earnings) p.participant.vars['fines'] = p.participant.vars.get('fines', list()) p.participant.vars['fines'].append(fine) def calculate_ownerearnings(player: Player): ownerearnings_matrix = { (True, True, True, True): C.OPPAA, (True, True, True, False): C.OPPAB, (True, True, False, True): C.OPPBA, (True, True, False, False): C.OPPBB, (True, False, True, True): C.OPRAA, (True, False, True, False): C.OPRAB, (True, False, False, True): C.OPRBA, (True, False, False, False): C.OPRBB, (False, True, True, True): C.ORPAA, (False, True, True, False): C.ORPAB, (False, True, False, True): C.ORPBA, (False, True, False, False): C.ORPBB, (False, False, True, True): C.ORRAA, (False, False, True, False): C.ORRAB, (False, False, False, True): C.ORRBA, (False, False, False, False): C.ORRBB, } other_o = other_owner(player) same_m = same_manager(player) other_m = other_manager(player) return ownerearnings_matrix[(player.owner_decision, other_o.owner_decision, same_m.manager_decision, other_m.manager_decision)] def calculate_managerearnings(player: Player): managerearnings_matrix = { (True, True, True, True): C.MPPAA, (True, True, True, False): C.MPPAB, (True, True, False, True): C.MPPBA, (True, True, False, False): C.MPPBB, (True, False, True, True): C.MPRAA, (True, False, True, False): C.MPRAB, (True, False, False, True): C.MPRBA, (True, False, False, False): C.MPRBB, (False, True, True, True): C.MRPAA, (False, True, True, False): C.MRPAB, (False, True, False, True): C.MRPBA, (False, True, False, False): C.MRPBB, (False, False, True, True): C.MRRAA, (False, False, True, False): C.MRRAB, (False, False, False, True): C.MRRBA, (False, False, False, False): C.MRRBB, } other_o = other_owner(player) same_o = same_owner(player) other_m = other_manager(player) return managerearnings_matrix[(same_o.owner_decision, other_o.owner_decision, player.manager_decision, other_m.manager_decision)] def should_fine(): return random.random() < C.FINE_PROB def calculate_ownerfine(player: Player): ownerfine_matrix = { (True, True, True, True): C.OFPPAA, (True, True, True, False): C.NOFINE, (True, True, False, True): C.NOFINE, (True, True, False, False): C.NOFINE, (True, False, True, True): C.OFPRAA, (True, False, True, False): C.NOFINE, (True, False, False, True): C.NOFINE, (True, False, False, False): C.NOFINE, (False, True, True, True): C.OFRPAA, (False, True, True, False): C.NOFINE, (False, True, False, True): C.NOFINE, (False, True, False, False): C.NOFINE, (False, False, True, True): C.OFRRAA, (False, False, True, False): C.NOFINE, (False, False, False, True): C.NOFINE, (False, False, False, False): C.NOFINE, } other_o = other_owner(player) same_m = same_manager(player) other_m = other_manager(player) return ownerfine_matrix[(player.owner_decision, other_o.owner_decision, same_m.manager_decision, other_m.manager_decision)] def calculate_managerfine(player: Player): managerfine_matrix = { (True, True, True, True): C.MFPPAA, (True, True, True, False): C.NOFINE, (True, True, False, True): C.NOFINE, (True, True, False, False): C.NOFINE, (True, False, True, True): C.MFPRAA, (True, False, True, False): C.NOFINE, (True, False, False, True): C.NOFINE, (True, False, False, False): C.NOFINE, (False, True, True, True): C.MFRPAA, (False, True, True, False): C.NOFINE, (False, True, False, True): C.NOFINE, (False, True, False, False): C.NOFINE, (False, False, True, True): C.MFRRAA, (False, False, True, False): C.NOFINE, (False, False, False, True): C.NOFINE, (False, False, False, False): C.NOFINE, } other_o = other_owner(player) same_o = same_owner(player) other_m = other_manager(player) return managerfine_matrix[(same_o.owner_decision, other_o.owner_decision, player.manager_decision, other_m.manager_decision)] # PAGES class Instruction1(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Instruction2(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class MyWaitPage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): pass class ResultsWaitPage(WaitPage): after_all_players_arrive = 'set_earnings' class Roundst(Page): @staticmethod def after_all_players_arrive(group: Group): pass class Stage1(Page): form_model = 'player' form_fields = ['owner_decision'] @staticmethod def is_displayed(player: Player): if player.id_in_group == 1: player.type = C.OWNER_ROLE player.participant.vars['type'] = C.OWNER_ROLE player.firm = "1" elif player.id_in_group == 2: player.type = C.MANAGER_ROLE player.participant.vars['type'] = C.MANAGER_ROLE player.firm = "1" elif player.id_in_group == 3: player.type = C.OWNER_ROLE player.participant.vars['type'] = C.OWNER_ROLE player.firm = "2" elif player.id_in_group == 4: player.type = C.MANAGER_ROLE player.participant.vars['type'] = C.MANAGER_ROLE player.firm = "2" return player.type == C.OWNER_ROLE @staticmethod def vars_for_template(player: Player): player.type = player.type player.firm = player.firm return dict( type=player.type, firm=player.firm ) # class Chat(Page): # timeout_seconds = 60 # form_model = 'player' # # @staticmethod # def is_displayed(player: Player): # return player.type == C.MANAGER_ROLE # # @staticmethod # def vars_for_template(player: Player): # chosencontract = set_owners_contract_combination(player) # player.type = player.type # player.firm = player.firm # return dict(chosencontract=chosencontract, # PP=C.PP, # PR=C.PR, # RP=C.RP, # RR=C.RR, # MFPPAA=C.MFPPAA, # MFPRAA=C.MFPRAA, # MFRPAA=C.MFRPAA, # MFRRAA=C.MFRRAA, # type=player.type, # firm=player.firm # ) class Stage2(Page): form_model = 'player' form_fields = ['manager_decision'] @staticmethod def is_displayed(player: Player): return player.type == C.MANAGER_ROLE @staticmethod def vars_for_template(player: Player): chosencontract = set_owners_contract_combination(player) player.type = player.type player.firm = player.firm return dict(chosencontract=chosencontract, PP=C.PP, PR=C.PR, RP=C.RP, RR=C.RR, type=player.type, firm=player.firm ) class RoundResults(Page): @staticmethod def vars_for_template(player: Player): history = list() all_earnings = player.participant.vars['earnings'] all_fines = player.participant.vars['fines'] player.type = player.type player.firm = player.firm compete = competitor(player) myfirm_mem = myfirm_member(player) opposite_mem = oppositefirm_member(player) com_earning = compete.participant.vars['earnings'] com_fine = compete.participant.vars['fines'] myfirm_mem_earning = myfirm_mem.participant.vars['earnings'] myfirm_mem_fine = myfirm_mem.participant.vars['fines'] opposite_mem_earning = opposite_mem.participant.vars['earnings'] opposite_mem_fine = opposite_mem.participant.vars['fines'] for index, earnings in enumerate(all_earnings): fine = all_fines[index] com_earnings = com_earning[index] com_fines = com_fine[index] myfirm_mem_earnings = myfirm_mem_earning[index] myfirm_mem_fines = myfirm_mem_fine[index] opposite_mem_earnings = opposite_mem_earning[index] opposite_mem_fines = opposite_mem_fine[index] history.append(dict( round=index + 1, earnings=earnings, fine=fine, payoff=earnings-fine, com_earnings=com_earnings, myfirm_mem_earnings=myfirm_mem_earnings, opposite_mem_earnings=opposite_mem_earnings, com_fines=com_fines, myfirm_mem_fines=myfirm_mem_fines, opposite_mem_fines=opposite_mem_fines, com_payoffs=com_earnings-com_fines, myfirm_mem_payoffs=myfirm_mem_earnings-myfirm_mem_fines, opposite_mem_payoffs=opposite_mem_earnings-opposite_mem_fines, )) return dict( history=history, total_payoff=player.participant.payoff, total_fine=sum(all_fines), total_earnings=sum(all_earnings), type=player.type, competitor=compete.type, myfirm_mem_type=myfirm_mem.type, opposite_mem_type=opposite_mem.type, firm=player.firm, com_firm=compete.firm, myfirm_mem_firm=myfirm_mem.firm, opposite_mem_firm=opposite_mem.firm, total_com_payoffs=compete.participant.payoff, total_myfirm_mem_payoffs=myfirm_mem.participant.payoff, total_opposite_mem_payoffs=opposite_mem.participant.payoff ) class FinalResults(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 16 @staticmethod def vars_for_template(player: Player): if player.type == C.OWNER_ROLE: reward_dollars = player.participant.payoff.to_real_world_currency(player.session) * 0.5 else: reward_dollars = player.participant.payoff.to_real_world_currency(player.session) * 5 return dict( reward=reward_dollars, rewardandshowupfee=reward_dollars + 5, type=player.type, owner=C.OWNER_ROLE, manager=C.MANAGER_ROLE ) # Instruction1, Instruction2, page_sequence = [Instruction1, Instruction2, MyWaitPage, Roundst, Stage1, MyWaitPage, Stage2, ResultsWaitPage, RoundResults, FinalResults]