from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from .models import Constants from otree_tools.utils import get_time_per_page from random import shuffle import copy import collections, random def left_shift(l): l.append(l.pop(0)) return l def making_dictators(players): total_players = len(players) shuffle(players) dictators_each_group = int((2/3)*total_players)/2 blue_dictator_count=red_dictator_count=dictators_each_group for player in players: if(player.participant.vars['color']=='blue' and blue_dictator_count>0): player.participant.vars['dictator'] = True blue_dictator_count -= 1 elif(player.participant.vars['color']=='red' and red_dictator_count>0): player.participant.vars['dictator'] = True red_dictator_count -= 1 else: player.participant.vars['dictator'] = False def dictator_sequences(players): dictator_sequences = [[1,3,2,4,5],[2,3,1,4,5],[3,1,4,2,5],[4,1,3,2,5],[5,1,3,2,4], [1,4,2,3,5],[2,4,1,3,5],[3,1,5,2,4],[4,1,5,2,3],[5,1,4,2,3], [1,5,2,3,4],[2,5,1,3,4],[3,2,4,1,5],[4,2,3,1,5],[5,2,3,1,4], [1,4,2,5,3],[2,4,1,5,3],[3,2,5,1,4],[4,2,5,1,3],[5,2,4,1,3]] shuffle(dictator_sequences) for player in players: if player.participant.vars['dictator']: player.participant.vars['dictator_sequence'] = dictator_sequences.pop() def restoring_state(players): player_id = 1 for player in players: player.participant.vars['player_id'] = player_id player_id += 1 if player_id%2==0: player.participant.vars['color']= 'blue' player.participant.vars['letters'] = ['A','I'] elif player_id%2==1: player.participant.vars['color']= 'red' player.participant.vars['letters'] = ['I','I'] player.participant.vars['reportedletters'] = ['I', 'A', 'I'] making_dictators(players) dictator_sequences(players) def print_player_vars(players): for player in players: print(player.participant.vars) def clean_up(player): player.self_offer = None player.ingroup_offer = None player.outgroup_offer = None class DictatorSelection(WaitPage): def after_all_players_arrive(self): players = self.group.get_players() # restoring_state(players) #Randomly sets up players to cut testing time. # blue_receiver_ids = [] # red_receiver_ids = [] # for player in players: # player.participant.vars['payoff_array'] = [] # if not player.participant.vars["dictator"]: # if player.participant.vars["color"] == 'blue': # blue_receiver_ids.append(player.participant.vars['player_id']) # else: # red_receiver_ids.append(player.participant.vars['player_id']) # else: # player.participant.vars['blue_receivers_list'] = [] # player.participant.vars['red_receivers_list'] = [] # player.participant.vars['active_treatment'] = -1 # for i in range(5): # for count,player in enumerate(players): # if player.participant.vars['dictator']: # player.participant.vars['blue_receivers_list'].append(blue_receiver_ids[count%(len(blue_receiver_ids))]) # player.participant.vars['red_receivers_list'].append(red_receiver_ids[count%(len(blue_receiver_ids))]) # left_shift(blue_receiver_ids) # left_shift(red_receiver_ids) # After this section we will keep this code # we use this to set up for each treatment - at the end of this section of code each player has the vars active_treatment and active_blue_receiver and active_red_receiver set for player in players: if player.participant.vars['dictator'] and player.participant.vars['active_treatment'] == -1: player.participant.vars['active_treatment'] = player.participant.vars['dictator_sequence'][player.round_number-1] player.participant.vars['active_red_receiver'] = player.participant.vars['red_receivers_list'][player.round_number-1] player.participant.vars['active_blue_receiver'] = player.participant.vars['blue_receivers_list'][player.round_number-1] else: player.participant.vars['active_treatment'] = random.randint(1,5) # for testing a specific active treatment | comment out when done # active_treatment = 2 # for player in players: # if player.participant.vars['dictator'] and player.participant.vars['active_treatment'] == -1: # player.participant.vars['active_treatment'] = active_treatment # player.participant.vars['active_red_receiver'] = player.participant.vars['red_receivers_list'][player.round_number-1] # player.participant.vars['active_blue_receiver'] = player.participant.vars['blue_receivers_list'][player.round_number-1] # else: # player.participant.vars['active_treatment'] = active_treatment for player in players: player.active_treatment = player.participant.vars['active_treatment'] if player.participant.vars['active_treatment'] == 5: values = [1,2,3] random.shuffle(values) player.participant.vars['decision_order'] = copy.deepcopy(values) player.participant.vars['decision_order'].append(0) player.participant.vars['active_decision'] = 0 #print_player_vars(self.subsession.get_players()) # post this, in every treatment page, we can use active_treatment/active_red_receiver and active_blue_receiver to be able to access the relevant peeps class InstructionPage(Page): def is_displayed(self): return self.player.participant.vars['dictator']==True def before_next_page(self): self.player.time_spent = str(get_time_per_page(self.player, 'InstructionPage')) # return self.player.participant.vars['dictator']==True class ReceiverInstructionPage(Page): def is_displayed(self): return self.player.participant.vars['dictator']==False class ResultsWaitPage(WaitPage): def after_all_players_arrive(self): pass class Treatment1Page(Page): def is_displayed(self): return self.player.participant.vars['active_treatment'] == 1 form_model = 'player' def get_form_fields(self): all_formfields = ['self_offer','ingroup_offer','outgroup_offer'] shuffle(all_formfields) return all_formfields def error_message(self, values): if values["self_offer"] + values["ingroup_offer"] + values["outgroup_offer"] != 100: return 'The offers must add up to 100' def before_next_page(self): if not self.player.participant.vars['dictator']: pass #clean_up(self.player) self.player.time_spent = str(get_time_per_page(self.player, 'Treatment1Page')) class Treatment2Page(Page): # so this is a tricky one, since we're actually working with probability. A good way to do this seems to be to use the before_next_page to actually roll the dice and thus only store the correct offers from that point on. def is_displayed(self): # this is also going to be tricky to test; maybe bias the dice roll during testing? return self.player.participant.vars['active_treatment'] == 2 form_model = 'player' def get_form_fields(self): all_formfields = ['self_offer','ingroup_offer','outgroup_offer'] shuffle(all_formfields) return all_formfields def error_message(self, values): if values["self_offer"] + values["ingroup_offer"] + values["outgroup_offer"] != 100: return 'The offers must add up to 100' def before_next_page(self): if not self.player.participant.vars['dictator']: pass #clean_up(self.player) commenting out these because we still want data! else: k = random.randint(0,100) # ah another tricky one. basically we split the 0=>100 section into the offer values, so we have to sigma self_threshold = self.player.self_offer # sorry if the previous comment was bad, but just trust that this works ingroup_threshold = self.player.ingroup_offer + self_threshold outgroup_threshold = self.player.outgroup_offer + ingroup_threshold if k<=self_threshold: self.player.self_offer = 100 self.player.ingroup_offer = 0 self.player.outgroup_offer = 0 elif k<= ingroup_threshold: self.player.self_offer = 0 self.player.ingroup_offer = 100 self.player.outgroup_offer = 0 else: self.player.self_offer = 0 self.player.ingroup_offer = 0 self.player.outgroup_offer = 100 self.player.time_spent = str(get_time_per_page(self.player, 'Treatment2Page')) class Treatment3Page(Page): def is_displayed(self): return self.player.participant.vars['active_treatment'] == 3 form_model = 'player' def get_form_fields(self): all_formfields = ['ingroup_offer', 'outgroup_offer'] shuffle(all_formfields) return all_formfields def error_message(self, values): if values['ingroup_offer'] + values['outgroup_offer'] != 100: return "The offers must add up to 100" def before_next_page(self): if not self.player.participant.vars['dictator']: pass # clean_up(self.player) else: # average self.player.self_offer = (self.player.ingroup_offer + self.player.outgroup_offer)/2 self.player.time_spent = str(get_time_per_page(self.player, 'Treatment3Page')) class Treatment4Page(Page): def is_displayed(self): return self.player.participant.vars['active_treatment'] == 4 form_model = 'player' def get_form_fields(self): all_formfields = ['ingroup_offer', 'outgroup_offer'] shuffle(all_formfields) return all_formfields def error_message(self, values): if values['ingroup_offer'] + values['outgroup_offer'] != 100: return "The offers must add up to 100" def before_next_page(self): if not self.player.participant.vars['dictator']: pass # clean_up(self.player) else: # roll a dice to decide which offer you get offer_dice_roll = random.randint(0,1) if offer_dice_roll == 0: self.player.self_offer = self.player.ingroup_offer else: self.player.self_offer = self.player.outgroup_offer self.player.time_spent = str(get_time_per_page(self.player, 'Treatment4Page')) class Treatment5Page1(Page): def is_displayed(self): if self.player.participant.vars['active_treatment'] == 5 and self.player.participant.vars['decision_order'][self.player.participant.vars['active_decision']] == 1: #print_player_vars(self.subsession.get_players()) return True return False def get_form_fields(self): all_formfields = ['ingroup_offer_treatment5_offer1', 'outgroup_offer_treatment5_offer1'] shuffle(all_formfields) return all_formfields form_model = 'player' def error_message(self, values): if values['ingroup_offer_treatment5_offer1'] + values['outgroup_offer_treatment5_offer1'] != 22: return "The offers must add up to 22" def before_next_page(self): self.player.participant.vars['active_decision']+=1 #self.player.time_spent += str(get_time_per_page(self.player, 'Treatment5Page1')) self.player.time_spent_treatment5_D1 = str(get_time_per_page(self.player, 'Treatment5Page1')) def vars_for_template(self): return {"DecisionID" : self.player.participant.vars['active_decision'] + 1} class Treatment5Page2(Page): def is_displayed(self): if self.player.participant.vars['active_treatment'] == 5 and self.player.participant.vars['decision_order'][self.player.participant.vars['active_decision']] == 2: return True return False def get_form_fields(self): all_formfields = ['ingroup_offer_treatment5_offer2', 'outgroup_offer_treatment5_offer2'] shuffle(all_formfields) return all_formfields form_model = 'player' def error_message(self, values): if values['ingroup_offer_treatment5_offer2'] + values['outgroup_offer_treatment5_offer2'] != 35: return "The offers must add up to 35" def before_next_page(self): self.player.participant.vars['active_decision']+=1 #self.player.time_spent += str(get_time_per_page(self.player, 'Treatment5Page2')) self.player.time_spent_treatment5_D2 = str(get_time_per_page(self.player, 'Treatment5Page2')) def vars_for_template(self): return {"DecisionID" : self.player.participant.vars['active_decision'] + 1} class Treatment5Page3(Page): def is_displayed(self): if self.player.participant.vars['active_treatment'] == 5 and self.player.participant.vars['decision_order'][self.player.participant.vars['active_decision']] == 3: return True return False def get_form_fields(self): all_formfields = ['ingroup_offer_treatment5_offer3', 'outgroup_offer_treatment5_offer3'] shuffle(all_formfields) return all_formfields form_model = 'player' def error_message(self, values): if values['ingroup_offer_treatment5_offer3'] + values['outgroup_offer_treatment5_offer3'] != 43: return "The offers must add up to 43" def before_next_page(self): self.player.participant.vars['active_decision']+=1 # self.player.time_spent += str(get_time_per_page(self.player, 'Treatment5Page2')) self.player.time_spent_treatment5_D3 = str(get_time_per_page(self.player, 'Treatment5Page3')) def vars_for_template(self): return {"DecisionID" : self.player.participant.vars['active_decision'] + 1} class PostTreatmentPage(WaitPage): # appending to payoff matrix def after_all_players_arrive(self): all_players = self.subsession.get_players() for some_player in all_players: if some_player.participant.vars['active_treatment'] == 5: some_player.ingroup_offer = some_player.ingroup_offer_treatment5_offer1 + some_player.ingroup_offer_treatment5_offer2 + some_player.ingroup_offer_treatment5_offer3 some_player.outgroup_offer = some_player.outgroup_offer_treatment5_offer1 + some_player.outgroup_offer_treatment5_offer2 + some_player.outgroup_offer_treatment5_offer3 some_player.self_offer = (some_player.ingroup_offer + some_player.outgroup_offer)/2 for some_player in all_players: #painful variable choice but w/e if some_player.participant.vars['active_treatment']>=1 and some_player.participant.vars['active_treatment']<=5 : # we can combine them because of post processing done in the before_next_page step if some_player.participant.vars['dictator']: payoff_dictator = some_player.self_offer some_player.participant.vars['payoff_array'].append(payoff_dictator) else: # repeated section of code - can we try and refactor, Venky? player_id = some_player.participant.vars["player_id"] player_payoff = 0 for player in self.subsession.get_players(): if player.participant.vars['dictator']: # First we check active receivers to check if the player was involved, and if they were we check color to see if we have to count their ingroup or outgroup offer if (player.participant.vars["active_blue_receiver"] == player_id or player.participant.vars["active_red_receiver"] == player_id) and some_player.participant.vars["color"] == player.participant.vars["color"]: some_player.participant.vars['payoff_array'].append(player.ingroup_offer) elif (player.participant.vars["active_blue_receiver"] == player_id or player.participant.vars["active_red_receiver"] == player_id) and some_player.participant.vars["color"] != player.participant.vars["color"]: some_player.participant.vars['payoff_array'].append(player.outgroup_offer) print_player_vars(self.subsession.get_players()) # Calculating final payoff for this app for some_player in all_players: some_player.participant.vars['the_dictator_payoff'] = random.choice(some_player.participant.vars['payoff_array']) some_player.payoff = some_player.participant.vars['the_dictator_payoff'] # class Results(Page): # pass page_sequence = [ InstructionPage, ReceiverInstructionPage, DictatorSelection, Treatment1Page, Treatment2Page, Treatment3Page, Treatment4Page, Treatment5Page1, Treatment5Page2, Treatment5Page3, Treatment5Page1, Treatment5Page2, Treatment5Page3, Treatment5Page1, Treatment5Page2, Treatment5Page3, PostTreatmentPage, ]