from otree.api import * import random doc = """ Ultimatum game AI only Comp/Coop Moderation Version 2 """ class C(BaseConstants): NAME_IN_URL = 'UltMod2' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 ENDOWMENT = cu(79) TREATMENTS = ['cooperative', 'competitive'] class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): players = subsession.get_players() last_condition = None for player in players: # Check if the previous player was a dropout prev_player = player.in_all_rounds()[-2] if len(player.in_all_rounds()) > 1 else None if prev_player and prev_player.is_dropout: # Assign this player the same condition as the previous dropout player player.condition = prev_player.condition else: # Alternate between 'cooperative' and 'competitive' conditions if last_condition == 'cooperative': player.condition = 'competitive' else: player.condition = 'cooperative' print(f"Assigning player {player.id_in_group} to condition {player.condition}") # Update last_condition last_condition = player.condition # print(f"Assigning player {player.id_in_group} to condition {player.condition}") # num_players = len(players) # last_player = players[-1] if players else None # for i, player in enumerate(players, start=1): # if last_player and last_player.is_dropout: # # Assign this player as a makeup for the last dropout # player.condition = last_player.condition # else: # # Alternate between the two treatments for each player # if i % 2 == 0: # player.condition = 'competitive' # else: # player.condition = 'cooperative' # print(f"Assigning player {player.id_in_group} to condition {player.condition}") class Group(BaseGroup): pass class Player(BasePlayer): condition = models.StringField() is_p1 = models.BooleanField() offer = models.CurrencyField( min=0, max=C.ENDOWMENT, doc="This field is only used if the player is P1", ) potsize = models.CurrencyField( min=5, max=90, doc="This field is only used if the player is P1", ) partner_id = models.IntegerField( doc="This field is only used if the player is P2. It stores the ID of P1", ) accepted = models.BooleanField( label="Do you accept Player 1's offer?", doc="This field is only used if the player is P2", ) bot_accepted = models.BooleanField() quiz1 = models.IntegerField(label='The size of the pot is $____.') quiz2a = models.StringField( label="The AI bot is told that the pot size ranges between _____.", choices=['$5 - $100', '$0 - $70', '$5 - $90'],) # quiz2h = models.StringField( # label="The other participant is told that the pot size ranges between _____.", # choices=['$5 - $100', '$0 - $70', '$5 - $90'],) quiz3 = models.StringField( label="As the first participant, I will receive ____.", choices=['All of the pot money', 'The pot money minus my offer amount', 'None of the pot money'],) quiz4a = models.BooleanField(label="The AI bot knows the actual size of the pot. In other words, the bot will know if you are telling the truth about the pot size.") # quiz4h = models.BooleanField(label="The other participant knows the actual size of the pot. In other words, your partner will know if you are telling the truth about the pot size.") quiz5 = models.StringField( label="What training data has this bot been given?", choices=['Generic internet data', 'Techniques for competitive negotiations', 'Techniques for cooperative negotiations','No data'],) is_dropout = models.BooleanField(initial=False) def set_payoff(player: Player): if player.bot_accepted: player.payoff = C.ENDOWMENT - player.offer else: player.payoff = cu(0) ### COOPERATIVE VERSION ### class UOIntro(Page): form_model = 'player' @staticmethod def is_displayed(player): return player.condition == 'cooperative' @staticmethod def before_next_page(player: Player, timeout_happened): session = player.session class UOInstruct(Page): timeout_seconds = 180 form_model = 'player' form_fields = ['quiz1', 'quiz2a', 'quiz3', 'quiz4a', 'quiz5'] @staticmethod def before_next_page(player: Player, timeout_happened): # If timeout happens, mark as dropout if timeout_happened: player.is_dropout = True return @staticmethod def is_displayed(player): return player.condition == 'cooperative' and not player.is_dropout @staticmethod def error_message(player: Player, values): solutions = dict(quiz1=C.ENDOWMENT, quiz2a='$5 - $90', quiz3='The pot money minus my offer amount', quiz4a=False, quiz5='Techniques for cooperative negotiations') if values != solutions: return "One or more answers were incorrect. Please reread the instructions and try again." class UOOffer(Page): timeout_seconds = 180 form_model = 'player' form_fields = ['offer', 'potsize'] @staticmethod def is_displayed(player): return player.condition == 'cooperative' and not player.is_dropout @staticmethod def vars_for_template(player: Player): offer_label = "What is your offer to the AI bot? (Amount between $0 and $79 to give to the AI bot)" potsize_label = "How much will you tell the AI bot the pot size is? (Amount between $5-$90)" return { 'offer_label': offer_label, 'potsize_label': potsize_label } @staticmethod def before_next_page(player: Player, timeout_happened): # If timeout happens, mark as dropout if timeout_happened: player.is_dropout = True return session = player.session # Rest of your code for the offer... half_pot_size = player.potsize / 2 half_endowment = (C.ENDOWMENT - 1) / 2 # Condition 0: Accept if offer is equal to or greater than 1 if player.offer >= 1: player.bot_accepted = True # Condition 1: Accept if offer is equal to or greater than one less than half of the endowment elif player.offer >= half_endowment: player.bot_accepted = True # Condition 2: Reject if offer is less than half of the stated pot size elif player.offer < half_pot_size: player.bot_accepted = False # Condition 3: Randomly accept or reject if in between else: player.bot_accepted = random.choice([True, False]) # Ensure the payoff is calculated based on this decision set_payoff(player) class UOResults(Page): @staticmethod def is_displayed(player): return player.condition == 'cooperative' and not player.is_dropout @staticmethod def vars_for_template(player: Player): bot_decision = "Accepted" if player.bot_accepted else "Rejected" bot_payoff = player.offer if player.bot_accepted else cu(0) player_payoff=player.payoff return dict( bot_decision=bot_decision, bot_payoff=bot_payoff ) ### COMPETITIVE PARTNERSHIP VERSION ### class UMIntro(Page): form_model = 'player' # form_fields = ['quiz1', 'quiz2h', 'quiz3', 'quiz4h'] @staticmethod def is_displayed(player): return player.condition == 'competitive' @staticmethod def before_next_page(player: Player, timeout_happened): session = player.session class UMInstruct(Page): timeout_seconds = 180 form_model = 'player' # All players get AI condition - change to h for human form_fields = ['quiz1', 'quiz2a', 'quiz3', 'quiz4a', 'quiz5'] @staticmethod def is_displayed(player): return player.condition == 'competitive' and not player.is_dropout @staticmethod def before_next_page(player: Player, timeout_happened): # If timeout happens, mark as dropout if timeout_happened: player.is_dropout = True return @staticmethod def error_message(player: Player, values): solutions = dict(quiz1=C.ENDOWMENT, quiz2a='$5 - $90', quiz3='The pot money minus my offer amount', quiz4a=False, quiz5='Techniques for competitive negotiations') if values != solutions: return "One or more answers were incorrect. Please reread the instructions and try again." class UMOffer(Page): timeout_seconds = 180 form_model = 'player' form_fields = ['offer', 'potsize'] @staticmethod def is_displayed(player): return player.condition == 'competitive' and not player.is_dropout @staticmethod def vars_for_template(player: Player): # Both conditions get the AI version - edit this for the human version # offer_label = "What is your offer to the other participant? (Amount between $0 and $79)" # potsize_label = "How much will you tell the other participant the pot size is? (Amount between $5-$90)" offer_label = "What is your offer to the AI bot? (Amount between $0 and $79 to give to the AI bot)" potsize_label = "How much will you tell the AI bot the pot size is? (Amount between $5-$90)" return { 'offer_label': offer_label, 'potsize_label': potsize_label } @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.is_dropout = True return session = player.session half_pot_size = player.potsize / 2 half_endowment = (C.ENDOWMENT - 1) / 2 # Condition 0: Accept if offer is equal to or greater than 1 ### HARD CODED TO ACCEPT EVERYTHING above $0! if player.offer >= 1: player.bot_accepted = True # Condition 1: Accept if offer is equal to or greater than one less than half of the endowment elif player.offer >= half_endowment: player.bot_accepted = True # Condition 2: Reject if offer is less than half of the stated pot size elif player.offer < half_pot_size: player.bot_accepted = False # Condition 3: Randomly accept or reject if in between else: player.bot_accepted = random.choice([True, False]) # Ensure the payoff is calculated based on this decision set_payoff(player) class UMResults(Page): @staticmethod def is_displayed(player): return player.condition == 'competitive' and not player.is_dropout @staticmethod def vars_for_template(player: Player): bot_decision = "Accepted" if player.bot_accepted else "Rejected" bot_payoff = player.offer if player.bot_accepted else cu(0) player_payoff=player.payoff return dict( bot_decision=bot_decision, bot_payoff=bot_payoff ) class UTimeout(Page): @staticmethod def is_displayed(player: Player): return player.is_dropout @staticmethod def error_message(player: Player, values): return "Cannot proceed past this page" page_sequence = [UOIntro, UMIntro, UOInstruct, UMInstruct, UOOffer, UMOffer, UTimeout, UOResults, UMResults]