from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) from random import sample, choice, random, shuffle from types import SimpleNamespace from time import time from math import ceil, floor from numpy import array_split author = 'Eva Vriens' doc = """ Sequential Hawk Dove Game - PRIN Honour Norms project """ class Constants(BaseConstants): name_in_url = 'seq_hawk_dove' players_per_group = 5 timer_switch = 4 # From this round onwards decrease time available to make decisions allowed_timeouts = 3 rounds_paid = 4 # Change to 4 # For testing test = False inc_norms = True inc_risksvo = True # Punishment punish_r = 3 # Punishment costs 3 points for receiver punish_s = 1 # Punishment costs 1 point for sender # Incentivization norm questions norm_inc = 1 appr_inc = 0.5 # Stopping rule: maximum number of rounds num_rounds = 3 # 35 # 46 # 51 # Payment exchange_rate = 0.25 sn_pnb = 'seq_hawk_dove/sn_pnb.html' sn_ee = 'seq_hawk_dove/sn_ee.html' sn_ne = 'seq_hawk_dove/sn_ne.html' ap_beh = 'seq_hawk_dove/ap_beh.html' ap_pun = 'seq_hawk_dove/ap_pun.html' class Subsession(BaseSubsession): num_active = models.IntegerField() value = models.IntegerField() cost = models.IntegerField() showup = models.FloatField() endowment = models.IntegerField() reputation = models.BooleanField() round_number2 = models.IntegerField() last_round = models.IntegerField() def creating_session(self): # Cost and value self.endowment = 3 self.showup = 5 if self.session.config['vc_ratio'] == 'high': # ratio = 1/2 self.value = 6 self.cost = 12 else: # ratio = 1/4 self.value = 2 self.cost = 8 self.reputation = self.session.config['reputation'] # Decide on number of rounds # if self.round_number == 1: # if random() <= 0.2: # self.last_round = Constants.num_rounds - 5 # elif random() <= 0.4: # self.last_round = Constants.num_rounds - 4 # elif random() <= 0.6: # self.last_round = Constants.num_rounds - 3 # elif random() <= 0.8: # self.last_round = Constants.num_rounds - 2 # else: # self.last_round = Constants.num_rounds - 1 # print('Last round = ', self.last_round) # else: # self.last_round = self.in_round(1).last_round self.round_number2 = self.round_number + 1 self.last_round = Constants.num_rounds + 1 self.session.vars['last_round'] = self.last_round # if Constants.test: # # Overwrite last round for testing # self.last_round = 3 #Constants.num_rounds - 1 for p in self.get_players(): p.participant.vars['choice_dl'] = 2 p.participant.vars['pun_payoff'] = 0 def shuffle_groups(self): # Sort attackers all_players = self.get_players() attackers = [p for p in all_players if p.participant.vars['role'] == 'attacker'] sorted_attackers = sorted( attackers, key=lambda player: player.participant.vars['score'] ) if not self.session.config['group']: # Sort defenders individual treatments: defenders = [p for p in all_players if p.participant.vars['role'] == 'defender'] sorted_defenders = sorted( defenders, key=lambda player: player.participant.vars['score'] ) group_matrix = [] dpg = Constants.players_per_group - 1 # defenders per group for i in range(0, len(sorted_defenders), dpg): group_matrix.append(sorted_defenders[i:i + dpg]) # Add attacker to each defender group print(group_matrix) for defenders, attacker in zip(group_matrix, sorted_attackers): defenders.append(attacker) print(group_matrix) else: # For group treatments: defender groups remain the same in all rounds sorted_attackers = [p.id_in_subsession for p in sorted_attackers] first_round_matrix = self.session.vars['group_matrix'] # self.in_round(1).get_group_matrix() grouped_defenders = [] sumscores = [] for playergroup in first_round_matrix: # Retrieve defender groups defender_ids = [p.id_in_subsession for p in all_players if p.id_in_subsession in playergroup and p.participant.vars['role'] == 'defender'] print('Defender_ids = ', defender_ids) grouped_defenders.append(defender_ids) # Calculate scores for sorting scores = [p.participant.vars['score'] for p in all_players if p.id_in_subsession in playergroup and p.participant.vars['role'] == 'defender'] print('scores are', scores) scores = [s if s == 1 else 0 for s in scores] print('scores are', scores) sumscores.append(sum(scores)) # Sort defender groups based on # inactive defenders combine = zip(sumscores, grouped_defenders) combine_sorted = sorted(combine) print('Old list = ', grouped_defenders) group_matrix = [element for _, element in combine_sorted] print('Sorted list = ', group_matrix) # Add attacker to each defender group for defenders, attacker in zip(group_matrix, sorted_attackers): defenders.append(attacker) print('Groups including attackers =', group_matrix) # Set group matrix for new round self.set_group_matrix(group_matrix) class Group(BaseGroup): attacker_active = models.IntegerField() defenders_active = models.IntegerField() attacker_id = models.IntegerField() defender1_id = models.IntegerField() defender2_id = models.IntegerField() defender3_id = models.IntegerField() defender4_id = models.IntegerField() no_attacker = models.BooleanField(initial=False) attack_paid = models.IntegerField(initial=0) num_decisions = models.IntegerField() num_likes = models.IntegerField() # Number of likes assigned in this round num_punishers = models.IntegerField(initial=0) # Reputation table: Defender's choices if challenged d1_up = models.IntegerField(initial=0) d1_down = models.IntegerField(initial=0) d1_total = models.IntegerField(initial=0) # For defenders (group treatments) d1_up7 = models.IntegerField(initial=0) d1_down7 = models.IntegerField(initial=0) d1_total7 = models.IntegerField(initial=0) # For attackers (individual & group treatments) d2_up = models.IntegerField(initial=0) d2_down = models.IntegerField(initial=0) d2_total = models.IntegerField(initial=0) d2_up7 = models.IntegerField(initial=0) d2_down7 = models.IntegerField(initial=0) d2_total7 = models.IntegerField(initial=0) d3_up = models.IntegerField(initial=0) d3_down = models.IntegerField(initial=0) d3_total = models.IntegerField(initial=0) d3_up7 = models.IntegerField(initial=0) d3_down7 = models.IntegerField(initial=0) d3_total7 = models.IntegerField(initial=0) d4_up = models.IntegerField(initial=0) d4_down = models.IntegerField(initial=0) d4_total = models.IntegerField(initial=0) d4_up7 = models.IntegerField(initial=0) d4_down7 = models.IntegerField(initial=0) d4_total7 = models.IntegerField(initial=0) # Group-level treatments dsel_id = models.IntegerField() dsel_choice = models.IntegerField() dsel_round_payoff = models.FloatField() dsel_up7 = models.IntegerField(initial=0) dsel_down7 = models.IntegerField(initial=0) dsel_total7 = models.IntegerField(initial=0) def assign_roles(self): print('ASSIGN ROLES IN ROUND ', self.round_number + 1) player_roles = {} sorted_players = sorted( self.get_players(), key=lambda player: player.participant.vars['score'] ) id = 0 for p in sorted_players: if p.participant.vars['role'] == 'defender' and not self.session.config['group']: id += 1 p.defender_id = id print('Player ', p.id_in_subsession, '(', p.id_in_group, ') = defender ', p.defender_id, 'in round ', self.round_number) else: # If group treatments (or attacker = 0): copy defender id from round 1 p.defender_id = p.participant.vars['defender_id'] # For all treatments and all rounds: assign roles p.role() player_roles[p] = p.role() print('Player roles in round ', self.round_number, ' are ', player_roles) # Number of active players self.defenders_active = len([p for p in self.get_players() if p.participant.vars['role'] == 'defender' and p.score < 1]) self.attacker_active = len([p for p in self.get_players() if p.participant.vars['role'] == 'attacker' and p.score < 1]) # if not self.no_attacker and self.round_number <= self.subsession.last_round: if self.round_number <= self.subsession.last_round: self.attacker_id = self.get_player_by_role('attacker').id_in_subsession self.defender1_id = self.get_player_by_role('defender1').id_in_subsession self.defender2_id = self.get_player_by_role('defender2').id_in_subsession self.defender3_id = self.get_player_by_role('defender3').id_in_subsession self.defender4_id = self.get_player_by_role('defender4').id_in_subsession def set_payoffs(self): attacker = self.get_player_by_role('attacker') if not self.session.config['group']: defenders = [ {'defender': self.get_player_by_role('defender1'), 'choice_a': attacker.choice_a1, 'id': 1}, {'defender': self.get_player_by_role('defender2'), 'choice_a': attacker.choice_a2, 'id': 2}, {'defender': self.get_player_by_role('defender3'), 'choice_a': attacker.choice_a3, 'id': 3}, {'defender': self.get_player_by_role('defender4'), 'choice_a': attacker.choice_a4, 'id': 4} ] else: defenders = [ {'defender': self.get_player_by_role('defender1'), 'choice_a': attacker.choice_a1, 'id': 1}, {'defender': self.get_player_by_role('defender2'), 'choice_a': attacker.choice_a1, 'id': 2}, {'defender': self.get_player_by_role('defender3'), 'choice_a': attacker.choice_a1, 'id': 3}, {'defender': self.get_player_by_role('defender4'), 'choice_a': attacker.choice_a1, 'id': 4} ] print(defenders) if self.session.config['group']: # Store attacker choice as defender variable for d in defenders: d['defender'].choice_a1 = d['choice_a'] # attacker.choice_a1 # For group treatments: select defender to respond # Prepare dictionary: how often defender has been selected dict_num_selected = {} for d in defenders: if self.round_number == 1: # Get num selected from previous round/app d['defender'].num_selected = d['defender'].participant.vars['num_selected'] else: d['defender'].num_selected = d['defender'].in_round(self.round_number - 1).num_selected # Add to dictionary only if defender made a valid choice (< 2) if not d['defender'].choice_d == 2: dict_num_selected[d['id']] = d['defender'].num_selected print('$$$$$$ Defenders have been selected to respond X times:', dict_num_selected) valid_decisions = [] if len(dict_num_selected) > 0: minval = min(dict_num_selected.values()) valid_decisions = [k for k, v in dict_num_selected.items() if v == minval] # else: no defender made valid choice if self.round_number > 4 and len(list(dict_num_selected)) >= 2: # Add two random ids to avoid going in perfect order valid_decisions += sample(list(dict_num_selected), 2) print('$$$$$$ Defenders left to choose from with minimum number selected:', valid_decisions) if attacker.choice_a1 == 1: if valid_decisions: # For group treatments: attack_paid signals selected player from valid decisions list # For individual treatments: attack_paid selects one of attacker decisions for payment self.attack_paid = choice(valid_decisions) print('$$$$$$$ The selected defender is', self.attack_paid) else: # If none of the defenders made a valid choice in time self.attack_paid = 99 for d in defenders: # If defender is selected defender: if self.attack_paid == d['id']: d['defender'].was_challenged = True d['defender'].participant.vars['choice_if_challenged'].append(d['defender'].choice_d) print('Choices if challenged', d['defender'].participant.vars['choice_if_challenged']) # if len(d['defender'].participant.vars['choice_if_challenged']) > 7: # ndel = len(d['defender'].participant.vars['choice_if_challenged']) - 7 # print('$&%%*$&#(%&%&*$*#$(# More than 7 decisions when challenged, delete first', ndel, 'entries') # d['defender'].participant.vars['choice_if_challenged_7'] = d['defender'].participant.vars['choice_if_challenged'][ndel:] # print('@#%&@#($&@#(%&@#)$%(@)#$ Restricted choice_if_challenged_7 is now', d['defender'].participant.vars['choice_if_challenged_7']) # else: # d['defender'].participant.vars['choice_if_challenged_7'] = d['defender'].participant.vars['choice_if_challenged'] # print('@#%&@#($&@#(%&@#)$%(@)#$ Length <= 7 is ', d['defender'].participant.vars['choice_if_challenged_7'] == d['defender'].participant.vars['choice_if_challenged']) if d['defender'].choice_d == 1: d['defender'].fight = True else: d['defender'].fight = False if self.round_number == 1: d['defender'].num_selected += 1 else: d['defender'].num_selected = d['defender'].in_round(self.round_number - 1).num_selected + 1 print('$$$$$$$ Increase in num_selected, now', d['defender'].num_selected) # Remaining defenders elif d['choice_a'] == 1 and self.attack_paid != d['id']: # attacker.choice_a1 == 1 and self.attack_paid != d['id']: d['defender'].was_challenged = False d['defender'].fight = False # Get decision of selected defender if self.attack_paid == 1: self.dsel_choice = self.get_player_by_role('defender1').choice_d self.dsel_id = self.get_player_by_role('defender1').id_in_subsession self.get_player_by_role('defender1').selected_defender = True elif self.attack_paid == 2: self.dsel_choice = self.get_player_by_role('defender2').choice_d self.dsel_id = self.get_player_by_role('defender2').id_in_subsession self.get_player_by_role('defender2').selected_defender = True elif self.attack_paid == 3: self.dsel_choice = self.get_player_by_role('defender3').choice_d self.dsel_id = self.get_player_by_role('defender3').id_in_subsession self.get_player_by_role('defender3').selected_defender = True elif self.attack_paid == 4: self.dsel_choice = self.get_player_by_role('defender4').choice_d self.dsel_id = self.get_player_by_role('defender4').id_in_subsession self.get_player_by_role('defender4').selected_defender = True elif self.attack_paid == 99: # No choice made by any of the defenders: self.dsel_choice = 2 else: # Attacker did not attack: for d in defenders: d['defender'].was_challenged = False d['defender'].fight = False self.dsel_choice = 2 print('SELECTED CHOICE CHECK', self.dsel_choice) if self.dsel_choice < 2: attacker.participant.vars['dsel_last7'].append(self.dsel_choice) print('All selected defender choices are:', attacker.participant.vars['dsel_last7']) # SET PAYOFFS if attacker.choice_a1 == 1: # Payoffs if attacker attacks and defender defends: if self.dsel_choice == 1: # self.d1_down += 1 self.dsel_round_payoff = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 attacker.round_payoff = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 # Payoffs if attacker attacks and defender does nothing: elif self.dsel_choice == 0: # self.d1_up += 1 self.dsel_round_payoff = self.subsession.endowment attacker.round_payoff = self.subsession.endowment + self.subsession.value # Payoffs if none of the defenders made a choice elif self.dsel_choice == 2: self.dsel_round_payoff = 0 attacker.round_payoff = 0 # Payoffs if attacker doesn't attack elif attacker.choice_a1 == 0: attacker.round_payoff = self.subsession.endowment self.dsel_round_payoff = self.subsession.endowment + self.subsession.value # Payoff if attacker doesn't make choice elif attacker.choice_a1 == 2: self.dsel_round_payoff = 0 attacker.round_payoff = 0 for d in defenders: # Store the dictionary of focal player choices for each defender # d['defender'].participant.vars['choice_if_challenged'].append(self.dsel_choice) d['defender'].round_payoff = self.dsel_round_payoff # !!!!!!!!! TBD: If payoff for fight should vary depending on whether defender is selected or not else: # Individual level treatments # Payoff for defenders for d in defenders: # Store attacker choice under a1 for all defenders d['defender'].choice_a1 = d['choice_a'] if d['defender'].choice_d == 2: # no decision was made so no payoff this round d['defender'].round_payoff = 0 d['defender'].not_valid = True else: # If A attacked if d['choice_a'] == 1: d['defender'].was_challenged = True d['defender'].participant.vars['choice_if_challenged'].append(d['defender'].choice_d) print('Choices if challenged', d['defender'].participant.vars['choice_if_challenged']) if d['defender'].choice_d == 1: # d['defender'].num_down += 1 d['defender'].round_payoff = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 d['defender'].fight = True elif d['defender'].choice_d == 0: # d['defender'].num_up += 1 d['defender'].round_payoff = self.subsession.endowment d['defender'].fight = False # If A didn't do anything elif d['choice_a'] == 0: d['defender'].was_challenged = False d['defender'].fight = False d['defender'].round_payoff = self.subsession.endowment + self.subsession.value # If A didn't make a choice if d['choice_a'] == 2: d['defender'].was_challenged = False d['defender'].timeout_other = True d['defender'].not_valid = True d['defender'].round_payoff = 0 # First store attacker's round payoff against each defender decision for results table for d in defenders: if d['id'] == 1: if d['choice_a'] == 1: if d['defender'].choice_d == 1: attacker.rpayoff1 = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 elif d['defender'].choice_d == 0: attacker.rpayoff1 = self.subsession.endowment + self.subsession.value elif d['choice_a'] == 0: attacker.rpayoff1 = self.subsession.endowment elif d['choice_a'] == 2: attacker.rpayoff1 = 0 attacker.not_valid = True elif d['id'] == 2: if d['choice_a'] == 1: if d['defender'].choice_d == 1: attacker.rpayoff2 = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 elif d['defender'].choice_d == 0: attacker.rpayoff2 = self.subsession.endowment + self.subsession.value elif d['choice_a'] == 0: attacker.rpayoff2 = self.subsession.endowment elif d['choice_a'] == 2: attacker.rpayoff2 = 0 attacker.not_valid = True elif d['id'] == 3: if d['choice_a'] == 1: if d['defender'].choice_d == 1: attacker.rpayoff3 = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 elif d['defender'].choice_d == 0: attacker.rpayoff3 = self.subsession.endowment + self.subsession.value elif d['choice_a'] == 0: attacker.rpayoff3 = self.subsession.endowment elif d['choice_a'] == 2: attacker.rpayoff3 = 0 attacker.not_valid = True elif d['id'] == 4: if d['choice_a'] == 1: if d['defender'].choice_d == 1: attacker.rpayoff4 = self.subsession.endowment + (self.subsession.value - self.subsession.cost) / 2 elif d['defender'].choice_d == 0: attacker.rpayoff4 = self.subsession.endowment + self.subsession.value elif d['choice_a'] == 0: attacker.rpayoff4 = self.subsession.endowment elif d['choice_a'] == 2: attacker.rpayoff4 = 0 attacker.not_valid = True # Calculate round payoff for attacker > randomly draw one decision against valid_decisions = [] for d in defenders: # Draw random payoff only from decisions against defenders where both A and B made their decision in time if not d['defender'].choice_d == 2 or d['choice_a'] == 2: valid_decisions.append(d['id']) print('List of valid decisions for payoff attacker = ', valid_decisions) self.num_decisions = len(valid_decisions) if valid_decisions: self.attack_paid = choice(valid_decisions) if self.attack_paid == 1: attacker.round_payoff = attacker.rpayoff1 elif self.attack_paid == 2: attacker.round_payoff = attacker.rpayoff2 elif self.attack_paid == 3: attacker.round_payoff = attacker.rpayoff3 elif self.attack_paid == 4: attacker.round_payoff = attacker.rpayoff4 else: # If neither defender made a decision in time: no payoff this round attacker.not_valid = True attacker.round_payoff = 0 for p in self.get_players(): print('$$$$$$$$ ROUND PAYOFF = ', p.round_payoff) def defender_choices(self): defender_ids = [] # for p in self.get_players(): # if p.participant.vars['role'] == 'defender' and not p.inactive: # p.participant.vars['participates']: # defender_ids.append(p.defender_id) # if 1 in defender_ids: defender1 = self.get_player_by_role('defender1').participant.vars['choice_if_challenged'] num_up_down = {i: defender1.count(i) for i in defender1} self.d1_up = num_up_down.get(0, 0) self.d1_down = num_up_down.get(1, 0) self.d1_total = self.d1_down + self.d1_up print('Defender 1 played {} up and {} down'.format(self.d1_up, self.d1_down)) # else: # # Defender dropped out # self.defender1_id = None # if 2 in defender_ids: defender2 = self.get_player_by_role('defender2').participant.vars['choice_if_challenged'] num_up_down = {i: defender2.count(i) for i in defender2} self.d2_up = num_up_down.get(0, 0) self.d2_down = num_up_down.get(1, 0) self.d2_total = self.d2_down + self.d2_up # else: # self.defender2_id = None # if 3 in defender_ids: defender3 = self.get_player_by_role('defender3').participant.vars['choice_if_challenged'] num_up_down = {i: defender3.count(i) for i in defender3} self.d3_up = num_up_down.get(0, 0) self.d3_down = num_up_down.get(1, 0) self.d3_total = self.d3_down + self.d3_up # else: # self.defender3_id = None # if 4 in defender_ids: defender4 = self.get_player_by_role('defender4').participant.vars['choice_if_challenged'] num_up_down = {i: defender4.count(i) for i in defender4} self.d4_up = num_up_down.get(0, 0) self.d4_down = num_up_down.get(1, 0) self.d4_total = self.d4_down + self.d4_up # else: # self.defender4_id = None # Reputation table for attackers if self.session.config['group']: defchoices7 = self.get_player_by_role('attacker').participant.vars['dsel_last7'] defchoices7 = defchoices7[-7:] num_up_down = {i: defchoices7.count(i) for i in defchoices7} self.dsel_up7 = num_up_down.get(0, 0) self.dsel_down7 = num_up_down.get(1, 0) self.dsel_total7 = self.dsel_up7 + self.dsel_down7 print('In the last 7 rounds the selected defenders played {} up and {} down'.format(self.dsel_up7, self.dsel_down7)) else: defchoices1 = self.get_player_by_role('defender1').participant.vars['choice_if_challenged'] defchoices1 = defchoices1[-7:] num_up_down = {i: defchoices1.count(i) for i in defchoices1} self.d1_up7 = num_up_down.get(0, 0) self.d1_down7 = num_up_down.get(1, 0) self.d1_total7 = self.d1_down7 + self.d1_up7 print('In the last 7 rounds defender 1 played {} up and {} down'.format(self.d1_up7, self.d1_down7)) defchoices2 = self.get_player_by_role('defender2').participant.vars['choice_if_challenged'] defchoices2 = defchoices2[-7:] num_up_down = {i: defchoices2.count(i) for i in defchoices2} self.d2_up7 = num_up_down.get(0, 0) self.d2_down7 = num_up_down.get(1, 0) self.d2_total7 = self.d2_down7 + self.d2_up7 print('In the last 7 rounds defender 2 played {} up and {} down'.format(self.d2_up7, self.d2_down7)) defchoices3 = self.get_player_by_role('defender1').participant.vars['choice_if_challenged'] defchoices3 = defchoices3[-7:] num_up_down = {i: defchoices3.count(i) for i in defchoices3} self.d3_up7 = num_up_down.get(0, 0) self.d3_down7 = num_up_down.get(1, 0) self.d3_total7 = self.d3_down7 + self.d3_up7 print('In the last 7 rounds defender 3 played {} up and {} down'.format(self.d3_up7, self.d3_down7)) defchoices4 = self.get_player_by_role('defender4').participant.vars['choice_if_challenged'] defchoices4 = defchoices4[-7:] num_up_down = {i: defchoices4.count(i) for i in defchoices4} self.d4_up7 = num_up_down.get(0, 0) self.d4_down7 = num_up_down.get(1, 0) self.d4_total7 = self.d4_down7 + self.d4_up7 print('In the last 7 rounds defender 4 played {} up and {} down'.format(self.d4_up7, self.d4_down7)) # if self.session.config['group']: # self.dsel_up = self.d1_up + self.d2_up + self.d3_up + self.d4_up # self.dsel_down = self.d1_down + self.d2_down + self.d3_down + self.d4_down # self.dsel_total = self.dsel_up + self.dsel_down def rewards(self): current_likes = 0 for p in self.get_players(): if p.participant.vars['role'] == 'defender' and p.rewpun == 1: p.reward = True if p.reward: current_likes += 1 print('Number of likes given = ', current_likes) self.num_likes = current_likes for p in self.get_players(): if p.defender_id == self.attack_paid: if self.round_number == 1: p.num_likes = p.participant.vars['num_likes'] + current_likes # current_likes else: p.num_likes = p.in_round(self.round_number - 1).num_likes + current_likes print('Number of likes for selected player = ', p.num_likes) else: if self.round_number == 1: p.num_likes = p.participant.vars['num_likes'] # 0 else: p.num_likes = p.in_round(self.round_number - 1).num_likes def punishments(self): num_punishments = 0 for p in self.get_players(): if p.participant.vars['role'] == 'defender' and p.rewpun == 3: p.punish = True if p.punish: print('THIS PLAYER PUNISHED') num_punishments += 1 #p.round_payoff -= Constants.punish_s print('Number of punishments given = ', num_punishments) for p in self.get_players(): if p.defender_id == self.attack_paid: self.num_punishers = num_punishments p.round_payoff -= Constants.punish_r * self.num_punishers self.dsel_round_payoff -= Constants.punish_r * self.num_punishers print('Number of punishments for selected player = ', self.num_punishers) def final_payoff(self): print('Last round = ', self.subsession.last_round) for p in self.get_players(): pvars = p.participant.vars p.participant.vars['defender_id'] = p.defender_id # if Constants.inc_risksvo: # svo = float("{:.2f}".format(pvars.get('payoff_svo') / 100)) # risk = float("{:.2f}".format(pvars.get('risk_lottery_payoff') / 100)) # else: # svo = 0.5 # risk = 0.4 # print('Payoff from risk = {} and from svo = {}'.format(risk,svo)) if pvars.get('num_timeouts') < Constants.allowed_timeouts and not pvars.get('inactive'): # Payoff for 4 randomly drawn rounds round_sets = list(range(1, Constants.num_rounds + 1)) #p.role() == 'attacker' and self.session.config['group']: rounds_to_remove = [] punishment_rounds = [] for round in round_sets: print('Player {} in round {}'.format(p, round)) if p.in_round(round).not_valid: #p.in_round(round).choice_a1 == 2: print('PLAYER DIDN\'T MAKE CHOICE in round', round) #round_sets.remove(round) #print('new round set', round_sets) rounds_to_remove.append(round) print(rounds_to_remove) elif p.in_round(round).choice_a1 == 1 and not p.in_round(round).selected_defender and pvars.get('role') == 'defender': print('PLAYER WASN\'T SELECTED in round', round) rounds_to_remove.append(round) print(rounds_to_remove) if self.session.config['punishment']: punishment_rounds.append(round) # else: # print('In round {} Player made choice {} and earned {}'.format(round,p.in_round(round).choice_a1,p.in_round(round).payoff)) for r in rounds_to_remove: round_sets.remove(r) print('new round set', round_sets) round_sets = [x.tolist() for x in array_split(round_sets, Constants.rounds_paid)] # CHANGE: ONLY 2 ROUNDS PAID! print('Round sets are', round_sets) if round_sets[0]: p.sel_round1 = choice(round_sets[0]) print('Selected round1 =', p.sel_round1) print('Payoff in that round = ', p.in_round(p.sel_round1).round_payoff) p.payoff1 = float(p.in_round(p.sel_round1).round_payoff) else: p.sel_round1 = 99 p.payoff1 = 0 if round_sets[1]: p.sel_round2 = choice(round_sets[1]) print('Selected round2 =', p.sel_round2) print('Payoff in that round = ', p.in_round(p.sel_round2).round_payoff) p.payoff2 = float(p.in_round(p.sel_round2).round_payoff) else: p.sel_round2 = 99 p.payoff2 = 0 if round_sets[2]: p.sel_round3 = choice(round_sets[2]) print('Selected round3 =', p.sel_round3) print('Payoff in that round = ', p.in_round(p.sel_round3).round_payoff) p.payoff3 = float(p.in_round(p.sel_round3).round_payoff) else: p.sel_round3 = 99 p.payoff3 = 0 if round_sets[3]: p.sel_round4 = choice(round_sets[3]) print('Selected round4 =', p.sel_round4) print('Payoff in that round = ', p.in_round(p.sel_round4).round_payoff) p.payoff4 = float(p.in_round(p.sel_round4).round_payoff) else: p.sel_round4 = 99 p.payoff4 = 0 if self.session.config['punishment']: shuffle(punishment_rounds) if len(punishment_rounds) >= 1: if p.in_round(punishment_rounds[0]).punish: p.participant.vars['pun_payoff'] += 0 else: p.participant.vars['pun_payoff'] += 1 if len(punishment_rounds) >= 2: if p.in_round(punishment_rounds[1]).punish: p.participant.vars['pun_payoff'] += 0 else: p.participant.vars['pun_payoff'] += 1 if len(punishment_rounds) >= 3: if p.in_round(punishment_rounds[2]).punish: p.participant.vars['pun_payoff'] += 0 else: p.participant.vars['pun_payoff'] += 1 if len(punishment_rounds) >= 4: if p.in_round(punishment_rounds[3]).punish: p.participant.vars['pun_payoff'] += 0 else: p.participant.vars['pun_payoff'] += 1 print('Player {} spent {} points on punishing group members'.format(p.id_in_subsession, p.participant.vars['pun_payoff'])) p.total_payoff = float("{:.6f}".format(self.subsession.showup + 0.25 * (p.payoff1 + p.payoff2 + p.payoff3 + p.payoff4 + float(p.participant.vars['pun_payoff'])))) print('For player {} the drawn payoffs are: round {}, payoff {}; ' # round {}, payoff {}; ' 'round {}, payoff {}; round {}, payoff {}, and punishment payoff is {}, so the total is {}'.format(p.id_in_subsession, p.sel_round1, p.payoff1,p.sel_round2, p.payoff2, p.sel_round3, p.payoff3, p.sel_round4, p.payoff4, p.participant.vars['pun_payoff'], p.total_payoff)) else: p.total_payoff = float("{:.6f}".format(self.subsession.showup + 0.25 * (p.payoff1 + p.payoff2 + p.payoff3 + p.payoff4))) # CHANGE: NOT INCLUDED svo + risk + print('For player {} the drawn payoffs are: round {}, payoff {}; ' # round {}, payoff {}; ' 'round {}, payoff {}; round {}, payoff {}, so the total is {}'.format(p.id_in_subsession, p.sel_round1, p.payoff1, p.sel_round2, p.payoff2, p.sel_round3, p.payoff3, p.sel_round4, p.payoff4, p.total_payoff)) else: p.sel_round1 = 0 p.sel_round2 = 0 p.sel_round3 = 0 p.sel_round4 = 0 p.payoff1 = 0 p.payoff2 = 0 p.payoff3 = 0 p.payoff4 = 0 p.total_payoff = 0 p.participant.vars['sel_round1'] = p.sel_round1 p.participant.vars['sel_round2'] = p.sel_round2 p.participant.vars['sel_round3'] = p.sel_round3 p.participant.vars['sel_round4'] = p.sel_round4 p.participant.vars['payoff1'] = p.payoff1 p.participant.vars['payoff2'] = p.payoff2 p.participant.vars['payoff3'] = p.payoff3 p.participant.vars['payoff4'] = p.payoff4 p.participant.vars['total_payoff'] = p.total_payoff print('%%%%%%%% Stored values are', p.participant.vars['sel_round1'],p.participant.vars['sel_round2'], 'for role ', p.participant.vars['role']) class Player(BasePlayer): # Score for grouping inactive = models.BooleanField() score = models.FloatField() # Attacker makes 4 choices each round: choice_a1 = models.IntegerField( choices=[ [1, 'Challenge'], [0, 'Leave'], [99, 'No choice'], ], widget=widgets.RadioSelect, label="", # initial=99, ) choice_a2 = models.IntegerField( choices=[ [1, 'Challenge'], [0, 'Leave'], ], widget=widgets.RadioSelect, label="", # initial=99, ) choice_a3 = models.IntegerField( choices=[ [1, 'Challenge'], [0, 'Leave'], ], widget=widgets.RadioSelect, label="", # initial=99, ) choice_a4 = models.IntegerField( choices=[ [1, 'Challenge'], [0, 'Leave'], ], widget=widgets.RadioSelect, label="", # initial=99, ) # Defender makes 1 choice per round choice_d = models.IntegerField( choices=[ [0, 'Up'], # 'Acquiesce'], [1, 'Down'], # 'Resist'], ], widget=widgets.RadioSelect, label="", ) experimentended = models.BooleanField(initial=False) not_valid = models.BooleanField(initial=False) # For payoff: Set to true if attacker or defender didn't make choice (= 2) # Treatment 5/6: Reward system reward = models.BooleanField(initial=False, blank=True) # Like not mandatory, so blank=True num_likes = models.IntegerField(initial=0) rewpun = models.IntegerField(initial=2, blank=True, choices=[ [1, 'reward'], [2, 'nothing'], [3, 'punish'] ]) # Treatment 6: Punishment system punish = models.BooleanField(initial=False, blank=True) # Punishment not mandatory, so blank=True # For defenders: defender_id = models.IntegerField() # id used to assign to defender role was_challenged = models.BooleanField() # stores per round whether defender was challenged fight = models.BooleanField() # stores per round if attacker and defender fight selected_defender = models.BooleanField(initial=False) num_selected = models.IntegerField(initial = 0) # Number of times up/down chosen if challenged # num_up = models.IntegerField(initial=0) # num_down = models.IntegerField(initial=0) playing_status = models.IntegerField( choices=[ [1, 'Attacker'], [2, 'Defender'], ], ) round_payoff = models.FloatField() rpayoff1 = models.FloatField(initial=0) rpayoff2 = models.FloatField(initial=0) rpayoff3 = models.FloatField(initial=0) rpayoff4 = models.FloatField(initial=0) timeout = models.IntegerField(initial=0) timeout_other = models.BooleanField(initial=False) # To indicate if participant receives average payoff started = models.BooleanField(initial=False) # Used for grouping: if assigned to group value changed to True groupnumber = models.IntegerField() num_completed = models.IntegerField(initial=0) # For attacker: number of completed decisions num_players = models.IntegerField() # Final payoff: 4 rounds drawn sel_round1 = models.IntegerField() sel_round2 = models.IntegerField() sel_round3 = models.IntegerField() sel_round4 = models.IntegerField() payoff1 = models.FloatField() payoff2 = models.FloatField() payoff3 = models.FloatField() payoff4 = models.FloatField() # norm_payoff = models.FloatField() total_payoff = models.FloatField() # store time timestamp_start = models.FloatField() # Retrieve time minutes_left_wait = models.IntegerField() # Function that stores time spent on WaitPage def time_passed(self): return time() - self.participant.vars.get('instr_timestamp', 0) def minutes_left(self): time2 = ceil(20 - (self.time_passed() / 60)) self.minutes_left_wait = time2 return time2 def role(self): # if self.round_number == 1: # Create roles: Person with lowest score value is attacker if self.defender_id == 0: # self.id_in_group % Constants.players_per_group == 1: self.participant.vars['role'] = 'attacker' self.playing_status = 1 return 'attacker' # First round: defender roles are assigned sequentially based on score in group elif self.defender_id == 1: # self.id_in_group % Constants.players_per_group == 2: self.participant.vars['role'] = 'defender' self.playing_status = 2 # self.defender_id = 2 return 'defender1' elif self.defender_id == 2: # self.id_in_group % Constants.players_per_group == 3: self.participant.vars['role'] = 'defender' self.playing_status = 2 # self.defender_id = 3 return 'defender2' elif self.defender_id == 3: # self.id_in_group % Constants.players_per_group == 4: self.participant.vars['role'] = 'defender' self.playing_status = 2 # self.defender_id = 4 return 'defender3' else: self.participant.vars['role'] = 'defender' self.playing_status = 2 # self.defender_id = 1 return 'defender4' def timeout_seconds(self): if self.round_number == 1: if self.inactive: timeout = 0 else: timeout = self.participant.vars['end_time'] - time() else: print('Num timeouts = ', self.participant.vars['num_timeouts']) if self.inactive: timeout = 0 elif self.participant.vars['num_timeouts'] >= Constants.allowed_timeouts: timeout = 0 else: timeout = self.participant.vars['end_time'] - time() return timeout def timeout_norms(self): timeout = self.participant.vars['end_time'] - time() return timeout def register_timeouts(self): if self.timeout == 0: # Increment only if not incremented before (i.e. on page on which timeout happened) self.participant.vars['num_timeouts'] += 1 self.timeout = 1 if self.participant.vars['num_timeouts'] == Constants.allowed_timeouts: print('Player', self.id_in_subsession, 'with role', self.participant.vars['role'], 'let time run out', Constants.allowed_timeouts, ' times in a row so will be removed from game') # Show timeout page print('Player who dropped out has role', self.role()) # self.participant.vars['inactive'] = True def creating_score(self): if self.inactive: self.score = 1 else: self.score = random()