from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random import time author = 'Wang Song' doc = """ Designated Vouchers as second stage """ class Constants(BaseConstants): name_in_url = 'designated2' players_per_group = 24 num_rounds = 45 # TBC, subject to time # instructions_template = num_Afailure = 2 num_Bfailure = 6 endowment = c(4) period_income_healthy = c(3) # earn $3 if survived period_income_death = c(-1) # loss $1 if not survived parents_care = 0.5 candidate_donation_cost = [0.5, 0.5, 0.5, 0.5, 2, 2, 2, 2] minutes_for_timeout = 5 # 5 minutes minutes_for_endofround = 35 # 35 minutes minutes_for_dice = 5 # 5 minutes class Subsession(BaseSubsession): def creating_session(self): self.session.vars['num_rounds'] = Constants.num_rounds # set group_id: 1, 2, or 0; # and initialize payoff if self.round_number == 1: self.session.vars['diced'] = -1 for p in self.get_players(): p.participant.vars['sec_round_own_payoffs'] = [] #if p.id_in_group in list(range(1, 9)): # p.participant.vars['group_id'] = 1 # Group A #elif p.id_in_group in list(range(9, 17)): # p.participant.vars['group_id'] = 2 # Group B #else: # p.participant.vars['group_id'] = 0 # Group C! # set donation_cost # later need to move out the app # if self.round_number == 1: # cost1 = Constants.candidate_donation_cost.copy() # cost2 = Constants.candidate_donation_cost.copy() # cost3 = Constants.candidate_donation_cost.copy() # random.shuffle(cost1) # random.shuffle(cost2) # random.shuffle(cost3) # for p in self.get_players(): # if p.participant.vars['group_id'] == 1: # p.participant.vars['donation_cost'] = c(cost1.pop()) # elif p.participant.vars['group_id'] == 2: # p.participant.vars['donation_cost'] = c(cost2.pop()) # else: # p.participant.vars['donation_cost'] = c(cost3.pop()) # shuffle roles after every 3 rounds print('in round ', self.round_number, ',') if self.round_number % 3 == 1: print('shuffle group:') print('old group matrix:') print(self.get_group_matrix()) grp1 = [] grp2 = [] grp3 = [] for p in self.get_players(): if p.participant.vars['group_id'] == 1: grp1.append(p.id_in_group) elif p.participant.vars['group_id'] == 2: grp2.append(p.id_in_group) else: grp3.append(p.id_in_group) random.shuffle(grp1) random.shuffle(grp2) random.shuffle(grp3) grp = grp1 + grp2 + grp3 self.set_group_matrix([grp]) print('new group matrix:') print(self.get_group_matrix()) elif self.round_number % 3 == 2: self.group_like_round(self.round_number-1) print('group matrix:') print(self.get_group_matrix()) else: self.group_like_round(self.round_number-2) print('group matrix:') print(self.get_group_matrix()) class Group(BaseGroup): def get_timeout_seconds(self): return self.session.vars['expiry'] - time.time() def get_player_by_grpid(self, grpid): # return the list of group member (8 in total) grp = [] for p in self.get_players(): if p.participant.vars['group_id'] == grpid: grp.append(p) return grp def draw_health_outcome(self, grpid): current_group = self.get_player_by_grpid(grpid) died = random.sample(current_group, 2) for p in died: p.A_health = False for p in current_group: if p.A_health: p.B_health = False def donation_allocation(self, grpid): donors_lst = [] patients_v = [] # patients with vouchers patients_n = [] # patients without vouchers current_group = self.get_player_by_grpid(grpid) for p in current_group: if p.A_health is False and p.is_donor: donors_lst.append(p) if p.B_health is False and (p.is_donor or p.inherit_voucher): # changed for Extended treatment patients_v.append(p) p.used_voucher = True if p.B_health is False and not p.is_donor: patients_n.append(p) random.shuffle(donors_lst) random.shuffle(patients_v) random.shuffle(patients_n) while len(donors_lst) > 0: if len(patients_v) > 0: donors_lst.pop() p = patients_v.pop() p.is_receiver = True elif len(patients_n) > 0: donors_lst.pop() p = patients_n.pop() p.is_receiver = True else: break def check_inheritance_for_players(self, grpid): # for Designated treatment print("check_inheritance_for_players grpid:", grpid) current_group = self.get_player_by_grpid(grpid) if grpid == 0: senior_grpid = 2 else: senior_grpid = grpid - 1 senior_group = self.get_player_by_grpid(senior_grpid) for p in current_group: my_role = p.role() for pp in senior_group: if pp.role() == my_role: psenior = pp senior = psenior.in_round(self.round_number - 1) if senior.designate_junior == 1: p.inherit_voucher = True print("inherit_voucher: True") def set_round_payoff(self, grpid): current_group = self.get_player_by_grpid(grpid) for p in current_group: if p.survived(): pp = Constants.endowment + Constants.period_income_healthy else: pp = Constants.endowment + Constants.period_income_death if p.is_donor: pp = pp - p.participant.vars['donation_cost'] p.payoff = pp # set first, in case being the last round p.own_payoff = pp p.participant.vars['sec_round_own_payoffs'].append(pp) def set_senior_payoff(self, grpid): current_group = self.get_player_by_grpid(grpid) if grpid == 0: senior_grpid = 2 else: senior_grpid = grpid - 1 senior_group = self.get_player_by_grpid(senior_grpid) for p in current_group: my_role = p.role() for pp in senior_group: if pp.role() == my_role: psenior = pp senior = psenior.in_round(self.round_number - 1) senior.junior_payoff = p.own_payoff senior.payoff = (1 - Constants.parents_care) * senior.own_payoff + \ Constants.parents_care * senior.junior_payoff # Update sec_round_own_payoffs temp = senior.participant.vars['sec_round_own_payoffs'].pop() temp = senior.payoff senior.participant.vars['sec_round_own_payoffs'].append(temp) def set_final_payoff(self): for p in self.get_players(): payoff_lst = p.participant.vars['sec_round_own_payoffs'] print("player.id_in_group ", p.id_in_group, "sec_round_own_payoffs", payoff_lst) payoff_index = list(range(len(payoff_lst))) # index starts from 0 picked_index = random.sample(payoff_index, 2) print("picked_index: ", picked_index) grpid = p.participant.vars['group_id'] if grpid == 0: grpid = 3 if picked_index[0] < picked_index[1]: round1 = picked_index[0]*3 + grpid round2 = picked_index[1]*3 + grpid else: round1 = picked_index[1]*3 + grpid round2 = picked_index[0]*3 + grpid print("round1 = ", round1, "; round2 = ", round2) picked1 = p.in_round(round1) picked2 = p.in_round(round2) payoff1 = picked1.payoff payoff2 = picked2.payoff p.participant.vars['sec_picked_r1'] = round1 p.participant.vars['sec_picked_r2'] = round2 p.participant.vars['sec_picked_p1'] = payoff1 p.participant.vars['sec_picked_p2'] = payoff2 p.participant.vars['game2_s2_payoff'] = payoff1 + payoff2 p.sec_picked_r1 = round1 p.sec_picked_r2 = round2 p.sec_picked_p1 = payoff1 p.sec_picked_p2 = payoff2 # for payment_info p.participant.vars['game2_payoff'] = payoff1 + payoff2 + p.participant.vars['game2_s1_payoff'] p.participant.payoff = p.participant.vars['game1_payoff'] + p.participant.vars['game2_payoff'] class Player(BasePlayer): inherit_voucher = models.BooleanField(initial=False) # for Designated treatment is_donor = models.BooleanField(initial=False) designate_junior = models.IntegerField( blank=True, choices=[ [-1, 'Yes and keep the voucher to myself.'], [1, 'Yes and give the voucher to my Junior.'], [0, 'No.'], ], widget=widgets.RadioSelect, ) # for Designated treatment, 0: non-donor, -1: keep, 1: pass A_health = models.BooleanField(initial=True) B_health = models.BooleanField(initial=True) # A_health and B_health are complementary used_voucher = models.BooleanField(initial=False) is_receiver = models.BooleanField(initial=False) # instead of changing B_health to True, change is_receiver to True own_payoff = models.CurrencyField(initial=c(0)) junior_payoff = models.CurrencyField(initial=c(0)) role_index = models.StringField() donation_cost = models.CurrencyField() sec_picked_r1 = models.IntegerField(initial=0) sec_picked_r2 = models.IntegerField(initial=0) sec_picked_p1 = models.CurrencyField(initial=c(0)) sec_picked_p2 = models.CurrencyField(initial=c(0)) def role(self): if self.id_in_group % 8 == 1: return 'a' elif self.id_in_group % 8 == 2: return 'b' elif self.id_in_group % 8 == 3: return 'c' elif self.id_in_group % 8 == 4: return 'd' elif self.id_in_group % 8 == 5: return 'e' elif self.id_in_group % 8 == 6: return 'f' elif self.id_in_group % 8 == 7: return 'g' else: return 'h' def setup(self): self.is_donor = False self.A_health = True self.B_health = True self.used_voucher = False self.is_receiver = False self.inherit_voucher = False # for Designated treatment self.designate_junior = None # for Designated treatment self.role_index = self.role() self.donation_cost = self.participant.vars['donation_cost'] def survived(self): if self.A_health is False: return False elif self.is_receiver is True: return True else: return False