from otree.api import * import random doc = """ N-player all-pay auction game with a common bid cap k=18. Using the programs provided by John Duffy and Sergio Sapiña España, and Chris @ oTree as references. """ class Constants(BaseConstants): name_in_url = 'N_auction_18' players_per_group = 3 num_rounds = 61 #change to 61 in the real game table_template = __name__ + '/table.html' class Subsession(BaseSubsession): endowment = models.FloatField( initial=18.0 ) #the value of bid cap class Group(BaseGroup): highest_bid = models.FloatField(initial=0.0) value_list = models.StringField( initial="" ) #shown in result page# bid_list = models.StringField( initial="" ) winner_id = models.IntegerField(initial=0) participants = models.IntegerField(initial=3) class Player(BasePlayer): bid = models.FloatField( min=0.0, max=18.0, label="Please enter how much you want to bid for the reward:", ) is_winner = models.BooleanField( initial=False, doc="""Indicates whether the player is the winner""" ) round_payoff = models.FloatField(initial=0.0) value = models.FloatField(initial=0) raw_responses = models.LongStringField() chose_lottery = models.BooleanField() won_lottery = models.BooleanField(initial=False) age = models.IntegerField( label='What is your age?', min=18, max=100) gender = models.StringField( choices=['Male', 'Female', 'Other'], label='What is your gender?', widget=widgets.RadioSelect) citizenship = models.StringField( label='What is your country(s) of citizenship?') major = models.StringField( label='What is your major?') year = models.StringField( choices=['Freshman', 'Sophomore', 'Junior', 'Senior', 'Graduate', 'Other'], label='Which year are you in?', widget=widgets.RadioSelect) risk = models.StringField( choices=['Strongly Disagree', 'Disagree', 'Slightly Disagree', 'Slightly Agree', 'Agree', 'Strongly Agree'], label='You are a person who is fully prepared to take risks.', widget=widgets.RadioSelect) decision1 = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when the reward was worth 180 points for you?") decision2 = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when the reward was worth 60 points for you?") decision3 = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when the reward was worh 50 points for you?") comments = models.LongStringField(blank=True, initial=None, label="Do you have any comments, questions, or complaints about today's experiment?") name = models.StringField(initial=None, label="To link with your payments, please type your name below. Note that your identity will be confidential. " ) quiz1 = models.StringField( choices=['The same participants', 'Two randomly determined participants'], label='1. In each period, you are matched with:', widget=widgets.RadioSelect) quiz2 = models.StringField( choices=['True', 'False'], label='2. In each period, the reward is worth a different amount to different participants.', widget=widgets.RadioSelect) quiz3 = models.StringField( choices=['True', 'False'], label='3. In each period, the computer will assign the reward to the participant who makes the highest bid in a group (suppose there is no tie).', widget=widgets.RadioSelect) quiz4 = models.StringField( choices=['True', 'False'], label='4. You can bid up to 18 points.', widget=widgets.RadioSelect) quiz5 = models.StringField( choices=['True', 'False'], label='5. If your bid is the highest in your group, you win the competition, and your period earnings = your value of the reward – your bid (suppose there is no tie). ', widget=widgets.RadioSelect) quiz6 = models.StringField( choices=['True', 'False'], label='6. If your bid is not the highest in your group, you do not need to pay your bid.', widget=widgets.RadioSelect) class Trial(ExtraModel): player = models.Link(Player) sure_payoff = models.CurrencyField() lottery_high = models.CurrencyField() lottery_low = models.CurrencyField() probability = models.FloatField() chose_lottery = models.BooleanField() randomly_chosen = models.BooleanField(initial=False) # Functions def read_csv(): import csv f = open(__name__ + '/stimuli.csv', encoding='utf-8-sig') rows = list(csv.DictReader(f)) return rows def creating_session(subsession: Subsession): # print('in creating_session') session = subsession.session subsession.group_randomly(fixed_id_in_group=True) if subsession.round_number == 1: paying_round1 = random.randint(2, 11) # change those values to [2,21] later paying_round2 = random.randint(12, 21) paying_round3 = random.randint(22, 31) paying_round4 = random.randint(32, 41) paying_round5 = random.randint(42, 51) # change those values to [22,41] later paying_round6 = random.randint(52, 61) # change those values to [42,61] later session.vars['paying_round1'] = paying_round1 session.vars['paying_round2'] = paying_round2 session.vars['paying_round3'] = paying_round3 session.vars['paying_round4'] = paying_round4 session.vars['paying_round5'] = paying_round5 session.vars['paying_round6'] = paying_round6 session.vars['endowment'] = subsession.endowment for p in subsession.get_players(): p.round_chosen1 = session.vars['paying_round1'] p.round_chosen2 = session.vars['paying_round2'] p.round_chosen3 = session.vars['paying_round3'] p.round_chosen4 = session.vars['paying_round4'] p.round_chosen5 = session.vars['paying_round5'] p.round_chosen6 = session.vars['paying_round6'] stimuli = read_csv() for stim in stimuli: Trial.create(player=p, **stim) def set_winner(group: Group): import random players = group.get_players() group.highest_bid = max([p.bid for p in players]) players_with_highest_bid = [p for p in players if p.bid == group.highest_bid] winner = random.choice( players_with_highest_bid ) # if tie, winner is chosen at random winner.is_winner = True group.winner_id = winner.id_in_group #for p in players: # set_payoff(p) def set_payoff(group:Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p3 = group.get_player_by_id(3) for p in [p1,p2,p3]: if p.round_number <= 21: #change 3 to 21 in real-game p1.value = 180 p2.value = 60 p3.value = 50 elif p.round_number <= 41: #change 5 to 41 in real-game p1.value = 60 p2.value = 50 p3.value = 180 else: p1.value = 50 p2.value = 180 p3.value = 60 for p in [p1,p2,p3]: if p.is_winner: p.round_payoff = p.value - p.bid else: p.round_payoff = 0 - p.bid for p in [p1,p2,p3]: # Collect the list of values and bids to display at the feedback screen group.bid_list = group.bid_list + str(p.bid) + " " group.value_list = group.value_list + str(p.value) + " " def close_round(group: Group): set_winner(group) set_payoff(group) def set_value(group:Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p3 = group.get_player_by_id(3) for p in [p1, p2, p3]: if p.round_number <= 21: # change 3 to 21 in real-game p1.value = 180 p2.value = 60 p3.value = 50 elif p.round_number <= 41: # change 5 to 41 in real-game p1.value = 60 p2.value = 50 p3.value = 180 else: p1.value = 50 p2.value = 180 p3.value = 60 # PAGES class P1_Welcome(Page): def is_displayed(self): return self.round_number == 1 after_all_players_arrive = creating_session class P2_Group(Page): @staticmethod def before_next_page(player: Player, timeout_happened): group = player.group set_value(group) def vars_for_template(player: Player): endowment = player.subsession.endowment return dict( endowment=endowment, round_shown=player.round_number - 1, total_shown=Constants.num_rounds - 1 ) class P3_Practiceround_instruction(Page): def is_displayed(self): return self.round_number == 1 class P6_Practiceround_summary (Page): def is_displayed(self): return self.round_number == 1 class P4_Contribution(Page): form_model = 'player' form_fields = ['bid'] @staticmethod def vars_for_template(player: Player): endowment = player.subsession.endowment return dict( endowment=endowment, round_shown=player.round_number-1, total_shown=Constants.num_rounds - 1 ) class P5_RoundResults(Page): @staticmethod def vars_for_template(player: Player): winner_id = player.group.winner_id value_list = player.group.value_list.split(" ") bid_list = player.group.bid_list.split(" ") value_list.pop() bid_list.pop() endowment = player.subsession.endowment participants = ["Participant " + str(i + 1) for i in range(Constants.players_per_group)] return dict( participants=participants, winner_id=winner_id, value_list=value_list, bid_list=bid_list, endowment=endowment ) class P7_Part1_end(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds @staticmethod def vars_for_template(player: Player): session = player.session player_in_round1 = player.in_round(session.vars['paying_round1']) player_in_round2 = player.in_round(session.vars['paying_round2']) player_in_round3 = player.in_round(session.vars['paying_round3']) player_in_round4 = player.in_round(session.vars['paying_round4']) player_in_round5 = player.in_round(session.vars['paying_round5']) player_in_round6 = player.in_round(session.vars['paying_round6']) return dict( round1_payoff=player_in_round1.round_payoff, round2_payoff=player_in_round2.round_payoff, round3_payoff=player_in_round3.round_payoff, round4_payoff=player_in_round4.round_payoff, round5_payoff=player_in_round5.round_payoff, round6_payoff=player_in_round6.round_payoff, part1_payoff=player_in_round1.round_payoff + player_in_round2.round_payoff + player_in_round3.round_payoff + player_in_round4.round_payoff + player_in_round5.round_payoff + player_in_round6.round_payoff, paying_round1=session.vars['paying_round1'] - 1, paying_round2=session.vars['paying_round2'] - 1, paying_round3=session.vars['paying_round3'] - 1, paying_round4=session.vars['paying_round4'] - 1, paying_round5=session.vars['paying_round5'] - 1, paying_round6=session.vars['paying_round6'] - 1 ) class P8_Stimuli(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['raw_responses'] @staticmethod def vars_for_template(player: Player): return dict(trials=Trial.filter(player=player)) @staticmethod def before_next_page(player: Player, timeout_happened): import json import random trials = Trial.filter(player=player) # you could adjust this code to handle timeout_happened. responses = json.loads(player.raw_responses) for trial in trials: trial.chose_lottery = responses[str(trial.id)] trial = random.choice(trials) trial.randomly_chosen = True player.chose_lottery = trial.chose_lottery if player.chose_lottery: #player.won_lottery = trial.probability > random.random() if trial.probability > 100*(random.random()): player.won_lottery = True else: player.won_lottery = False if player.won_lottery: risk_payoff = trial.lottery_high else: risk_payoff = trial.lottery_low else: risk_payoff = trial.sure_payoff player.payoff = risk_payoff class P9_Stimuli_Results(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds @staticmethod def vars_for_template(player: Player): trials = Trial.filter(player=player, randomly_chosen=True) return dict(trials=trials) class P10_Demographic(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['name', 'age', 'gender', 'citizenship', 'major', 'year', 'risk'] class P11_Comment(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['decision1', 'decision2', 'decision3', 'comments'] class P12_End_game(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds @staticmethod def vars_for_template(player: Player): session = player.session player_in_round1 = player.in_round(session.vars['paying_round1']) player_in_round2 = player.in_round(session.vars['paying_round2']) player_in_round3 = player.in_round(session.vars['paying_round3']) player_in_round4 = player.in_round(session.vars['paying_round4']) player_in_round5 = player.in_round(session.vars['paying_round5']) player_in_round6 = player.in_round(session.vars['paying_round6']) return dict( round1_payoff=player_in_round1.round_payoff, round2_payoff=player_in_round2.round_payoff, round3_payoff=player_in_round3.round_payoff, round4_payoff=player_in_round4.round_payoff, round5_payoff=player_in_round5.round_payoff, round6_payoff=player_in_round6.round_payoff, part1_payoff=240 + player_in_round1.round_payoff + player_in_round2.round_payoff + player_in_round3.round_payoff + player_in_round4.round_payoff + player_in_round5.round_payoff + player_in_round6.round_payoff, part2_payoff=player.payoff, part1_payoff_currency=(240 + player_in_round1.round_payoff + player_in_round2.round_payoff + player_in_round3.round_payoff + player_in_round4.round_payoff + player_in_round5.round_payoff + player_in_round6.round_payoff) / 20, total_payoff_currency=7 + ((240 + player_in_round1.round_payoff + player_in_round2.round_payoff + player_in_round3.round_payoff + player_in_round4.round_payoff + player_in_round5.round_payoff + player_in_round6.round_payoff) / 20) + player.payoff, paying_round1=session.vars['paying_round1'] - 1, paying_round2=session.vars['paying_round2'] - 1, paying_round3=session.vars['paying_round3'] - 1, paying_round4=session.vars['paying_round4'] - 1, paying_round5=session.vars['paying_round5'] - 1, paying_round6=session.vars['paying_round6'] - 1, player_chose_lottery=player.chose_lottery, player_won_lottery=player.won_lottery ) class QUIZ(Page): def is_displayed(self): return self.round_number == 1 form_model = 'player' form_fields = ['quiz1', 'quiz2', 'quiz3', 'quiz4', 'quiz5', 'quiz6'] @staticmethod def error_message(player, values): solutions = dict( quiz1='Two randomly determined participants', quiz2='True', quiz3='True', quiz4='True', quiz5='True', quiz6='False' ) error_messages = dict() for field_name in solutions: if values[field_name] != solutions[field_name]: error_messages[field_name] = 'Wrong answer' return error_messages class Notice(Page): def is_displayed(self): return self.round_number in (22, 42) #waitpages# class ResultsWaitPage(WaitPage): after_all_players_arrive = close_round #class GroupWaitPage(WaitPage): # after_all_palyers_arrive = set_value page_sequence = [ P1_Welcome, QUIZ, P2_Group, P3_Practiceround_instruction, Notice, P4_Contribution, ResultsWaitPage, P5_RoundResults, P6_Practiceround_summary, P7_Part1_end, P8_Stimuli, P9_Stimuli_Results, P10_Demographic, P11_Comment, P12_End_game ]