from otree.api import *
import random
doc = """
Asynchronous 2-player sequential game (players can play at different times),
where we guarantee players never have to wait.
The example used is an ultimatum game.
The first player who arrives is assigned to be P1.
If the next player arrives after P1 has made a decision, he will be paired with P1 and see P1's decision.
Otherwise, he will be P1 in a new group.
Worst-case scenario is that all players arrive around the same time,
and therefore everyone gets assigned to be P1.
This game doesn't use oTree groups.
Rather, it stores the partner's ID in a player field.
"""
class C(BaseConstants):
NAME_IN_URL = 'human_ult'
PLAYERS_PER_GROUP = None
NUM_ROUNDS = 1
ENDOWMENT = cu(79)
class Subsession(BaseSubsession):
pass
def creating_session(subsession: Subsession):
session = subsession.session
# queue of players who are finished.
session.finished_p1_list = []
class Group(BaseGroup):
pass
class Player(BasePlayer):
is_p1 = models.BooleanField()
offer = models.CurrencyField(
min=0,
max=C.ENDOWMENT,
label="What is your offer to the other participant? (Amount between $0 and $79)",
doc="This field is only used if the player is P1",
)
potsize = models.CurrencyField(
min=5,
max=90,
label="How much will you tell the other participant the pot size is? (Amount between $5-$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()
def set_payoff(player: Player):
if player.bot_accepted:
player.payoff = C.ENDOWMENT - player.offer
else:
player.payoff = cu(0)
class HumanU_Intro(Page):
form_model = 'player'
form_fields = ['quiz1', 'quiz2', 'quiz3', 'quiz4']
@staticmethod
def error_message(player: Player, values):
solutions = dict(quiz1=C.ENDOWMENT, quiz2='$5 - $90', quiz3='The pot money minus my offer amount', quiz4=False)
if values != solutions:
return "One or more answers were incorrect. Please reread the instructions and try again."
class HumanU_Offer(Page):
form_model = 'player'
form_fields = ['offer', 'potsize']
@staticmethod
def before_next_page(player: Player, timeout_happened):
half_pot_size = player.potsize / 2
half_endowment = (C.ENDOWMENT - 1) / 2
# Condition 1: Accept if offer is equal to or greater than one less than half of the endowment
if 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 HumanU_Results(Page):
@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
)
page_sequence = [HumanU_Intro, HumanU_Offer, HumanU_Results]