import numpy as np import random from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) author = 'Jonathan Staebler' doc = """ Sabotage Game for group size uncertainty in the conditional choices part """ class Constants(BaseConstants): name_in_url = 'part_A' players_per_group = 3 num_rounds = 15 endowment = 200 cost_effort = 1 cost_sabotage = 1 donation_base = 10 donation_fraction = 1 externality_effort = 1/6 externatlity_sabotage = 1/6 prize = 200 instructions_template = "sabotage_game_certainty/InstructionsOverview.html" contestants_pool = players_per_group entering_probability = 1 / 4 # Set this to either 3/4 or 1/4, otherwise functionality will not work everywhere! currency_name = 'points' groupSize_template = "sabotage_game_certainty/GroupSizeProbabilities.html" timeout = 180000 # 120'000= 3minutes for JavaScript in html pages timeout2 = 240 # for pages.py file timeoutText = 60 # for text in JavaScript in html pages should be set as the difference of timeout and timeout2 class Subsession(BaseSubsession): def creating_session(self): payoffRounds = random.sample(range(1, 35), 3) for p in self.session.get_participants(): p.vars['payoffRound1'] = payoffRounds[0] p.vars['payoffRound2'] = payoffRounds[1] p.vars['payoffRound3'] = payoffRounds[2] class Group(BaseGroup): ## define group variables group_size_realization = models.IntegerField() winner = models.IntegerField() bonus = models.FloatField() donations = models.FloatField() totalPerformance = models.FloatField() # Realize the group size for the group def realize_group_size(self): self.group_size_realization = np.random.binomial(Constants.contestants_pool, Constants.entering_probability) while self.group_size_realization == 0: self.group_size_realization = np.random.binomial(Constants.contestants_pool, Constants.entering_probability) # Calculate the performances and probabilities def set_performances_probabilities_and_winner(self): players = self.get_players() contestants = random.sample(players, self.group_size_realization) # Initialize a value of False for everyone for p in players: p.active = False # Only if active (i.e. part of the contestants group) set active to True. for p in contestants: p.active = True for p in players: if self.group_size_realization == 1: p.effort = p.effort1 p.sabotage = p.sabotage1 p.sabotage_received = 0 #For functionality of the payoff of the beliefs, store the belief for the chosen group size. p.belief_effort = p.belief_effort1 p.belief_sabotage = p.belief_sabotage1 elif self.group_size_realization == 2: p.effort = p.effort2 p.sabotage = p.sabotage2 p.sabotage_received = sum([o.sabotage2 for o in contestants]) - p.sabotage2 p.belief_effort = p.belief_effort2 p.belief_sabotage = p.belief_sabotage2 elif self.group_size_realization == 3: p.effort = p.effort3 p.sabotage = p.sabotage3 p.sabotage_received = sum([o.sabotage3 for o in contestants]) - p.sabotage3 p.belief_effort = p.belief_effort3 p.belief_sabotage = p.belief_sabotage3 # Calculate the performances and probabilities for p in contestants: p.performance = p.effort / (1 + p.sabotage_received) total_performance = sum([o.performance for o in contestants]) for p in contestants: if total_performance == 0: p.probability = 1 / self.group_size_realization else: p.probability = p.performance / total_performance ####Set the winner#### probabilities = [p.probability for p in contestants] id_contestants = [p.id_in_group for p in contestants] self.winner = np.random.choice(id_contestants, 1, p=probabilities) self.donations = total_performance*Constants.donation_fraction+Constants.donation_base self.totalPerformance = round(total_performance, 2) #self.bonus = Constants.externality_effort * sum([o.effort for o in contestants]) - \ ## Constants.externatlity_sabotage * sum([o.sabotage for o in contestants]) for p in contestants: # Round numbers for display purposes p.performance = round(p.performance, 2) p.probability = round(p.probability, 3) self.donations = round(self.donations, 2) #self.bonus = round(self.bonus, 2) # contestants = [p.id_in_group] # contestants.extend([o.id_in_group for o in opponents]) def set_payoff_round(self): players = self.get_players() for p in players: if p.active: if p.id_in_group == p.group.winner: p.payoff_round = Constants.endowment - p.effort * Constants.cost_effort - \ p.sabotage * Constants.cost_sabotage + \ Constants.prize #+ p.group.bonus else: p.payoff_round = Constants.endowment - p.effort * Constants.cost_effort - \ p.sabotage * Constants.cost_sabotage# + p.group.bonus else: p.payoff_round = Constants.endowment# + p.group. #### Set random other active and inactive group members for individual randomized display purposes def set_individual_display(self): players = self.get_players() for p in players: ### Change this for the n=3 treatment ### Conditional on the group size realization, make a list of active players ### Also make a list of non-active players. In the end first give the list of active to the first other ### players and then fill up with non-active. For the display purpose then make conditional on group size ## everything. ### Also add the id, so I can identify the other players still (to display the rightful winner) ### Everything depends also if the person him/herself is active or not.... # First make lists of other active and inactive (depending on the group size realization some of them will # just not be used e_others_active = [p.effort_other1, p.effort_other2] s_others_active = [p.sabotage_other1, p.sabotage_other2] perf_others_active = [p.performance_other1, p.performance_other2] prob_others_active = [p.probability_other1, p.probability_other2] id_others_active = [p.id_other1, p.id_other2] e_others_inactive = [p.effort_other1, p.effort_other2] s_others_inactive = [p.effort_other1, p.sabotage_other2] perf_others_inactive = [p.performance_other1, p.performance_other2] prob_others_inactive = [p.probability_other1, p.probability_other2] id_others_inactive = [p.id_other1, p.id_other2] ### get others and put them in a random order others = p.get_others_in_group() others_mixed = random.sample(others, len(others)) e = 0 #e is the indice of all other active players u = 0 #u is the idice of all other inactive players i = 0 #is the indice of all other players while i < 2: #If the pivot player is active, give data and go to the next player and next active player if others_mixed[i].active: e_others_active[e] = others_mixed[i].effort s_others_active[e] = others_mixed[i].sabotage perf_others_active[e] = others_mixed[i].performance prob_others_active[e] = round(100*others_mixed[i].probability, 1) id_others_active[e] = others_mixed[i].id_in_group e = e+1 i = i+1 #If the pivot player is inactive, give data and go to the next player and next inactive player else: e_others_inactive[u] = others_mixed[i].effort s_others_inactive[u] = others_mixed[i].sabotage id_others_inactive[u] = others_mixed[i].id_in_group i = i+1 u = u+1 # Give the others values to the players models conditional on being active herself. for realization 3 it is # clear hence can be done without the conditionality. if self.group_size_realization == 3: p.effort_other1 = e_others_active[0] p.effort_other2 = e_others_active[1] p.sabotage_other1 = s_others_active[0] p.sabotage_other2 = s_others_active[1] p.performance_other1 = perf_others_active[0] p.performance_other2 = perf_others_active[1] p.probability_other1 = prob_others_active[0] p.probability_other2 = prob_others_active[1] p.id_other1 = id_others_active[0] p.id_other2 = id_others_active[1] ## pivot player active if p.active: if self.group_size_realization == 2: p.effort_other1 = e_others_active[0] p.effort_other2 = e_others_inactive[0] p.sabotage_other1 = s_others_active[0] p.sabotage_other2 = s_others_inactive[0] p.performance_other1 = perf_others_active[0] p.probability_other1 = prob_others_active[0] p.id_other1 = id_others_active[0] p.id_other2 = id_others_inactive[0] elif self.group_size_realization == 1: p.effort_other1 = e_others_inactive[0] p.effort_other2 = e_others_inactive[1] p.sabotage_other1 = s_others_inactive[0] p.sabotage_other2 = s_others_inactive[1] p.id_other1 = id_others_inactive[0] p.id_other2 = id_others_inactive[1] ### pivot player non-active the others into the correct state of either active or inactive. else: if self.group_size_realization == 2: p.effort_other1 = e_others_active[0] p.effort_other2 = e_others_active[1] p.sabotage_other1 = s_others_active[0] p.sabotage_other2 = s_others_active[1] p.performance_other1 = perf_others_active[0] p.performance_other2 = perf_others_active[1] p.probability_other1 = prob_others_active[0] p.probability_other2 = prob_others_active[1] p.id_other1 = id_others_active[0] p.id_other2 = id_others_active[1] elif self.group_size_realization == 1: p.effort_other1 = e_others_active[0] p.effort_other2 = e_others_inactive[0] p.sabotage_other1 = s_others_active[0] p.sabotage_other2 = s_others_inactive[0] p.performance_other1 = perf_others_active[0] p.probability_other1 = prob_others_active[0] p.id_other1 = id_others_active[0] p.id_other2 = id_others_inactive[0] class Player(BasePlayer): # These are the ultimately chosen values effort1 = models.IntegerField(widget=widgets.Slider, doc="The effort put from one participant for a group size of 1", max=Constants.endowment, min=0) effort2 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) effort3 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) effort4 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) effort5 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) effort = models.IntegerField(widget=widgets.Slider, doc="The effort from the realized group size") sabotage1 = models.IntegerField(widget=widgets.Slider, doc="sabotage put from one participant for a group size of 1", max=Constants.endowment, min=0) sabotage2 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) sabotage3 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) sabotage4 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) sabotage5 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) sabotage = models.IntegerField(widget=widgets.Slider, doc="The sabotage from the realized group size") group_size_realization = models.IntegerField() sabotage_received = models.IntegerField() performance = models.FloatField() probability = models.FloatField() winner = models.IntegerField() # Integer because it will hold the ID number of the winning player payoff_round = models.IntegerField() ## Payoff per round, since the oTree payoff will only get the chosen payoff # from one random round active = models.BooleanField() ### Theses are the expectations for the choices of the opponents belief_effort1 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_effort2 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_effort3 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_effort4 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_effort5 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_effort = models.IntegerField() belief_sabotage1 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_sabotage2 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_sabotage3 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_sabotage4 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_sabotage5 = models.IntegerField(widget=widgets.Slider, max=Constants.endowment, min=0) belief_sabotage = models.IntegerField() # Fields of the calculator effort_calc_try = models.IntegerField() sabotage_calc_try = models.IntegerField() effort_opponent_calc_try = models.IntegerField() sabotage_opponent_calc_try = models.IntegerField() effort_p1_calc_try = models.IntegerField() sabotage_p1_calc_try = models.IntegerField() effort_p2_calc_try = models.IntegerField() sabotage_p2_calc_try = models.IntegerField() effort_p3_calc_try = models.IntegerField() sabotage_p3_calc_try = models.IntegerField() effort_p4_calc_try = models.IntegerField() sabotage_p4_calc_try = models.IntegerField() effort_p5_calc_try = models.IntegerField() sabotage_p5_calc_try = models.IntegerField() clicksEffort_calc_try = models.IntegerField() clicksSabotage_calc_try = models.IntegerField() clicksOpponentEffort_calc_try = models.IntegerField() clicksOpponentSabotage_calc_try = models.IntegerField() clicksEffort_calc_advanced_try = models.IntegerField() clicksSabotage_calc_advanced_try = models.IntegerField() clicksOpponentEffort_calc_advanced_try = models.IntegerField() clicksOpponentSabotage_calc_advanced_try = models.IntegerField() ##Fields for Average Choices Others average_A_others = models.FloatField() average_B_others = models.FloatField() # Others on individual level effort_other1 = models.IntegerField() sabotage_other1 = models.IntegerField() performance_other1 = models.FloatField() probability_other1 = models.FloatField() id_other1 = models.IntegerField() effort_other2 = models.IntegerField() sabotage_other2 = models.IntegerField() performance_other2 = models.FloatField() probability_other2 = models.FloatField() id_other2 = models.IntegerField() effort_other3 = models.IntegerField() sabotage_other3 = models.IntegerField() performance_other3 = models.FloatField() probability_other3 = models.FloatField() id_other3 = models.IntegerField() effort_other4 = models.IntegerField() sabotage_other4 = models.IntegerField() performance_other4 = models.FloatField() probability_other4 = models.FloatField() id_other4 = models.IntegerField() ## Set average choices from others for the payoff from the belief elicitation def set_average_choices_others(self): o = self.get_others_in_group() self.average_A_others = sum([a.effort for a in o]) / len(o) self.average_B_others = sum([a.sabotage for a in o]) / len(o)