import random from otree.api import * MAX = 100 MIN = 25 cue_quality_possible_values = [10, 20, 30, 40, 50, 60, 70, 80, 90] MAX_INVESTIGATIONS = 2 class C(BaseConstants): NAME_IN_URL = 'survey' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 30 class Subsession(BaseSubsession): whistleblowing_actual = models.IntegerField(initial=0) pass class Group(BaseGroup): pass class Player(BasePlayer): cue_id = models.IntegerField(initial=0) cue_quality = models.IntegerField(initial=0, choices=cue_quality_possible_values) actual_cue_misreported = models.BooleanField(initial=False) cue_misreported = models.BooleanField(initial=False) misreported_display_line = models.StringField(initial="") first_random_number = models.FloatField(initial=0) second_random_number = models.FloatField(initial=0) number_received = models.IntegerField(initial=0) number_reported = models.IntegerField(label='', min=MIN, max=MAX) misreported = models.BooleanField(initial=False) possible_investigation_result_bonus = models.IntegerField(initial=0) whistleblowing = models.BooleanField(choices=[ [False, "Do not raise the flag"], [True, "Raise the flag and point out potential misreporting"] ], widget=widgets.RadioSelect) whistleblowing_actual = models.BooleanField(initial=False) number_competition = models.IntegerField(initial=0) reduction_in_points = models.IntegerField(initial=0) misreporting_own_detected = models.BooleanField(initial=False) misreporting_other_detected = models.BooleanField(initial=False) investigation_started = models.BooleanField(initial=False) winner = models.BooleanField(initial=False) bonus_game = models.IntegerField(initial=0) bonus_whistleblowing = models.IntegerField(initial=0) bonus_total = models.IntegerField(initial=0) pass # FUNCTIONS def creating_session(subsession: Subsession): for player in subsession.get_players(): player.participant.bonus_totals = [] player.possible_investigation_result_bonus = determine_bonus(subsession) def determine_deduction(player: object) -> int: if player.group.session.config['anonymous']: return 1 else: return 20 def determine_bonus(subsession: Subsession) -> int: if subsession.session.config['incentives']: return 10 else: return 0 def determine_winners(subsession: Subsession): groups = list(subsession.get_groups()) for group in groups: players = list(group.get_players()) if players[0].number_competition == 0 and players[1].number_competition == 0: players[0].winner = False players[1].winner = False pass else: if players[0].number_competition > players[1].number_competition: players[0].winner = True players[0].bonus_game = 10 if players[0].number_competition < players[1].number_competition: players[1].winner = True players[1].bonus_game = 10 if players[0].number_competition == players[1].number_competition: import random winner = random.randint(0, 1) players[winner].winner = True players[winner].bonus_game = 10 pass def calcualte_payoffs(all_players): for player in all_players: player.bonus_total = player.bonus_whistleblowing + player.bonus_game player.participant.vars['bonus_totals'].append(player.bonus_total) pass def set_payoffs(subsession: Subsession): all_players = list(subsession.get_players()) whistleblowers = list(filter(lambda x: x.whistleblowing is True, all_players)) subsession.whistleblowing_actual = len(whistleblowers) if subsession.whistleblowing_actual > MAX_INVESTIGATIONS: import random for i in range(subsession.whistleblowing_actual - MAX_INVESTIGATIONS): whistleblowers.pop(random.randint(0, subsession.whistleblowing_actual - i - 1)) do_whistleblowing(all_players, whistleblowers) determine_winners(subsession) calcualte_payoffs(all_players) pass def do_whistleblowing(all_players, whistleblowers): for whistleblower in whistleblowers: whistleblower.whistleblowing_actual = True suspect = list(filter(lambda x: x.participant.id_in_session == whistleblower.cue_id, all_players)) whistleblower.investigation_started = True if whistleblower.investigation_started and suspect[0].misreported: suspect[0].number_competition = 0 suspect[0].misreporting_own_detected = True whistleblower.misreporting_other_detected = True whistleblower.bonus_whistleblowing = whistleblower.possible_investigation_result_bonus def bool_to_yes_no(var): if var: return "Yes" else: return "No" def assign_cues(subsession: Subsession): subsession.group_randomly() groups = list(subsession.get_groups()) group_1 = list(groups[0].get_players()) group_2 = list(groups[1].get_players()) group_3 = list(groups[2].get_players()) group_4 = list(groups[3].get_players()) group_1[0].cue_id = group_2[1].participant.id_in_session group_1[1].cue_id = group_3[0].participant.id_in_session group_2[0].cue_id = group_1[1].participant.id_in_session group_2[1].cue_id = group_4[0].participant.id_in_session group_3[0].cue_id = group_2[0].participant.id_in_session group_3[1].cue_id = group_4[1].participant.id_in_session group_4[0].cue_id = group_3[1].participant.id_in_session group_4[1].cue_id = group_1[0].participant.id_in_session import random group_1[0].number_received = random.randint(MIN, MAX) group_1[1].number_received = random.randint(MIN, MAX) group_2[0].number_received = random.randint(MIN, MAX) group_2[1].number_received = random.randint(MIN, MAX) group_3[0].number_received = random.randint(MIN, MAX) group_3[1].number_received = random.randint(MIN, MAX) group_4[0].number_received = random.randint(MIN, MAX) group_4[1].number_received = random.randint(MIN, MAX) group_1[0].cue_quality = random.choice(cue_quality_possible_values) group_1[1].cue_quality = random.choice(cue_quality_possible_values) group_2[0].cue_quality = random.choice(cue_quality_possible_values) group_2[1].cue_quality = random.choice(cue_quality_possible_values) group_3[0].cue_quality = random.choice(cue_quality_possible_values) group_3[1].cue_quality = random.choice(cue_quality_possible_values) group_4[0].cue_quality = random.choice(cue_quality_possible_values) group_4[1].cue_quality = random.choice(cue_quality_possible_values) def bool_to_misreported_or_not(cue_misreported): if cue_misreported: return 'Misreported' else: return 'Did not misreport' # PAGES class RoundStart(Page): pass class TaskScreenWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = assign_cues pass class TaskScreen(Page): form_model = 'player' form_fields = ['number_reported'] @staticmethod def before_next_page(player, timeout_happened): if player.number_received != player.number_reported: player.misreported = True pass pass class PreWhistleblowingWaitPage(WaitPage): wait_for_all_groups = True pass class WhistleblowingScreen(Page): form_model = 'player' form_fields = ['whistleblowing'] @staticmethod def vars_for_template(player: Player): others = list(player.get_others_in_subsession()) cue = list(filter(lambda x: x.participant.id_in_session == player.cue_id, others)) WhistleblowingScreen.randomise_cue_misreported(cue, player) player.misreported_display_line = bool_to_misreported_or_not(player.cue_misreported) return dict( other_individual_misreported=player.misreported_display_line, probability_of_true_behavior=player.cue_quality, probability_of_randomly_generated=100 - player.cue_quality ) @staticmethod def randomise_cue_misreported(cue, player): player.actual_cue_misreported = cue[0].misreported player.first_random_number = random.random() if player.first_random_number < (player.cue_quality / 100): player.cue_misreported = cue[0].misreported else: player.second_random_number = random.random() player.cue_misreported = True if player.second_random_number < 0.5 else False @staticmethod def before_next_page(player, timeout_happened): if player.whistleblowing: player.reduction_in_points = determine_deduction(player) player.number_competition = player.number_reported - player.reduction_in_points else: player.whistleblowing = False player.reduction_in_points = 0 player.number_competition = player.number_reported pass pass class PreRoundSummaryWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = set_payoffs pass class RoundSummary(Page): @staticmethod def vars_for_template(player): own_misreporting = "-" if player.misreported: own_misreporting = bool_to_yes_no(player.misreporting_own_detected) other_misreported = "-" if player.investigation_started: other_misreported = bool_to_yes_no(player.misreporting_other_detected) winner_text = bool_to_yes_no(player.winner) investigation_started_text = bool_to_yes_no(player.investigation_started) return dict( own_misreporting=own_misreporting, winner=winner_text, investigation_started=investigation_started_text, other_misreported=other_misreported, whistleblowing_bonus=player.bonus_whistleblowing, incentives=player.subsession.session.config['incentives'] ) pass class GameSummary(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS pass page_sequence = [RoundStart, TaskScreenWaitPage, TaskScreen, PreWhistleblowingWaitPage, WhistleblowingScreen, PreRoundSummaryWaitPage, RoundSummary, GameSummary]