from otree.api import * import random doc = """ pure pre_commitment game with public fund={1,3}, with social preference test at the end. """ class Constants(BaseConstants): name_in_url = 'pre_commitment_positive' players_per_group = 2 num_rounds = 35 #will change to 35 in real game #table_template = __name__ + '/table.html' class Subsession(BaseSubsession): endowment = models.FloatField( initial=10.0 ) # the value of endowment w externality = models.FloatField( initial=0.5 ) class Group(BaseGroup): total_private_claim = models.FloatField(initial=0) total_public_claim = models.FloatField(initial=0) public_claim = models.FloatField(initial=0) class Player(BasePlayer): public = models.StringField( choices=['1 point', '3 points'], label='Please select how many points you are willing to invest into the public project:', widget=widgets.RadioSelect) private = models.IntegerField( min=0, label='', ) #private = models.FloatField( # min=0, # label='', #) attack = models.BooleanField( initial=False, doc="""Indicates whether the player invest into the private claim""" ) total_payoff = models.FloatField(initial=0) total_payoff_externality = models.FloatField(initial=0) remaining_endowment = models.FloatField(initial=0) available_for_private = models.FloatField(initial=0) quiz1 = models.StringField( choices=['1 point', '10 points', '20 points'], label='1. What is your endowment in each round?', widget=widgets.RadioSelect) quiz2 = models.StringField( choices=['0 points', '1 point', '3 points'], label='2. How many points are invested by each of you (you and your group member) into the public project? ' 'Hint: Only the lower of the two public project investments will count towards the public project.', widget=widgets.RadioSelect) #quiz3 = models.StringField( # choices=['0 points', '1 points', '2 points', '10 points'], # label='3. What is the TOTAL PUBLIC CLAIMS?', # widget=widgets.RadioSelect) quiz4 = models.StringField( choices=['(10-1) + (10-1) = 18 points', '(10-1-5) + (10-1-4) = 9 points', '10 points',], label='3. What is the prize? ' 'Hint: See page 3 for the exact formula for the prize.', widget=widgets.RadioSelect) quiz5 = models.StringField( choices=['5/(5+4)*9 = 5 points', '5/(5+4)*18 = 10 points', '4/(5+4)*9 = 4 points', ], label='4. What are your earnings? Hint: See CASE 2 on page 4 for the exact formula for your earnings.', widget=widgets.RadioSelect) quiz6 = models.StringField( choices=['5/(5+4)*9 = 5 points', '5/(5+4)*18 = 10 points', '4/(5+4)*9 = 4 points',], label='5. What are your group member’s earnings? Hint: See CASE 2 on page 4 for the exact formula for your group member’s earnings. ', widget=widgets.RadioSelect) quiz7 = models.StringField( choices=['5+0.5*4 = 7 points', '5 points', '5-0.5*4 = 3 points',], label='6. What is your payoff? Hint: See CASE 2 on page 4 for the exact formula for your payoff. ', widget=widgets.RadioSelect) quiz8 = models.StringField( choices=['0 points', '1 point', '3 points'], label='7. How many points are invested by each of you (you and your group member) into the public project? ' 'Hint: Only the lower of the two public project investments will count towards the public project.', widget=widgets.RadioSelect) quiz9 = models.StringField( choices=['(10-1) + (10-1) = 18 points', '(10-1-4) + (10-1-0) = 14 points', '10 points'], label='8. What is the prize?' 'Hint: See page 2 for the exact formula for the prize.', widget=widgets.RadioSelect) quiz10 = models.StringField( choices=['4/(4+0)*14 = 14 points', '4/(4+2)*14 = 9.33 points', '2/(4+2)*14 = 4.67 points'], label='9. What are your earnings? Hint: See CASE 3 on page 4 for the exact formula for your earnings.', widget=widgets.RadioSelect) quiz11 = models.StringField( choices=['4/(4+2)*14 = 9.33 points', '2/(4+2)*14 = 4.67 points', '0/(4+0)*14 = 0 points'], label='10. What are your group member’s earnings? Hint: See CASE 3 on page 4 for the exact formula for your ' 'group member’s earnings. ', widget=widgets.RadioSelect) quiz12 = models.StringField( choices=['9.33 points', '9.33+0.5*4.67 = 11.67 points', '9.33-0.5*4.67 = 6.99 points' ], label='11. What is your payoff? Hint: Hint: See CASE 2 on page 4 for the exact formula for your payoff. ', widget=widgets.RadioSelect) # randomly select 1 out of 6-35 rounds for final payment payoff_money = models.FloatField( initial=0, ) payoff_money_total = models.FloatField( initial=0, ) payoff_final = models.FloatField( initial=0, ) round_to_pay = models.IntegerField( initial=0 ) ## social preference test social_1 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $2.5 to other'], ], label='Question 1', widget = widgets.RadioSelectHorizontal ) social_2 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $2.0 to other'], ], label='Question 2', widget=widgets.RadioSelectHorizontal ) social_3 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $1.5 to other'], ], label='Question 3', widget=widgets.RadioSelectHorizontal ) social_4 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $2.5 to you, $2.0 to other'], ], label='Question 4', widget=widgets.RadioSelectHorizontal ) social_5 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $2.5 to you, $1.5 to other'], ], label='Question 5', widget=widgets.RadioSelectHorizontal ) social_6 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $2.5 to you, $1.0 to other'], ], label='Question 6', widget=widgets.RadioSelectHorizontal ) social_7 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $3.5 to other'], ], label='Question 7', widget=widgets.RadioSelectHorizontal ) social_8 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $4.0 to other'], ], label='Question 8', widget=widgets.RadioSelectHorizontal ) social_9 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.0 to you, $4.5 to other'], ], label='Question 9', widget=widgets.RadioSelectHorizontal ) social_10 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.5 to you, $4.0 to other'], ], label='Question 10', widget=widgets.RadioSelectHorizontal ) social_11 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.5 to you, $4.5 to other'], ], label='Question 11', widget=widgets.RadioSelectHorizontal ) social_12 = models.IntegerField( choices=[ [1, 'A: $3.0 to you, $3.0 to other'], [2, 'B: $3.5 to you, $5.0 to other'], ], label='Question 12', widget=widgets.RadioSelectHorizontal ) social_payoff = models.FloatField( initial=0, ) social_selected_round = models.IntegerField( initial=0, ) # Functions def creating_session(subsession: Subsession): # Random matching subsession.group_randomly() # Decide round to pay players = subsession.get_players() # Decide a random round to pay from round 6 to 35 round_to_pay = random.randint(6, 35) # Decide a social test question social_selected_round = random.randint(1,12) for p in players: if p.round_number == 1: p.round_to_pay = round_to_pay p.social_selected_round = social_selected_round else: last_p = p.in_round(p.round_number - 1) p.round_to_pay = last_p.round_to_pay p.social_selected_round = last_p.social_selected_round def set_public_claim(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) if p1.public == "1 point": group.public_claim = 1 elif p2.public == "1 point": group.public_claim = 1 else: group.public_claim = 3 #group.public_claim = min([p.public for p in players]) group.total_public_claim = 2 * group.public_claim for p in [p1,p2]: p.available_for_private = 10 - group.public_claim def set_payoff(group:Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) for p in [p1, p2]: p.remaining_endowment = 20 - p1.private - p2.private - group.total_public_claim if p.private > 0: p.attack = True if p1.attack == True and p2.attack == True: p1.total_payoff = p1.private / (p1.private + p2.private) * p1.remaining_endowment p2.total_payoff = p2.private / (p1.private + p2.private) * p2.remaining_endowment elif p1.attack == True and p2.attack == False: p1.total_payoff = p1.private / (p1.private + group.total_public_claim) * p1.remaining_endowment p2.total_payoff = group.total_public_claim / (p1.private + group.total_public_claim) * p2.remaining_endowment elif p1.attack == False and p2.attack == True: p1.total_payoff = group.total_public_claim / (p2.private + group.total_public_claim) * p1.remaining_endowment p2.total_payoff = p2.private / (p2.private + group.total_public_claim) * p2.remaining_endowment else: for p in [p1, p2]: p.total_payoff = 10 - group.public_claim p1.total_payoff_externality = p1.total_payoff + 0.5 * p2.total_payoff p2.total_payoff_externality = p2.total_payoff + 0.5 * p1.total_payoff # Store the earnings of this round at the total # the show-up fee is $10 in this experiment for p in [p1,p2]: if p.round_number == p.round_to_pay: p.payoff_final = p.total_payoff_externality p.payoff_money = 1 *p.total_payoff_externality if p.payoff_money < 0: p.payoff_money_total = 10 p.payoff_money = 0 else: p.payoff_money_total = 10 + 1 * p.total_payoff_externality else: if p.round_number > 1: last_p = p.in_round(p.round_number - 1) p.payoff_final = last_p.payoff_final p.payoff_money = last_p.payoff_money p.payoff_money_total = last_p.payoff_money_total ## set the payoff for social efficiency test results def set_social_payoff(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) for p in [p1,p2]: if p.social_selected_round ==1 and p1.social_1 == 2: p1.social_payoff = 3 p2.social_payoff = 2.5 elif p.social_selected_round ==2 and p1.social_2 == 2: p1.social_payoff = 3 p2.social_payoff = 2.5 elif p.social_selected_round ==3 and p1.social_3 == 2: p1.social_payoff = 3 p2.social_payoff = 1.5 elif p.social_selected_round ==4 and p1.social_4 == 2: p1.social_payoff = 2.5 p2.social_payoff = 2 elif p.social_selected_round ==5 and p1.social_5==2: p1.social_payoff = 2.5 p2.social_payoff = 1.5 elif p.social_selected_round ==6 and p1.social_6==2: p1.social_payoff = 2.5 p2.social_payoff = 1 elif p.social_selected_round ==7 and p1.social_7 ==2: p1.social_payoff = 3 p2.social_payoff = 3.5 elif p.social_selected_round ==8 and p1.social_8 == 2: p1.social_payoff = 3 p2.social_payoff = 4 elif p.social_selected_round ==9 and p1.social_9 == 2: p1.social_payoff = 3 p2.social_payoff = 4.5 elif p.social_selected_round ==10 and p1.social_10 == 2: p1.social_payoff = 3.5 p2.social_payoff = 4 elif p.social_selected_round ==11 and p1.social_11 == 2: p1.social_payoff = 3.5 p2.social_payoff = 4.5 elif p.social_selected_round ==12 and p1.social_12 == 2: p1.social_payoff = 3.5 p2.social_payoff = 5 else: p1.social_payoff=3 p2.social_payoff=3 # PAGES class Welcome(Page): def is_displayed(self): return self.round_number == 1 after_all_players_arrive = creating_session class Stage1(Page): form_model = 'player' form_fields = ['public'] @staticmethod def vars_for_template(player: Player): other = player.get_others_in_group()[0] return dict( round_shown=player.round_number-5, total_shown=Constants.num_rounds-5, other=other ) class Stage2(Page): form_model = 'player' form_fields = ['private'] @staticmethod def vars_for_template(player: Player): public_claim = player.group.public_claim total_public_claim = player.group.total_public_claim other = player.get_others_in_group()[0] return dict( public_claim=public_claim, payoff1=10 - public_claim, payoff2=20 - total_public_claim, total_public_claim=total_public_claim, other=other, round_shown=player.round_number-5, total_shown=Constants.num_rounds-5 ) def error_message(player, values): public_claim = player.group.public_claim if values['private'] + public_claim > 10: return 'You cannot invest into PRIVATE CLAIMS more than 10 minus your PUBLIC CLAIMS' #def private_error_message(player, value): # print('value is', value) #if value + player.public > 10.0: # return 'You cannot invest into the private claim more than your remaining budget' class Result(Page): def vars_for_template(player: Player): public_claim = player.group.public_claim total_public_claim = player.group.total_public_claim other = player.get_others_in_group()[0] return dict( public_claim=public_claim, total_public_claim=total_public_claim, other=other, round_shown=player.round_number-5, total_shown=Constants.num_rounds-5 ) class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoff class Stage1WaitPage(WaitPage): after_all_players_arrive = set_public_claim class Quiz(Page): def is_displayed(self): return self.round_number == 1 form_model = 'player' form_fields = ['quiz1', 'quiz2', 'quiz4', 'quiz5', 'quiz6', 'quiz7', 'quiz8', 'quiz9', 'quiz10', 'quiz11', 'quiz12' ] @staticmethod def error_message(player, values): solutions = dict( quiz1='10 points', quiz2='1 point', quiz4='(10-1-5) + (10-1-4) = 9 points', quiz5='5/(5+4)*9 = 5 points', quiz6='4/(5+4)*9 = 4 points', quiz7='5+0.5*4 = 7 points', quiz8='1 point', quiz9='(10-1-4) + (10-1-0) = 14 points', quiz10='4/(4+2)*14 = 9.33 points', quiz11='2/(4+2)*14 = 4.67 points', quiz12='9.33+0.5*4.67 = 11.67 points' ) 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 Part1_end(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds @staticmethod def before_next_page(player: Player, timeout_happened): participant = player.participant # in settings.py need to add 'payoff_money' to PARTICIPANT_FIELDS. participant.payoff_money = player.payoff_money participant.round_to_pay = player.round_to_pay participant.payoff_final = player.payoff_final participant.social_payoff = player.social_payoff participant.social_selected_round = player.social_selected_round class Practiceround_instruction(Page): def is_displayed(self): return self.round_number == 1 class Practiceround_summary (Page): def is_displayed(self): return self.round_number == 5 class Social_efficiency (Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['social_1', 'social_2', 'social_3', 'social_4', 'social_5', 'social_6', 'social_7', 'social_8', 'social_9', 'social_10', 'social_11', 'social_12', ] class SocialWaitPage(WaitPage): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds after_all_players_arrive = set_social_payoff class Social_efficiency_results(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds page_sequence = [Welcome, Quiz, Practiceround_instruction, Stage1, Stage1WaitPage, Stage2, ResultsWaitPage, Result, Practiceround_summary, Social_efficiency, SocialWaitPage, Social_efficiency_results, Part1_end ]