from otree.api import * import random doc = """ Tullock contest with no identity externality, using the bid cap k=450 """ class Constants(BaseConstants): name_in_url = 'No_Externality_tullock_k450' 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=450.0 ) # the value of bid cap class Group(BaseGroup): position_list = models.StringField( initial="" ) # shown in result page of different positions bid# bid_list = models.StringField( initial="" ) winner_id = models.IntegerField(initial=0) winner_position_id = models.StringField(initial='') participants = models.IntegerField(initial=3) sum_bids = models.FloatField( initial=0.0, ) prob_list = models.StringField( initial="" ) rnd = models.FloatField( initial=0.0, ) prob_list_new = models.StringField( initial="" ) #only to be shown in the table class Player(BasePlayer): bid = models.FloatField( min=0.0, max=450.0, label="Please enter how much you want to bid this period:", ) is_winner = models.BooleanField( initial=False, doc="""Indicates whether the player is the winner""" ) prob_winner = models.FloatField( initial=0.0, ) round_payoff = models.FloatField(initial=0.0) value_11 = models.FloatField(initial=0) value_12 = models.FloatField(initial=0) value_13 = models.FloatField(initial=0) value_22 = models.FloatField(initial=0) value_21 = models.FloatField(initial=0) value_23 = models.FloatField(initial=0) value_33 = models.FloatField(initial=0) value_31 = models.FloatField(initial=0) value_32 = models.FloatField(initial=0) position = models.StringField(initial='') raw_responses = models.LongStringField() chose_lottery = models.BooleanField() won_lottery = models.BooleanField(initial=False) part2_payoff = models.CurrencyField() 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) decisionA = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when you were at position A (when you win, you receive 400 points; when you lose, you receive 0 points)?") decisionB = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when you were at position B (when you win, you receive 400 points; when you lose, you receive 220 points)?") decisionC = models.LongStringField(initial=None, label="How did you make your decisions in Part 1, when you were at position C (when you win, you receive 400 points; when you lose, you receive 0 points)?") comments = models.LongStringField(blank=True, initial=None, label="Do you have any comments, questions, or complaints about today's experiment?") 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. Your position will change every 20 periods.', widget=widgets.RadioSelect) quiz3 = models.StringField( choices=['True', 'False'], label='3. In each period, the participant who places the highest bid in a group will win.', widget=widgets.RadioSelect) quiz4 = models.StringField( choices=['True', 'False'], label='4. You can bid up to 450 points.', widget=widgets.RadioSelect) quiz5 = models.StringField( choices=['True', 'False'], label='5. The larger the bid you make, the higher the probability you will win. If you are the winner, your payoff in the period is = 400 – your bid.', widget=widgets.RadioSelect) quiz6 = models.StringField( choices=['True', 'False'], label='6. If you are not the winner in a period, you receive 0 points.', widget=widgets.RadioSelect) quiz7 = models.StringField( choices=['True', 'False'], label='7. If you are not the winner in a period, 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) #same ID in same positions every 20 rounds if subsession.round_number == 1: paying_round1 = random.randint(2, 11) # change those values to (2,11) later paying_round2 = random.randint(12, 21) # change those values to (12,21) later paying_round3 = random.randint(22, 31) # change those values to (22,31) later paying_round4 = random.randint(32, 41) # change to (32,41) later paying_round5 = random.randint(42, 51) # change to (42,51) later paying_round6 = random.randint(52, 61) # change to (52,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) # Decide who is the winner and gather all the required data def set_winner(group: Group): players = group.get_players() group.rnd = random.random() #set the positions for players 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.position = 'A' p2.position = 'B' p3.position = 'C' elif p.round_number <= 41: # change 5 to 41 in real-game, p1.position = 'B' p2.position = 'C' p3.position = 'A' else: p1.position = 'C' p2.position = 'A' p3.position = 'B' # Count the sum of bids for p in players: group.sum_bids = round(group.sum_bids + p.bid, 1) # Random number to decide the winner randomly rnd = group.rnd # Key to remember if we already found a winner found_winner = 0 # Iterate the players and look for the winner for p in players: # If the sum of bids are greater than 0 if group.sum_bids > 0: p.prob_winner = round(p.bid / group.sum_bids, 4) # If all the bids are 0 we avoid /0 if group.sum_bids == 0: p.prob_winner = round(1 / group.participants, 4) if found_winner == 0: # If rnd is lower than prob_winner we found a winner if rnd <= p.prob_winner: p.is_winner = 1 found_winner = 1 group.winner_id = p.id_in_group group.winner_position_id = p.position # Else we remove his prob_winning from the rnd and proceed with the next player else: rnd = rnd - p.prob_winner # Collect the list of probabs and bids to display at the feedback screen if p.prob_winner > 0: group.prob_list = group.prob_list + str(round(p.prob_winner * 100, 2)) + " " group.prob_list_new = group.prob_list_new + str(round(p.prob_winner, 2)) + " " group.position_list = group.position_list + str(p.position) + " " group.bid_list = group.bid_list + str(p.bid) + " " else: group.position_list = group.position_list + str(p.position) + " " group.prob_list = group.prob_list + "0.00 " group.prob_list_new = group.prob_list_new + "0.00 " group.bid_list = group.bid_list + "0.0 " 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, 1 and 3 are extremist, 2 is centrist p1.position = 'A' p2.position = 'B' p3.position = 'C' p1.value_12 = 0 p1.value_13 = 0 p2.value_23 = 220 p2.value_21 = 220 p3.value_31 = 0 p3.value_32 = 0 p1.value_11 = 400 p2.value_22 = 400 p3.value_33 = 400 elif p.round_number <= 41: # change 5 to 41 in real-game, 2 and 3 are extremist, 1 is centrist p1.position = 'B' p2.position = 'C' p3.position = 'A' p1.value_12 = 220 p1.value_13 = 220 p2.value_23 = 0 p2.value_21 = 0 p3.value_31 = 0 p3.value_32 = 0 p1.value_11 = 400 p2.value_22 = 400 p3.value_33 = 400 else: #1 and 2 are extremist, 3 is centrist p1.position = 'C' p2.position = 'A' p3.position = 'B' p1.value_12 = 0 p1.value_13 = 0 p2.value_23 = 0 p2.value_21 = 0 p3.value_31 = 220 p3.value_32 = 220 p1.value_11 = 400 p2.value_22 = 400 p3.value_33 = 400 if p1.is_winner: p1.round_payoff = p1.value_11 - p1.bid p2.round_payoff = p2.value_21 - p2.bid p3.round_payoff = p3.value_31 - p3.bid elif p2.is_winner: p1.round_payoff = p1.value_12 - p1.bid p2.round_payoff = p2.value_22 - p2.bid p3.round_payoff = p3.value_32 - p3.bid elif p3.is_winner: p1.round_payoff = p1.value_13 - p1.bid p2.round_payoff = p2.value_23 - p2.bid p3.round_payoff = p3.value_33 - p3.bid def close_round(group: Group): set_winner(group) set_payoff(group) def set_position(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.position = 'A' p2.position = 'B' p3.position = 'C' elif p.round_number <= 41: # change 5 to 41 in real-game, p1.position = 'B' p2.position = 'C' p3.position = 'A' else: p1.position = 'C' p2.position = 'A' p3.position = 'B' #PAGES class P1_Welcome(Page): def is_displayed(self): return self.round_number == 1 after_all_players_arrive = creating_session class P2_Quiz(Page): def is_displayed(self): return self.round_number == 1 form_model = 'player' form_fields = ['quiz1', 'quiz2', 'quiz3', 'quiz4', 'quiz5', 'quiz6', 'quiz7'] @staticmethod def error_message(player, values): solutions = dict( quiz1='Two randomly determined participants', quiz2='True', quiz3='False', quiz4='True', quiz5='True', quiz6='False', quiz7='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 P3_Group(Page): @staticmethod def before_next_page(player: Player, timeout_happened): group = player.group set_position(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 P4_Practice_round_instruction(Page): def is_displayed(self): return self.round_number == 1 class P5_Notice (Page): def is_displayed(self): return self.round_number in (22, 42) #in real game change to (22,42) class P6_Contest_A (Page): def is_displayed(player): return player.position == 'A' 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 P6_Contest_B(Page): def is_displayed(player): return player.position == 'B' 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 P6_Contest_C (Page): def is_displayed(player): return player.position == 'C' 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 P7_Round_result(Page): def vars_for_template(player: Player): winner_position_id = player.group.winner_position_id winner_prob = round(player.group.rnd * 100, 2) endowment = player.subsession.endowment position_list = player.group.position_list.split(" ") prob_list = player.group.prob_list.split(" ") prob_list_new = player.group.prob_list_new.split(" ") bid_list = player.group.bid_list.split(" ") position_list.pop() prob_list.pop() prob_list_new.pop() bid_list.pop() bid1=player.group.get_player_by_id(1).bid bid2=player.group.get_player_by_id(2).bid bid3=player.group.get_player_by_id(3).bid prob1=player.group.get_player_by_id(1).prob_winner prob2=player.group.get_player_by_id(2).prob_winner prob3=player.group.get_player_by_id(3).prob_winner sum_bids = player.group.sum_bids #participants = ["Participant " + str(i + 1) for i in range(Constants.players_per_group)] # Colors to iterate #colors = ["", "bg-success", "bg-danger"] colors = ["", "bg-warning", "bg-dark"] used = 0 # Data to make the graph graph_list = [] # Build the graph_list for i in range(Constants.players_per_group): current = [] # First item is the probability of winning current.append(prob_list[i]) #IndexError: list index out of range #second item is the position id current.append(position_list[i]) # Third item is the color of the bar only if needed if float(current[0]) > 0.0: current.append(colors[used % 3]) used = used + 1 else: current.append("") # Finally we add the current triplet to the list graph_list.append(current) return dict( winner_position_id=winner_position_id, winner_prob=winner_prob, endowment=endowment, position_list=position_list, prob_list_new=prob_list_new, prob_list=prob_list, bid_list=bid_list, graph_list=graph_list, sum_bids=sum_bids, bid1=bid1, bid2=bid2, bid3=bid3, prob1=prob1, prob2=prob2, prob3=prob3, round_shown=player.round_number - 1, total_shown=Constants.num_rounds - 1 ) class P8_Practice_round_summary (Page): def is_displayed(self): return self.round_number == 1 class P9_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 P10_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 > (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.part2_payoff = risk_payoff class P11_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 P12_Demographic(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['age', 'gender', 'citizenship', 'major', 'year', 'risk'] class P13_Comments(Page): @staticmethod def is_displayed(player: Player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['decisionA', 'decisionB', 'decisionC', 'comments'] class 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= 1000 + 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.part2_payoff, part1_payoff_currency=((1000 + 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) / 100), total_payoff_currency=7 + ((1000 + 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) / 100) + player.part2_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 ) #Waitpages class ResultsWaitPage(WaitPage): after_all_players_arrive = close_round page_sequence = [ P1_Welcome, P2_Quiz, P3_Group, P4_Practice_round_instruction, P5_Notice, P6_Contest_A, P6_Contest_B, P6_Contest_C, ResultsWaitPage, P7_Round_result, P8_Practice_round_summary, P9_Part1_end, P10_Stimuli, P11_Stimuli_Results, P12_Demographic, P13_Comments, End_game ]