from otree.api import * import random doc = """ Whistleblowing game """ class C(BaseConstants): NAME_IN_URL = 'whistle' PLAYERS_PER_GROUP = 3 NUM_ROUNDS = 12 MANAGER_FIXED = 120 TASK_DURATION = 120 TASK_TOTAL = 6 MINIMUM_CORRECT = 3 EMPLOYEE_BONUS = 20 EMPLOYEE_FIRM = 10 PUBLIC_PASSIVE = 140 PUBLIC_LOSS = 20 EMPLOYEE_WHISTLE_COST = 25 MANAGER_WHISTLE_COST = 70 MULTIPLIER_MANAGER = 2 MULTIPLIER_WIN = 1.5 MULTIPLIER_LOSE = 0.75 class Subsession(BaseSubsession): cheaters = models.IntegerField(initial=0) whistlers = models.IntegerField(initial=0) tie = models.BooleanField(initial=False) class Group(BaseGroup): role_group = models.StringField() output_employees = models.FloatField(initial=0.0) output_firm = models.FloatField(initial=0.0) surplus_firm = models.FloatField(initial=0.0) winning_firm = models.IntegerField(initial=0) class Player(BasePlayer): role_player = models.StringField() chosen = models.IntegerField(initial=0) correct = models.IntegerField(initial=0) cheat = models.IntegerField( label="Please make your decision.", choices=[[0, 'Do the multiplication task'], [1, 'Break the law']], widget=widgets.RadioSelectHorizontal ) whistle = models.IntegerField( label="If my manager broke the law,", choices=[[1, 'Blow the whistle'], [0, 'Not blow the whistle']], widget=widgets.RadioSelectHorizontal ) # FUNCTIONS def creating_session(subsession): # Fix group matching for every six rounds new_structure1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12, 13, 14, 15]] new_structure2 = [[1, 10, 11], [4, 12, 13], [7, 14, 15], [2, 3, 5, 6, 8, 9]] new_structure3 = [[1, 8, 9], [4, 2, 3], [7, 5, 6], [10, 11, 12, 13, 14, 15]] new_structure4 = [[1, 14, 15], [4, 10, 11], [7, 12, 13], [2, 3, 5, 6, 8, 9]] new_structure5 = [[1, 5, 6], [4, 8, 9], [7, 2, 3], [10, 11, 12, 13, 14, 15]] new_structure6 = [[1, 12, 13], [4, 14, 15], [7, 10, 11], [2, 3, 5, 6, 8, 9]] if subsession.round_number % C.NUM_ROUNDS == 1 or subsession.round_number % C.NUM_ROUNDS == 7: subsession.set_group_matrix(new_structure1) elif subsession.round_number % C.NUM_ROUNDS == 2 or subsession.round_number % C.NUM_ROUNDS == 8: subsession.set_group_matrix(new_structure2) elif subsession.round_number % C.NUM_ROUNDS == 3 or subsession.round_number % C.NUM_ROUNDS == 9: subsession.set_group_matrix(new_structure3) elif subsession.round_number % C.NUM_ROUNDS == 4 or subsession.round_number % C.NUM_ROUNDS == 10: subsession.set_group_matrix(new_structure4) elif subsession.round_number % C.NUM_ROUNDS == 5 or subsession.round_number % C.NUM_ROUNDS == 11: subsession.set_group_matrix(new_structure5) elif subsession.round_number % C.NUM_ROUNDS == 6 or subsession.round_number % C.NUM_ROUNDS == 0: subsession.set_group_matrix(new_structure6) # Set roles of groups and participants for p in subsession.get_players(): if p.group.id_in_subsession <= 3: p.group.role_group = 'firm' if p.id_in_group == 1: p.role_player = 'manager' else: p.role_player = 'employee' else: p.group.role_group = 'public' p.role_player = 'public' # Determine which employee is chosen to implement whistle decision for g in subsession.get_groups(): chosen = random.randint(2, 3) for p in g.get_players(): if p.role_player == 'employee' and p.id_in_group == chosen: p.chosen = 1 # Determining winning firm if treatment is random if subsession.session.config['treatment_random'] is True: winner = random.randint(1, 3) for g in subsession.get_groups(): if g.id_in_subsession == winner: g.winning_firm = 1 def compute_outputs(group: Group): if group.role_group == 'firm': m = group.get_player_by_id(1) e2 = group.get_player_by_id(2) e3 = group.get_player_by_id(3) group.output_employees = e2.correct + e3.correct if m.cheat == 1 or m.correct >= C.MINIMUM_CORRECT: group.output_firm = C.MULTIPLIER_MANAGER * group.output_employees else: group.output_firm = group.output_employees def determine_winner(subsession): [g1, g2, g3, g4] = subsession.get_groups() max_output = max(g1.output_firm, g2.output_firm, g3.output_firm) winners = (g1.output_firm == max_output) + (g2.output_firm == max_output) + (g3.output_firm == max_output) if winners == 1: for g in subsession.get_groups(): if g.role_group == 'firm' and g.output_firm == max_output: g.winning_firm = 1 elif winners == 2: subsession.tie = 1 if g1.output_firm == max_output and g2.output_firm == max_output: win_tie = random.randint(1, 2) if g1.output_firm == max_output and g1.id_in_subsession == win_tie: g1.winning_firm = 1 else: g2.winning_firm = 1 elif g1.output_firm == max_output and g3.output_firm == max_output: win_tie = random.randint(1, 2) if g1.output_firm == max_output and g1.id_in_subsession == win_tie: g1.winning_firm = 1 else: g3.winning_firm = 1 elif g2.output_firm == max_output and g3.output_firm == max_output: win_tie = random.randint(2, 3) if g2.output_firm == max_output and g2.id_in_subsession == win_tie: g2.winning_firm = 1 else: g3.winning_firm = 1 elif winners == 3: subsession.tie = 1 win_tie = random.randint(1, 3) if g1.output_firm == max_output and g1.id_in_subsession == win_tie: g1.winning_firm = 1 elif g2.output_firm == max_output and g2.id_in_subsession == win_tie: g2.winning_firm = 1 else: g3.winning_firm = 1 def compute_surpluses(group: Group): if group.role_group == 'firm': if group.session.config['treatment_competition'] is False and group.session.config['treatment_random'] is False: group.surplus_firm = C.EMPLOYEE_FIRM * group.output_firm else: if group.winning_firm == 1: group.surplus_firm = C.MULTIPLIER_WIN * C.EMPLOYEE_FIRM * group.output_firm else: group.surplus_firm = C.MULTIPLIER_LOSE * C.EMPLOYEE_FIRM * group.output_firm def set_round_payoffs(group: Group): # Count number of cheaters in round group.subsession.cheaters = 0 for p in group.subsession.get_players(): if p.role_player == 'manager': p.whistle = 0 if p.cheat == 1: p.subsession.cheaters = p.subsession.cheaters + 1 # Count number of whistleblowers in round group.subsession.whistlers = 0 for p in group.subsession.get_players(): if p.role_player == 'employee': p.cheat = 0 if p.whistle == 1: p.subsession.whistlers = p.subsession.whistlers + 1 # Set payoffs for members of the public if group.role_group == 'public': for p in group.get_players(): p.payoff = C.PUBLIC_PASSIVE - group.subsession.cheaters * C.PUBLIC_LOSS p.whistle = 0 p.cheat = 0 # Set payoffs for members of the firm elif group.role_group == 'firm': m = group.get_player_by_id(1) e2 = group.get_player_by_id(2) e3 = group.get_player_by_id(3) m.payoff = C.MANAGER_FIXED + 0.50 * group.surplus_firm - C.MANAGER_WHISTLE_COST * (e2.whistle * e2.chosen + e3.whistle * e3.chosen) * m.cheat e2.payoff = C.EMPLOYEE_BONUS * e2.correct + 0.25 * group.surplus_firm - C.EMPLOYEE_WHISTLE_COST * e2.whistle * e2.chosen * m.cheat e3.payoff = C.EMPLOYEE_BONUS * e3.correct + 0.25 * group.surplus_firm - C.EMPLOYEE_WHISTLE_COST * e3.whistle * e3.chosen * m.cheat def store_game_payoffs(subsession): for p in subsession.get_players(): random.seed(p.round_number) r1 = random.randrange(1, 7, 2) r2 = random.randrange(2, 8, 2) r3 = random.randrange(7, 13, 2) r4 = random.randrange(8, 14, 2) p.participant.vars['payoff_game'] = p.in_round(r1).payoff + p.in_round(r2).payoff + p.in_round(r3).payoff + p.in_round(r4).payoff def store_for_survey(subsession): # Store total number of cheating managers and whistleblowing employees subsession.session.vars['total_cheaters'] = 0 subsession.session.vars['total_whistlers'] = 0 for i in range(1, C.NUM_ROUNDS + 1): subsession.session.vars['total_cheaters'] = subsession.in_round(i).cheaters + subsession.session.vars['total_cheaters'] subsession.session.vars['total_whistlers'] = subsession.in_round(i).whistlers + subsession.session.vars['total_whistlers'] # PAGES class InstructionsWelcome(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class InstructionsRoles(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class InstructionsTasks(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class InstructionsEarnings(Page): @staticmethod def is_displayed(player): return player.round_number == 1 @staticmethod def vars_for_template(player): treatment_random = player.session.config['treatment_random'] treatment_competition = player.session.config['treatment_competition'] return dict( random=treatment_random, competition=treatment_competition ) class InstructionsExample(Page): @staticmethod def is_displayed(player): return player.round_number == 1 @staticmethod def vars_for_template(player): treatment_random = player.session.config['treatment_random'] treatment_competition = player.session.config['treatment_competition'] return dict( random=treatment_random, competition=treatment_competition ) class QuestionsBaseline(Page): @staticmethod def is_displayed(player): return player.round_number == 1 and player.session.config['treatment_competition'] is False and player.session.config['treatment_random'] is False class QuestionsRandom(Page): @staticmethod def is_displayed(player): return player.round_number == 1 and player.session.config['treatment_random'] is True class QuestionsCompetition(Page): @staticmethod def is_displayed(player): return player.round_number == 1 and player.session.config['treatment_competition'] is True class WaitQuestions(WaitPage): title_text = "Please wait" body_text = "Waiting for all participants to answer the questions." wait_for_all_groups = True @staticmethod def is_displayed(player): return player.round_number == 1 class ManagerDecision(Page): form_model = 'player' form_fields = ['cheat'] @staticmethod def is_displayed(player: Player): return player.role_player == 'manager' class EmployeeWaitPage(WaitPage): title_text = "Please wait" body_text = "Please wait for the manager to make their decision." @staticmethod def is_displayed(player): return player.role_player == 'employee' class EmployeeTask(Page): timeout_seconds = C.TASK_DURATION form_model = 'player' form_fields = ['correct'] @staticmethod def is_displayed(player): return player.role_player == 'employee' @staticmethod def vars_for_template(player): random.seed(player.round_number) num1_list = [] num2_list = [] sum_list = [] prod_list = [] for i in range(C.TASK_TOTAL): num1 = random.randint(10, 99) num2 = random.randint(10, 99) num1_list.append(num1) num2_list.append(num2) sum_list.append(num1+num2) prod_list.append(num1 * num2) return dict( num1_1=num1_list[0], num1_2=num1_list[1], num1_3=num1_list[2], num1_4=num1_list[3], num1_5=num1_list[4], num1_6=num1_list[5], num2_1=num2_list[0], num2_2=num2_list[1], num2_3=num2_list[2], num2_4=num2_list[3], num2_5=num2_list[4], num2_6=num2_list[5], ) class ManagerTask(Page): timeout_seconds = C.TASK_DURATION form_model = 'player' form_fields = ['correct'] @staticmethod def is_displayed(player): return player.role_player == 'manager' and player.cheat == 0 @staticmethod def vars_for_template(player): random.seed(player.round_number) num1_list = [] num2_list = [] sum_list = [] prod_list = [] for i in range(C.TASK_TOTAL): num1 = random.randint(10, 99) num2 = random.randint(10, 99) num1_list.append(num1) num2_list.append(num2) sum_list.append(num1+num2) prod_list.append(num1 * num2) return dict( num1_1=num1_list[0], num1_2=num1_list[1], num1_3=num1_list[2], num1_4=num1_list[3], num1_5=num1_list[4], num1_6=num1_list[5], num2_1=num2_list[0], num2_2=num2_list[1], num2_3=num2_list[2], num2_4=num2_list[3], num2_5=num2_list[4], num2_6=num2_list[5], ) class EmployeeDecision(Page): form_model = 'player' form_fields = ['whistle'] @staticmethod def is_displayed(player: Player): return player.role_player == 'employee' class ManagerWaitPage(WaitPage): title_text = "Please wait" body_text = "Please wait for the employees to make their decision." @staticmethod def is_displayed(player): return player.role_player == 'manager' class PublicWaitPage(WaitPage): title_text = "You are a member of the public in this round." body_text = "Please wait for the firm members to make their decisions." @staticmethod def is_displayed(player): return player.role_player == 'public' class OutputWaitPage(WaitPage): title_text = "Please wait" body_text = "Earnings are calculated." def after_all_players_arrive(group: Group): compute_outputs(group) class WinnerWaitPage(WaitPage): title_text = "Please wait" body_text = "Earnings are calculated." wait_for_all_groups = True @staticmethod def after_all_players_arrive(subsession): determine_winner(subsession) @staticmethod def is_displayed(player): return player.session.config['treatment_competition'] class ResultsWaitPage(WaitPage): title_text = "Please wait" body_text = "Earnings are calculated." def after_all_players_arrive(group: Group): compute_surpluses(group) set_round_payoffs(group) class WaitRound(WaitPage): title_text = "Please wait" body_text = "Waiting for next round to begin." wait_for_all_groups = True @staticmethod def is_displayed(player): return player.round_number < C.NUM_ROUNDS class Feedback(Page): @staticmethod def vars_for_template(player): treatment_random = player.session.config['treatment_random'] treatment_competition = player.session.config['treatment_competition'] return dict( random=treatment_random, competition=treatment_competition ) class ResultsCalculationPage(WaitPage): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS class StoreSurveyWaitPage(WaitPage): title_text = "Please wait" wait_for_all_groups = True @staticmethod def after_all_players_arrive(subsession): store_for_survey(subsession) store_game_payoffs(subsession) @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS class ResultsManager(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS and player.role_player == 'manager' @staticmethod def vars_for_template(player): payoff_game = player.participant.vars['payoff_game'] treatment_random = player.session.config['treatment_random'] treatment_competition = player.session.config['treatment_competition'] random.seed(player.round_number) round1 = random.randrange(1, 7, 2) round2 = random.randrange(2, 8, 2) round3 = random.randrange(7, 13, 2) round4 = random.randrange(8, 14, 2) payoff1 = player.in_round(round1).payoff payoff2 = player.in_round(round2).payoff payoff3 = player.in_round(round3).payoff payoff4 = player.in_round(round4).payoff cheat1 = player.in_round(round1).cheat cheat2 = player.in_round(round2).cheat cheat3 = player.in_round(round3).cheat cheat4 = player.in_round(round4).cheat whistle1 = player.in_round(round1).whistle whistle2 = player.in_round(round2).whistle whistle3 = player.in_round(round3).whistle whistle4 = player.in_round(round4).whistle surplus1 = player.group.in_round(round1).surplus_firm surplus2 = player.group.in_round(round2).surplus_firm surplus3 = player.group.in_round(round3).surplus_firm surplus4 = player.group.in_round(round4).surplus_firm winning1 = player.group.in_round(round1).winning_firm winning2 = player.group.in_round(round2).winning_firm winning3 = player.group.in_round(round3).winning_firm winning4 = player.group.in_round(round4).winning_firm correct1 = player.in_round(round1).correct correct2 = player.in_round(round2).correct correct3 = player.in_round(round3).correct correct4 = player.in_round(round4).correct ties = player.subsession.tie return dict( payoff_game=payoff_game, random=treatment_random, ties=ties, competition=treatment_competition, round1=round1, round2=round2, round3=round3, round4=round4, payoff1=payoff1, payoff2=payoff2, payoff3=payoff3, payoff4=payoff4, cheat1=cheat1, cheat2=cheat2, cheat3=cheat3, cheat4=cheat4, correct1=correct1, correct2=correct2, correct3=correct3, correct4=correct4, whistle1=whistle1, whistle2=whistle2, whistle3=whistle3, whistle4=whistle4, winning1=winning1, winning2=winning2, winning3=winning3, winning4=winning4, surplus1=surplus1, surplus2=surplus2, surplus3=surplus3, surplus4=surplus4 ) class ResultsEmployee(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS and player.role_player != 'manager' @staticmethod def vars_for_template(player): payoff_game = player.participant.vars['payoff_game'] treatment_random = player.session.config['treatment_random'] treatment_competition = player.session.config['treatment_competition'] random.seed(player.round_number) round1 = random.randrange(1, 7, 2) round2 = random.randrange(2, 8, 2) round3 = random.randrange(7, 13, 2) round4 = random.randrange(8, 14, 2) payoff1 = player.in_round(round1).payoff payoff2 = player.in_round(round2).payoff payoff3 = player.in_round(round3).payoff payoff4 = player.in_round(round4).payoff whistle1 = player.in_round(round1).whistle whistle2 = player.in_round(round2).whistle whistle3 = player.in_round(round3).whistle whistle4 = player.in_round(round4).whistle role1 = player.in_round(round1).role_player role2 = player.in_round(round2).role_player role3 = player.in_round(round3).role_player role4 = player.in_round(round4).role_player chosen1 = player.in_round(round1).chosen chosen2 = player.in_round(round2).chosen chosen3 = player.in_round(round3).chosen chosen4 = player.in_round(round4).chosen correct1 = player.in_round(round1).correct correct2 = player.in_round(round2).correct correct3 = player.in_round(round3).correct correct4 = player.in_round(round4).correct cheaters1 = player.subsession.in_round(round1).cheaters cheaters2 = player.subsession.in_round(round2).cheaters cheaters3 = player.subsession.in_round(round3).cheaters cheaters4 = player.subsession.in_round(round4).cheaters surplus1 = player.group.in_round(round1).surplus_firm surplus2 = player.group.in_round(round2).surplus_firm surplus3 = player.group.in_round(round3).surplus_firm surplus4 = player.group.in_round(round4).surplus_firm winning1 = player.group.in_round(round1).winning_firm winning2 = player.group.in_round(round2).winning_firm winning3 = player.group.in_round(round3).winning_firm winning4 = player.group.in_round(round4).winning_firm ties = player.subsession.tie return dict( payoff_game=payoff_game, random=treatment_random, ties=ties, competition=treatment_competition, round1=round1, round2=round2, round3=round3, round4=round4, payoff1=payoff1, payoff2=payoff2, payoff3=payoff3, payoff4=payoff4, chosen1=chosen1, chosen2=chosen2, chosen3=chosen3, chosen4=chosen4, role1=role1, role2=role2, role3=role3, role4=role4, cheaters1=cheaters1, cheaters2=cheaters2, cheaters3=cheaters3, cheaters4=cheaters4, correct1=correct1, correct2=correct2, correct3=correct3, correct4=correct4, whistle1=whistle1, whistle2=whistle2, whistle3=whistle3, whistle4=whistle4, winning1=winning1, winning2=winning2, winning3=winning3, winning4=winning4, surplus1=surplus1, surplus2=surplus2, surplus3=surplus3, surplus4=surplus4 ) page_sequence = [ InstructionsWelcome, InstructionsRoles, InstructionsTasks, InstructionsEarnings, # InstructionsExample, QuestionsBaseline, QuestionsRandom, QuestionsCompetition, WaitQuestions, ManagerDecision, EmployeeWaitPage, EmployeeTask, ManagerTask, EmployeeDecision, ManagerWaitPage, PublicWaitPage, WaitRound, OutputWaitPage, WinnerWaitPage, ResultsWaitPage, Feedback, ResultsCalculationPage, StoreSurveyWaitPage, # ResultsManager, # ResultsEmployee ]