from otree.api import * author = 'Knowlton Chan' doc = """ Cheap Talk Covert-Nonverifiable """ import random import json class Constants(BaseConstants): name_in_url = 'cheap_talk' players_per_group = 2 num_rounds = 41 Duties_list = ['A','B'] class Subsession(BaseSubsession): tot_matches = models.IntegerField(initial=41) def creating_session(subsession: Subsession): subsession.tot_matches = subsession.session.config['total_match_number'] subsession.group_randomly(fixed_id_in_group=True) for group in subsession.get_groups(): group.local_seed = random.randint(0, 9999) group.value_X = round(random.uniform(0.0, 100.0), 1) for player in subsession.get_players(): player.duty = Constants.Duties_list[player.id_in_group-1] class Group(BaseGroup): rounds = models.LongStringField(initial='{}') value_X = models.FloatField(initial=-1) choice_N = models.IntegerField(initial=1, min=1, max=10) guess_Y = models.FloatField(min=0.0, max=100.0) yellow_min = models.FloatField(min=0.0, max=100.0) yellow_max = models.FloatField(min=0.0, max=100.0) blue_index = models.IntegerField(initial=-1) local_seed = models.IntegerField(initial=-1) piece_min = models.FloatField(initial=-1) piece_max = models.FloatField(initial=101) class Player(BasePlayer): duty = models.StringField() points_A = models.FloatField(initial=0) points_B = models.FloatField(initial=0) read = models.IntegerField(initial=-1) payoff_final = models.IntegerField(initial=-1) chosen_match = models.IntegerField(initial=-1) q1 = models.IntegerField(choices=[ [1, '[BLANK]'], [2, '[answer]'], [3, '[BLANK]'], [4, '[BLANK]'] ], widget=widgets.RadioSelect, label='QUESTION 1: Which of the followings is NOT TRUE?') q2 = models.IntegerField(choices=[ [1, '[BLANK]'], [2, '[BLANK]'], [3, '[answer]'], [4, '[BLANK]'] ], widget=widgets.RadioSelect, label='QUESTION 2: Which of the followings is TRUE?') q3 = models.IntegerField(choices=[ [1, '[answer]'], [2, '[BLANK]'], [3, '[BLANK]'], [4, '[BLANK]'] ], widget=widgets.RadioSelect, label='QUESTION 3: Which of the followings CAN HAPPEN?') q4 = models.IntegerField(choices=[ [1, '[BLANK]'], [2, '[BLANK]'], [3, "[BLANK]"], [4, "[answer]"]], widget=widgets.RadioSelect, label='QUESTION 4: Which of the followings is TRUE?') class Instruction(Page): def is_displayed(player: Player): return player.subsession.round_number == 1 def vars_for_template(player: Player): read = -1 return { 'read': read, 'tot_matches': player.subsession.tot_matches-1 } class Quiz(Page): form_model = 'player' form_fields = ['q1','q2','q3', 'q4'] def live_method(player: Player, data): if data['read'] == 1: if player.read == -1: read = 1 return {player.id_in_group: {'read': read}} def is_displayed(player: Player): return player.subsession.round_number == 1 def error_message(player: Player, value): correct_answers = {"q1": 2, "q2": 3, "q3": 1, "q4": 4 } list_answers = list(value.items())[0:] list_correct_answers = list(correct_answers.items()) if list_answers != list_correct_answers: Text = 'Some of your answers are incorrect. It indicates that your understanding may not be fully accurate. ' \ 'Please try it again. You can always go back to the experimental instructions if you find anything unclear.' return Text # live_method = "live_instruct" def vars_for_template(player: Player): return { 'read' : player.read, 'tot_matches': player.subsession.tot_matches-1 } class Instruction2(Page): def is_displayed(player: Player): return player.subsession.round_number == 1 def vars_for_template(player: Player): return { 'tot_matches': player.subsession.tot_matches-1 } class PleaseWait(Page): def is_displayed(player: Player): return player.subsession.round_number == 1 pass class Usher(Page): def is_displayed(player: Player): return player.subsession.round_number <= player.subsession.tot_matches def vars_for_template(player: Player): return dict( roundnum = player.subsession.round_number - 1, role = player.duty ) class Page1_ChooseN(Page): form_model = 'group' form_fields = ['choice_N'] def is_displayed(player: Player): return player.duty == 'A' and player.subsession.round_number <= player.subsession.tot_matches @staticmethod def vars_for_template(player: Player): roundnum = player.subsession.round_number - 1 choice_N = player.group.choice_N return { "roundnum": roundnum, "choice_N": choice_N } def before_next_page(player: Player, timeout_happened): choice_N = player.group.choice_N value_X = player.group.value_X pieces = [[0.0] * 2 for _ in range(choice_N)] for i in range(choice_N): if i == 0: pass else: pieces[i][0] = round(pieces[i - 1][1] + 0.1, 1) pieces[i][1] = round((pieces[i][0] + (100.0 - pieces[i][0]) / (choice_N - i)), 1) j_index = 0 k_index = 0 for j in range(choice_N): for k in range(2): if value_X < pieces[j][k]: j_index = j k_index = k break else: continue break if value_X == 100: player.group.blue_index = choice_N else: if k_index==0: player.group.blue_index = j_index - 1 else: player.group.blue_index = j_index player.group.piece_min = pieces[player.group.blue_index][0] player.group.piece_max = pieces[player.group.blue_index][1] class Page2_Report(Page): form_model = 'group' form_fields = ['yellow_min','yellow_max'] def is_displayed(player: Player): return player.duty == 'A' and player.subsession.round_number <= player.subsession.tot_matches @staticmethod def vars_for_template(player: Player): roundnum = player.subsession.round_number - 1 choice_N = player.group.choice_N piece_min = player.group.piece_min piece_max = player.group.piece_max return { "roundnum": roundnum, "choice_N": choice_N, "piece_min": piece_min, "piece_max": piece_max } def before_next_page(player: Player, timeout_happened): player.group.yellow_min = round(player.group.yellow_min,1) player.group.yellow_max = round(player.group.yellow_max, 1) class Waiting_for_A(WaitPage): def is_displayed(player: Player): return player.subsession.round_number <= player.subsession.tot_matches def after_all_players_arrive(group: Group): pass class Page3_GuessY(Page): form_model = 'group' form_fields = ['guess_Y'] def is_displayed(player: Player): return player.duty == 'B' and player.subsession.round_number <= player.subsession.tot_matches @staticmethod def vars_for_template(player: Player): roundnum = player.subsession.round_number - 1 yellow_min = player.group.yellow_min yellow_max = player.group.yellow_max return { "roundnum": roundnum, "yellow_min": yellow_min, "yellow_max": yellow_max } def before_next_page(player: Player, timeout_happened): player.group.guess_Y = round(player.group.guess_Y, 1) class Waiting_for_B(WaitPage): def is_displayed(player: Player): return player.subsession.round_number <= player.subsession.tot_matches def after_all_players_arrive(group: Group): pass class Summary(Page): def is_displayed(player: Player): return player.subsession.round_number <= player.subsession.tot_matches @staticmethod def vars_for_template(player: Player): roundnum = player.subsession.round_number - 1 value_X = player.group.value_X choice_N = player.group.choice_N piece_min = player.group.piece_min piece_max = player.group.piece_max yellow_min = player.group.yellow_min yellow_max = player.group.yellow_max guess_Y = player.group.guess_Y if player.duty == 'A': player.points_A = round(max((100 - (((guess_Y - value_X + 20)*(guess_Y - value_X + 20)) / 50)) + (100 - 10 * choice_N), 0), 1) else: player.points_B = round(max(150 - (((guess_Y - value_X)*(guess_Y - value_X)) / 50) , 0), 1) points_A = player.points_A points_B = player.points_B return { "roundnum": roundnum, "value_X": value_X, "choice_N": choice_N, "piece_min": piece_min, "piece_max": piece_max, "yellow_min": yellow_min, "yellow_max": yellow_max, "guess_Y": guess_Y, "points_A": points_A, "points_B": points_B } page_sequence = [Instruction, Quiz, Instruction2, PleaseWait, Usher, Page1_ChooseN, Page2_Report, Waiting_for_A, Page3_GuessY, Waiting_for_B, Summary] # page_sequence = [Page1_ChooseN, Page2_Report, Waiting_for_A, Page3_GuessY, Waiting_for_B, Summary]