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 0 to 9, 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 0 to 9. Based on these decisions, the principal’s payoff is 0 if the agent rejects the wage offer and 1.5*effort-wage if the agent accepts. Further, the agent’s payoff is 0 if she rejects the wage offer and wage-c(effort) if she accepts. c(effort)=0.13*effort^2 represents the agent’s cost of exerting effort. """ class Constants(BaseConstants): name_in_url = 'Gift_Exchange' players_per_group = 2 num_rounds = 3 min_wage = 0 max_wage = 9 # 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.5 EFFORT_TO_RETURN = { 0: 0, 1: 1.5, 2: 3, 3: 4.5, 4: 6, 5: 7.5, 6: 9, 7: 10.5, 8: 12, 9: 13.5} #Effort² * 0.13 EFFORT_TO_COST = { 0: 0, 1: 0.13, 2: 0.52, 3: 1.17, 4: 2.08, 5: 3.25, 6: 4.68, 7: 6.37, 8: 8.32, 9: 10.53} 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): 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='Fixed Payment (from {} to {})'.format( Constants.min_wage, Constants.max_wage) ) agent_work_effort = models.PositiveIntegerField( choices=range(0,9+1), doc="""Agent's work effort, [0, 9]""", 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 agent accepts proposal""", widget=widgets.RadioSelect(), choices=( (True, 'Accept'), (False, 'Reject'), ) ) 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.total_return - 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'