from otree.api import * import random doc = """ a.k.a. Keynesian beauty contest. Players all guess a number; whoever guesses closest to 2/3 of the average wins. See https://en.wikipedia.org/wiki/Guess_2/3_of_the_average """ class C(BaseConstants): PLAYERS_PER_GROUP = 4 NUM_ROUNDS = 1 NAME_IN_URL = 'guess_two_thirds' JACKPOT = cu(200) GUESS_MAX = 100 class Subsession(BaseSubsession): pass class Group(BaseGroup): two_thirds_avg = models.FloatField() best_guess = models.IntegerField() num_winners = models.IntegerField() class Player(BasePlayer): payment = models.IntegerField(initial=10) is_bid_winner = models.BooleanField(initial=False) selected_bid = models.IntegerField(min=0, max=100) final_euro_amount = models.FloatField(initial=8.00) guess = models.IntegerField( min=0, max=C.GUESS_MAX, label="Please pick a number from 0 to 100:" ) is_winner = models.BooleanField(initial=False) q1 = models.StringField( choices=[['True', 'True'], ['False', 'False']], label='Question 1: In the BDM mechanism, if my bid is lower than the randomly chosen price, I will still receive the advice but will not have to pay for it.', widget=widgets.RadioSelectHorizontal, ) q2 = models.StringField( choices=[['True', 'True'], ['False', 'False']], label='Question 2: In the Prisoner Dilemma game, if both players defect, they will receive a smaller punishment than if they both cooperate.', widget=widgets.RadioSelectHorizontal, ) q3 = models.StringField( choices=[['You get a small punishment, and the other gets a reward', 'You get a small punishment, and the other gets a reward'], ['Both get a moderate punishment','Both get a moderate punishment'], ['You get a heavy punishment, and the other gets a reward','You get a heavy punishment, and the other gets a reward'], ['Both get a small punishment','Both get a small punishment'],], label='Question 3: In the Prisoners Dilemma, what happens if you cooperate and the other participant defects?', ) q4 = models.StringField( choices=[['50 ECU', '50 ECU'], ['100 ECU', '100 ECU'], ['150 ECU', '150 ECU'], ['200 ECU', '200 ECU']], label='Question 4: What is the maximum amount you can bid for the advice in the BDM mechanism?', widget=widgets.RadioSelectHorizontal, ) bid = models.IntegerField(label='What is bid for the Advice?', min=0, max=100) program = models.StringField( choices=[['MSc. Digital Economy', 'MSc. Digital Economy'], ['MSc. International Management/CEMS', 'MSc. International Management/CEMS'], ['MSc. Marketing', 'MSc. Marketing'], ['MSc. Supply Chain Management', 'MSc. Supply Chain Management'], ['Prefer Not To Say', 'Prefer Not To Say']], label='What is your Program?', ) age = models.IntegerField(label='What is your age?', min=13, max=125) gender = models.StringField( choices=[['Male', 'Male'], ['Female', 'Female'], ['Other', 'Other'], ['Prefer Not To Say', 'Prefer Not To Say']], label='What is your Gender?', ) knowledge = models.StringField( choices=[['Yes', 'Yes'], ['No', 'No']], label='Did you have any prior knowledge of the game?', widget=widgets.RadioSelectHorizontal, ) feedback = models.StringField( choices=[['Yes', 'Yes'], ['No', 'No']], label='Did you find the advice valuable when making your decision?', widget=widgets.RadioSelectHorizontal, ) # FUNCTIONS def set_payoffs(group: Group): players = group.get_players() guesses = [p.guess for p in players] two_thirds_avg = (2 / 3) * sum(guesses) / len(players) group.two_thirds_avg = round(two_thirds_avg, 2) group.best_guess = min(guesses, key=lambda guess: abs(guess - group.two_thirds_avg)) winners = [p for p in players if p.guess == group.best_guess] group.num_winners = len(winners) for p in winners: p.is_winner = True p.payoff = C.JACKPOT / group.num_winners p.payment += int(p.payoff) p.final_euro_amount += (p.payment / 100) * 2.5 def decide_bid_winner(group: Group): players = group.get_players() the_selected_bid = random.randint(1, 100) for p in players: p.is_bid_winner = True if p.bid >= the_selected_bid else False p.selected_bid = the_selected_bid if p.is_bid_winner == True: p.payment -= the_selected_bid def two_thirds_avg_history(group: Group): return [g.two_thirds_avg for g in group.in_previous_rounds()] # PAGES class Introduction_s(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Introduction_bdm(Page): timeout_seconds = 100 class Evaluation(Page): timeout_seconds = 120 form_model = 'player' form_fields = ['q1','q2','q3','q4'] def error_message(self, values): # Custom validation logic goes here error_messages = [] # Check if answers are correct if values['q1'] != 'False': error_messages.append("Answer to Question 1 is incorrect.") if values['q2'] != 'False': error_messages.append("Answer to Question 2 is incorrect.") if values['q3'] != 'You get a heavy punishment, and the other gets a reward': error_messages.append("Answer to Question 3 is incorrect.") if values['q4'] != '100 ECU': error_messages.append("Answer to Question 4 is incorrect.") # Display retry warning if any answers are incorrect if error_messages: return '\n'.join(error_messages) class Bidding(Page): timeout_seconds = 60 form_model = 'player' form_fields = ['bid'] class Guess(Page): form_model = 'player' form_fields = ['guess'] @staticmethod def vars_for_template(player: Player): group = player.group return dict(two_thirds_avg_history=two_thirds_avg_history(group)) class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs class ResultsWaitPageBDM(WaitPage): after_all_players_arrive = decide_bid_winner class Results(Page): @staticmethod def vars_for_template(player: Player): group = player.group sorted_guesses = sorted(p.guess for p in group.get_players()) return dict(sorted_guesses=sorted_guesses) class Questions(Page): form_model = 'player' form_fields = ['program', 'age', 'gender', 'knowledge', 'feedback'] class ThankYou(Page): form_model = 'player' page_sequence = [Introduction_s, Introduction_bdm, Evaluation, Bidding, ResultsWaitPageBDM, Guess, ResultsWaitPage, Results, Questions, ThankYou]