from otree.api import ( Page, WaitPage, models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) from random import choice import random from random import randint doc = """ 2 firms complete in a market by setting prices for homogenous goods. See "Kruse, J. B., Rassenti, S., Reynolds, S. S., & Smith, V. L. (1994). Bertrand-Edgeworth competition in experimental markets. Econometrica: Journal of the Econometric Society, 343-371." """ class Constants(BaseConstants): players_per_group = 6 name_in_url = 'school_choice' num_rounds = 2 instructions_template = 'school_choice/instructions.html' chosen_round = 41 StudentA = ['student_1', 'student_3', 'student_4'] StudentB = ['student_2', 'student_5', 'student_6'] StudentC = ['student1', 'student3', 'student4'] StudentD = ['student2', 'student5', 'student6'] class Subsession(BaseSubsession): def after_all_players_arrive(self, players): if self.round_number == 1: random.shuffle(players) return players pass class Group(BaseGroup): pass class Player(BasePlayer): random_number = models.IntegerField() slider_value = models.IntegerField(min=0, max=100) number = models.IntegerField() chosen_bet = models.CharField( choices=[['A', 'School A'], ['B', 'School B']], widget=widgets.RadioSelect, verbose_name="Select your choice of school" ) accept = models.StringField() school_accepted = models.StringField() practice = models.IntegerField(min=0, max=100) student_1 = models.BooleanField(blank=True) student_2 = models.BooleanField(blank=True) student_3 = models.BooleanField(blank=True) student_4 = models.BooleanField(blank=True) student_5 = models.BooleanField(blank=True) student_6 = models.BooleanField(blank=True) student1 = models.BooleanField(blank=True) student2 = models.BooleanField(blank=True) student3 = models.BooleanField(blank=True) student4 = models.BooleanField(blank=True) student5 = models.BooleanField(blank=True) student6 = models.BooleanField(blank=True) Percentage = models.IntegerField(min=0, max=100) Percentage2 = models.IntegerField(min=0, max=100) guess_a = models.IntegerField(min=0, max=100) guess_b = models.IntegerField(min=0, max=100) guess_am = models.IntegerField(min=0, max=100) guess_bm = models.IntegerField(min=0, max=100) belief_a = models.IntegerField() belief_n = models.IntegerField() belief_a1 = models.IntegerField() belief_n1 = models.IntegerField() # FUNCTIONS def creating_session(subsession: Subsession): subsession.group_randomly(fixed_id_in_group=True) def set_payoffs(group: Group): all_players = group.get_players() for player in all_players: if player.id_in_group in [1, 2]: player.number_JP = choice([100, 90, 75]) elif player.id_in_group in [3, 4]: player.number_JP = choice([95, 80, 65]) else: player.number_JP = choice([85, 70, 60]) chosen_a = [p for p in all_players if p.chosen_betJP == 'A'] chosen_b = [p for p in all_players if p.chosen_betJP == 'B'] top_a = sorted(chosen_a, key=lambda x: x.number_JP, reverse=True)[:2] top_b = sorted(chosen_b, key=lambda x: x.number_JP, reverse=True)[:2] guessa1 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 1] guessb1 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 1] top_guessa1 = sorted(guessa1, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb1 = sorted(guessb1, key=lambda x: x.number_JP, reverse=True)[:2] guessa2 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 2] guessb2 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 2] top_guessa2 = sorted(guessa2, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb2 = sorted(guessb2, key=lambda x: x.number_JP, reverse=True)[:2] guessa3 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 3] guessb3 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 3] top_guessa3 = sorted(guessa3, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb3 = sorted(guessb3, key=lambda x: x.number_JP, reverse=True)[:2] guessa4 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 4] guessb4 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 4] top_guessa4 = sorted(guessa4, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb4 = sorted(guessb4, key=lambda x: x.number_JP, reverse=True)[:2] guessa5 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 5] guessb5 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 5] top_guessa5 = sorted(guessa5, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb5 = sorted(guessb5, key=lambda x: x.number_JP, reverse=True)[:2] guessa6 = [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 6] guessb6 = [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 6] top_guessa6 = sorted(guessa6, key=lambda x: x.number_JP, reverse=True)[:2] top_guessb6 = sorted(guessb6, key=lambda x: x.number_JP, reverse=True)[:2] if group.subsession.round_number + 1 == Constants.chosen_round: for player in top_a: player.payoff = 10 for player in top_b: player.payoff = 7 elif group.subsession.round_number + 20 == Constants.chosen_round: for player in all_players: if player.id_in_group == 1 and player in top_guessa1: player.payoff = player.guess_a_JP elif player.id_in_group == 2 and player in top_guessa2: player.payoff = player.guess_a_JP elif player.id_in_group == 3 and player in top_guessa3: player.payoff = player.guess_a_JP elif player.id_in_group == 4 and player in top_guessa4: player.payoff = player.guess_a_JP elif player.id_in_group == 5 and player in top_guessa5: player.payoff = player.guess_a_JP elif player.id_in_group == 6 and player in top_guessa6: player.payoff = player.guess_a_JP elif group.subsession.round_number + 40 == Constants.chosen_round: for player in all_players: if player.id_in_group == 1 and player in top_guessb1: player.payoff = player.guess_b_JP elif player.id_in_group == 2 and player in top_guessb2: player.payoff = player.guess_b_JP elif player.id_in_group == 3 and player in top_guessb3: player.payoff = player.guess_b_JP elif player.id_in_group == 4 and player in top_guessb4: player.payoff = player.guess_b_JP elif player.id_in_group == 5 and player in top_guessb5: player.payoff = player.guess_b_JP elif player.id_in_group == 6 and player in top_guessb6: player.payoff = player.guess_b_JP for player in all_players: if player in top_a or player in top_b: player.accept_JP = 'accepted' else: player.accept_JP = 'rejected' # PAGES class ComprehensionQuestions(Page): # Waiting Page form_model = 'player' form_fields = ['CQ1', 'CQ2', 'CQ3', 'CQ4'] @staticmethod def vars_for_template(player: Player): num_of_questions = Constants.num_rounds - 1 return dict(num_of_questions=num_of_questions) def is_displayed(player: Player): return player.round_number == 1 @staticmethod def error_message(player: Player, values): # define error messages for each question error_messages = dict( CQ1='Incorrect answer! Hint: Box L has more green balls than orange balls, while Box R has more orange balls than green balls.', CQ2='Incorrect answer! Hint: The computer draws one ball at a time, record its color, place it back to the selected Box, and repeat this process several times.', CQ3='Incorrect answer! Hint: We designed the bonus rule to make sure that you can secure the largest chance of winning the $5 bonus by reporting your best guess of the true likelihood.', CQ4='Incorrect answer! Hint: If matches, the computer will submit your answer in that matched scenario question for you to determine your chance of winning the bonus.', ) # define the correct solution of each question solutions = dict( CQ1=1, CQ2=1, CQ3=3, CQ4=2, ) # initialize an empty dictionary for errors errors = dict() # check each question and add an error message if the answer is incorrect for field_name in solutions: if values[field_name] != solutions[field_name]: errors[field_name] = error_messages[field_name] player.num_failed_attempts += 1 if errors: if player.num_failed_attempts > 2: player.failed_more_than1 = True else: return errors @staticmethod def before_next_page(player: Player, timeout_happened): session = player.session participant = player.participant if player.failed_more_than1 == False: session.completions_by_treatment[participant.color] += 1 else: session.completions_by_treatment[participant.color] += 0 class Introduction(Page): pass class Introduction2(Page): pass class Intro1(Page): pass class Intro2(Page): form_model = 'player' form_fields = ['student_1', 'student_3', 'student_4'] class WrongA(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('student_1')==None or player.field_maybe_none('student_3')==None or player.field_maybe_none('student_4')==True form_model = 'player' form_fields = ['student_2', 'student_5', 'student_6'] class CorrectA(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('student_1')==1 and player.field_maybe_none('student_3')==1 and player.field_maybe_none('student_4')==None form_model = 'player' form_fields = ['student_2', 'student_5', 'student_6'] class WrongB(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('student_2')==None or player.field_maybe_none('student_5')==True or player.field_maybe_none('student_6')==None form_model = 'player' form_fields = ['student1', 'student2', 'student3','student4', 'student5', 'student6'] class CorrectB(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('student_2') == True and player.field_maybe_none( 'student_5') == None and player.field_maybe_none('student_6') == True class WrongB2(Page): @staticmethod def is_displayed(player: Player): if player.field_maybe_none('student_2')!=True and player.field_maybe_none( 'student_5')!=None and player.field_maybe_none( 'student_6')!=True: return player.field_maybe_none('student1') == None or player.field_maybe_none( 'student2') == None or player.field_maybe_none( 'student3') == None or player.field_maybe_none( 'student4') == True or player.field_maybe_none( 'student5') == True or player.field_maybe_none( 'student6') == None class Intro4(Page): form_model = 'player' form_fields = ['Percentage'] pass class Intro4wrong(Page): pass @staticmethod def is_displayed(player: Player): return player.field_maybe_none('Percentage') != 7 class Intro4wrong2(Page): form_model = 'player' form_fields = ['Percentage2'] @staticmethod def is_displayed(player: Player): return player.field_maybe_none('Percentage') != 7 class Intro4wrongRight(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('Percentage2') == 30 class Intro4wrongWrong(Page): @staticmethod def is_displayed(player: Player): if player.field_maybe_none('Percentage') != 7: return player.field_maybe_none('Percentage2') != 30 class Intro4right(Page): @staticmethod def is_displayed(player: Player): return player.field_maybe_none('Percentage') == 7 class Intro5(Page): pass class ChoosingBet(Page): form_model = 'player' form_fields = ['chosen_bet'] class GuessingA(Page): form_model = 'player' form_fields = ['guess_a','guess_am'] class GuessingB(Page): form_model = 'player' form_fields = ['guess_b','guess_bm'] class ResultsWaitPage(WaitPage): after_all_players_arrive = 'set_payoffs' class Results(Page): @staticmethod def vars_for_template(player: Player): group = player.group all_players = group.get_players() top_a = sorted( [p for p in all_players if p.chosen_betJP == 'A'], key=lambda x: x.number_JP, reverse=True)[:2] top_b = sorted( [p for p in all_players if p.chosen_betJP == 'B'], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa1 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 1], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb1 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 1], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa2 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 2], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb2 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 2], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa3 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 3], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb3 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 3], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa4 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 4], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb4 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 4], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa5 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 5], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb5 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 5], key=lambda x: x.number_JP, reverse=True)[:2] top_guessa6 = sorted( [p for p in all_players if p.chosen_betJP == 'A' or p.id_in_group == 6], key=lambda x: x.number_JP, reverse=True)[:2] top_guessb6 = sorted( [p for p in all_players if p.chosen_betJP == 'B' or p.id_in_group == 6], key=lambda x: x.number_JP, reverse=True)[:2] return { 'top_a': top_a, 'top_b': top_b, 'top_guessa1': top_guessa1, 'top_guessb1': top_guessb1, 'top_guessa2': top_guessa2, 'top_guessb2': top_guessb2, 'top_guessa3': top_guessa3, 'top_guessb3': top_guessb3, 'top_guessa4': top_guessa4, 'top_guessb4': top_guessb4, 'top_guessa5': top_guessa5, 'top_guessb5': top_guessb5, 'top_guessa6': top_guessa6, 'top_guessb6': top_guessb6, } pass class Payout(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 2 class Intro3(Page): form_model = 'player' @staticmethod def vars_for_template(player: Player): example_report = random.randint(2, 5) return dict(example_report=example_report, num_rounds=Constants.num_rounds - 1,) @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Intro3_1(Page): form_model = 'player' class Belief(Page): form_model = 'player' form_fields = ['guess_a','guess_b','guess_n'] class Belief2(Page): form_model = 'player' form_fields = ['guess_b','guess_n'] class Pose(WaitPage): wait_for_all_groups = True @staticmethod def is_displayed(player: Player): if player.round_number == 1: return class Number (Page): form_model = 'player' form_fields = ['random_number'] page_sequence = [Number,Intro3, Intro3_1, WrongB, Intro1, Intro2, WrongA, CorrectA, WrongB2, Intro4, Intro4wrong, Intro4wrong2, Intro4right, Intro4wrongRight, Intro4wrongWrong, Intro5, ChoosingBet, GuessingA, GuessingB, ResultsWaitPage, Results, Pose]