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]