from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random doc = """ Explanation here: There are three periods. Before this, half of the subjects are assigned the role of principal, and half of the subjects the role of the agent. This should be done by random. In each of the three periods, the game is as follows: A principal is matched with an agent by random (this happens in every period) The principal offers a wage w, which is an integer from 10 to 100, to the agent. Upon receiving the wage offer w, the agent then first decides whether or not she accepts (a) or rejects (r). If she rejects, no further decisions are made in the given period. If she accepts, she then decides how much effort e she is going to exert, which is an integer from 1 to 10. Based on these decisions, the principal’s payoff is 0 if the agent rejects the wage offer and [(240– wage)* (effort level/10)] if the agent accepts. Further, the agent’s payoff is 0 if she rejects the wage offer and wage-c(effort) if she accepts. The cost of effort c(effort) is predefined. """ class Constants(BaseConstants): name_in_url = 'Gift_Exchange_T3' players_per_group = 2 num_rounds = 10 random_payoff = True Results = True num_apps_to_pay = 2 instructions_template = 'Gift_Exchange_T3/Instructions.html' min_wage = 10 max_wage = 100 # if agent rejects offer -> principal payoff = 0 #only needed if accept and reject method is implemented reject_principal_payoff = c(0) reject_agent_payoff = c(0) #Effort * 1/10 (Principal return) EFFORT_TO_RETURN = { 1: 0.1, 2: 0.2, 3: 0.3, 4: 0.4, 5: 0.5, 6: 0.6, 7: 0.7, 8: 0.8, 9: 0.9, 10: 1,} #Cost of Effort (Agent's looses) EFFORT_TO_COST = { 1: 0, 2: 1, 3: 2, 4: 4, 5: 6, 6: 8, 7: 10, 8: 12, 9: 15, 10: 18,} def cost_from_effort(effort): return c(Constants.EFFORT_TO_COST[effort]) def return_from_effort(effort): return c(Constants.EFFORT_TO_RETURN[effort]) class Subsession(BaseSubsession): pass class Group(BaseGroup): # --- set round results and player's payoff # -------------------------------------------------------------------- pay_this_round = models.BooleanField() round_result = models.CurrencyField() total_return = models.CurrencyField( doc="""Total return from agent's effort = [Return for single unit of agent's work effort] * [Agent's work effort]""" ) principal_wage_offer = models.PositiveIntegerField( doc="""Amount offered as fixed pay to agent""", min=Constants.min_wage, max=Constants.max_wage, verbose_name='Wage offer (from {} to {})'.format( Constants.min_wage, Constants.max_wage) ) desired_agent_work_effort = models.PositiveIntegerField( choices=range(1, 10 + 1), doc="""Agent's work effort, [1, 10]""", widget=widgets.RadioSelectHorizontal(), ) agent_work_effort = models.PositiveIntegerField( choices=range(1,10+1), doc="""Agent's work effort, [1, 10]""", widget=widgets.RadioSelectHorizontal(), ) agent_work_cost = models.CurrencyField( doc="""Agent's cost of work effort""" ) # only needed if accept or reject option is implemented contract_accepted = models.BooleanField( doc="""Whether principal delegates wage decision""", widget=widgets.RadioSelect(), choices=( (True, 'Yes'), (False, 'No'), ) ) def set_payoffs(self): principal = self.get_player_by_role('principal') agent = self.get_player_by_role('agent') # if not self.contract_accepted: # principal.payoff = Constants.reject_principal_payoff # agent.payoff = Constants.reject_agent_payoff # else: self.agent_work_cost = cost_from_effort(self.agent_work_effort) self.total_return = return_from_effort(self.agent_work_effort) money_to_agent = self.principal_wage_offer agent.payoff = money_to_agent - self.agent_work_cost principal.payoff = (self.agent_work_effort/10) * (240-money_to_agent) class Player(BasePlayer): def role(self): if self.id_in_group == 1: return 'principal' if self.id_in_group == 2: return 'agent' # set payoffs if to round_result of randomly chosen round # randomly determine round to pay on player level if self.subsession.round_number == Constants.num_rounds: self.participant.vars['round_to_pay'] = random.randint(1, Constants.num_rounds) if Constants.random_payoff: if self.subsession.round_number == self.participant.vars['round_to_pay']: self.pay_this_round = True self.payoff = self.round_result else: self.pay_this_round = False self.payoff = c(0) # set payoffs to round_result if else: self.payoff = self.round_result