from otree.api import * import random import time doc = """ oTree implementation of the Strategy Method for the Public Goods Game (Linear VCM) as discussed in Fischbacher, Gächter, and Quercia 2012. """ class C(BaseConstants): NAME_IN_URL = 'StrategyMethod' PLAYERS_PER_GROUP = 4 NUM_ROUNDS = 1 MULTIPLIER = 2 ENDOWMENT = cu(20) def creating_session(subsession): subsession.group_randomly() class Subsession(BaseSubsession): pass class Group(BaseGroup): total_investment = models.CurrencyField() individual_share = models.CurrencyField() unconditional_total_investment = models.CurrencyField() average_unconditional_investment = models.CurrencyField() conditional_player_investment = models.CurrencyField() # Function used to generate fields of player class def make_conditional_investment(label): return models.CurrencyField( min=0, max=C.ENDOWMENT, label=label ) class Player(BasePlayer): # Unconditional Investment Field unconditional_investment = models.CurrencyField( min=0, max=C.ENDOWMENT, label="How much will you invest in the Group's Joint Account?" ) # Fields for Conditional Investment conditional_investment_0 = make_conditional_investment("Average Investment is 0") conditional_investment_1 = make_conditional_investment("Average Investment is 1") conditional_investment_2 = make_conditional_investment("Average Investment is 2") conditional_investment_3 = make_conditional_investment("Average Investment is 3") conditional_investment_4 = make_conditional_investment("Average Investment is 4") conditional_investment_5 = make_conditional_investment("Average Investment is 5") conditional_investment_6 = make_conditional_investment("Average Investment is 6") conditional_investment_7 = make_conditional_investment("Average Investment is 7") conditional_investment_8 = make_conditional_investment("Average Investment is 8") conditional_investment_9 = make_conditional_investment("Average Investment is 9") conditional_investment_10 = make_conditional_investment("Average Investment is 10") conditional_investment_11 = make_conditional_investment("Average Investment is 11") conditional_investment_12 = make_conditional_investment("Average Investment is 12") conditional_investment_13 = make_conditional_investment("Average Investment is 13") conditional_investment_14 = make_conditional_investment("Average Investment is 14") conditional_investment_15 = make_conditional_investment("Average Investment is 15") conditional_investment_16 = make_conditional_investment("Average Investment is 16") conditional_investment_17 = make_conditional_investment("Average Investment is 17") conditional_investment_18 = make_conditional_investment("Average Investment is 18") conditional_investment_19 = make_conditional_investment("Average Investment is 19") conditional_investment_20 = make_conditional_investment("Average Investment is 20") # The participant's investment that became payoff-relevant realized_investment = models.CurrencyField() personal_account = models.CurrencyField() # For tractability, store which player was selected as the 'conditional investor' am_i_the_conditional_investor = models.StringField() # Since oTree only allows for one conversion rate to be used across apps, this value allows me to scale # subjects earnings from the SM with the x10 value I want from SM as compared to the repeated iteration PGG uncorrected_payoff = models.CurrencyField() # Track decision time start_time = models.FloatField(initial=0) time_spent_unconditional = models.FloatField(initial=0) time_spent_conditional = models.FloatField(inital=0) time_spent_results = models.FloatField(initial=0) # Functions def make_player_conditional_investment_dictionary(player: Player): conditional_investment_dictionary = { 0: player.conditional_investment_0, 1: player.conditional_investment_1, 2: player.conditional_investment_2, 3: player.conditional_investment_3, 4: player.conditional_investment_4, 5: player.conditional_investment_5, 6: player.conditional_investment_6, 7: player.conditional_investment_7, 8: player.conditional_investment_8, 9: player.conditional_investment_9, 10: player.conditional_investment_10, 11: player.conditional_investment_11, 12: player.conditional_investment_12, 13: player.conditional_investment_13, 14: player.conditional_investment_14, 15: player.conditional_investment_15, 16: player.conditional_investment_16, 17: player.conditional_investment_17, 18: player.conditional_investment_18, 19: player.conditional_investment_19, 20: player.conditional_investment_20 } return conditional_investment_dictionary def set_payoffs(group: Group): """ Performs payoff calculation as specified in Fischbacher, Gächter, and Quercia 2012 in a code implementation :param group: Each Group instance in the experiment utilizes this function :return: stores Payoff and other information to the Group and Player instances """ players = group.get_players() # Choose a player to have their cooperation be one of their cond coops conditional_player = random.choice(players) # Store who was selected for p in players: if p == conditional_player: p.am_i_the_conditional_investor = 'Selected' else: p.am_i_the_conditional_investor = 'Not Selected' # Compute the unconditional investment values unconditional_investments = [p.unconditional_investment for p in players] unconditional_investments[(conditional_player.id_in_group - 1)] = 0 average_investments = int(sum(unconditional_investments) / (C.PLAYERS_PER_GROUP - 1)) # Save the below to be displayed to participants as a part of Results group.unconditional_total_investment = sum(unconditional_investments) group.average_unconditional_investment = average_investments # From the average, find its corresponding conditional cooperation conditional_player_conditional_cooperation_dictionary = \ make_player_conditional_investment_dictionary(conditional_player) unconditional_investments[(conditional_player.id_in_group - 1)] = \ conditional_player_conditional_cooperation_dictionary.get(average_investments) # Store the selected player's conditional cooperation to be displayed as part of Results group.conditional_player_investment = unconditional_investments[(conditional_player.id_in_group - 1)] # Calculate total investment and individual share for each player in a group group.total_investment = sum(unconditional_investments) group.individual_share = ( group.total_investment * C.MULTIPLIER / C.PLAYERS_PER_GROUP ) # Calculate payoff and realized investment for each player for p in players: p.uncorrected_payoff = C.ENDOWMENT - unconditional_investments[(p.id_in_group - 1)] + group.individual_share p.payoff = p.uncorrected_payoff * 10 # 10 because I have from FGQ-2012 that the SM is 10x stronger than 20 R p.realized_investment = unconditional_investments[(p.id_in_group - 1)] p.personal_account = C.ENDOWMENT - p.realized_investment # PAGES class TransitionToUnconditional(Page): pass class UnconditionalInvestment(Page): """ Place for participant interaction for their Unconditional Investment Question. Only experienced once per session """ form_model = 'player' form_fields = ['unconditional_investment'] @staticmethod def is_displayed(player: Player): player.start_time = time.time() return True @staticmethod def before_next_page(player: Player, timeout_happened): player.time_spent_unconditional = time.time() - player.start_time # Save invest decision time spent class TransitionToConditional(Page): pass class ConditionalInvestment(Page): """ Place for participant interaction for their Conditional Investment Questions. Experienced Endowment + 1 times per session. """ form_model = 'player' form_fields = ['conditional_investment_0', 'conditional_investment_1', 'conditional_investment_2', 'conditional_investment_3', 'conditional_investment_4', 'conditional_investment_5', 'conditional_investment_6', 'conditional_investment_7', 'conditional_investment_8', 'conditional_investment_9', 'conditional_investment_10', 'conditional_investment_11', 'conditional_investment_12', 'conditional_investment_13', 'conditional_investment_14', 'conditional_investment_15', 'conditional_investment_16', 'conditional_investment_17', 'conditional_investment_18', 'conditional_investment_19', 'conditional_investment_20'] @staticmethod def is_displayed(player: Player): player.start_time = time.time() return True @staticmethod def before_next_page(player: Player, timeout_happened): player.time_spent_conditional = time.time() - player.start_time # Save invest decision time spent class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs class Results(Page): @staticmethod def is_displayed(player: Player): player.start_time = time.time() return True @staticmethod def before_next_page(player: Player, timeout_happened): player.time_spent_results = time.time() - player.start_time # Save results time spent page_sequence = [TransitionToUnconditional, UnconditionalInvestment, TransitionToConditional, ConditionalInvestment, ResultsWaitPage, Results]