from otree.api import * from otree.export import get_fields_for_csv doc = """ Players come in groups and vote on firm policy. depending on firm policy, players are assigned as winners and losers. Then, players are grouped with another and go to the dictator game or kicked out. """ class C(BaseConstants): NAME_IN_URL = 'Vote_Policy' PLAYERS_PER_GROUP = 5 NUM_ROUNDS = 1 # Initial amount allocated to the dictator ENDOWMENT = cu(100) participation_fee = cu(200) # might need to make some of the constants go to the subsession depending on the code integration class Subsession(BaseSubsession): def group_by_arrival_time_method(self, waiting_players): if len(waiting_players) >=5: return waiting_players[:5] def creating_session(subsession: Subsession): session = subsession.session session.vars['num_winner_winner_pairs'] = 0 session.vars['num_winner_loser_pairs'] = 0 session.vars['num_loser_loser_pairs'] = 0 class Group(BaseGroup): def set_pairings(self): players = self.get_players() # count the number of votes for each policy office_votes = sum([1 for p in players if p.policy_vote == 'Office']) remote_votes = sum([1 for p in players if p.policy_vote == 'Remote']) # determine the winning policy if office_votes > remote_votes: winning_policy = 'Office' else: winning_policy = 'Remote' # assign winner or loser variable to each player for player in players: player.participant.vars['policy_vote'] = player.policy_vote player.participant.vars['office_votes'] = office_votes player.participant.vars['remote_votes'] = remote_votes player.participant.vars['winning_policy'] = winning_policy if player.policy_vote == winning_policy: player.participant.vars['policy_vote_result'] = 'winner' else: player.participant.vars['policy_vote_result'] = 'loser' #get the number of winners and losers num_winners = sum([1 for p in players if p.participant.vars['policy_vote_result'] == 'winner']) num_losers = sum([1 for p in players if p.participant.vars['policy_vote_result'] == 'loser']) print("Number of Losers") print(num_losers) # Algorithm for assigning pairings if num_losers == 0: # kick all participants out for player in players: player.is_out = True player.participant.vars['earnings'] = C.participation_fee elif num_losers == 1: # create one winner-loser pairing loser = next(p for p in players if p.participant.vars['policy_vote_result'] == 'loser') winner = next(p for p in players if p.participant.vars['policy_vote_result'] == 'winner') loser.participant.vars['pairing'] = 'winner-loser' loser.participant.vars['pair_number'] = 1 winner.participant.vars['pairing'] = 'winner-loser' winner.participant.vars['pair_number'] = 1 self.session.vars['num_winner_loser_pairs'] += 2 print("WL Pairs 1 Loser") print(self.session.vars['num_winner_loser_pairs']) # send players that were not paired out for player in players: if 'pairing' not in player.participant.vars: player.is_out = True player.participant.vars['pair_number'] = player.id_in_group * 11 player.participant.vars['earnings'] = C.participation_fee elif num_losers == 2: #If LL + 4 <=WL, if it is pretty close then do ll and ww if self.session.vars['num_loser_loser_pairs'] < self.session.vars['num_winner_loser_pairs'] + 4: # create one loser-loser pairing and one winner-winner pairing losers = [p for p in players if p.participant.vars['policy_vote_result'] == 'loser'] winners = [p for p in players if p.participant.vars['policy_vote_result'] == 'winner'] losers[0].participant.vars['pairing'] = 'loser-loser' losers[0].participant.vars['pair_number'] = 1 losers[1].participant.vars['pairing'] = 'loser-loser' losers[1].participant.vars['pair_number'] = 2 winners[0].participant.vars['pairing'] = 'winner-winner' winners[0].participant.vars['pair_number'] = 1 winners[1].participant.vars['pairing'] = 'winner-winner' winners[1].participant.vars['pair_number'] = 2 self.session.vars['num_loser_loser_pairs'] += 1 self.session.vars['num_winner_winner_pairs'] += 1 print("LL Pairs - 2 Losers") print(self.session.vars['num_loser_loser_pairs']) print("WW Pairs - 2 Losers") print(self.session.vars['num_winner_winner_pairs']) else: #create 2 WL pairings losers = [p for p in players if p.participant.vars['policy_vote_result'] == 'loser'] winners = [p for p in players if p.participant.vars['policy_vote_result'] == 'winner'] for loser in losers: loser.participant.vars['pairing'] = 'winner-loser' for winner in winners: winner.participant.vars['pairing'] = 'winner-loser' losers[0].participant.vars['pair_number'] = 1 losers[1].participant.vars['pair_number'] = 2 winners[0].participant.vars['pair_number'] = 1 winners[1].participant.vars['pair_number'] = 2 self.session.vars['num_winner_loser_pairs'] += 2 print("WL Pairs - 2 Losers") print(self.session.vars['num_winner_loser_pairs']) #send players that were not paired out for player in players: if 'pairing' not in player.participant.vars: player.is_out = True player.participant.vars['pair_number'] = player.id_in_group * 11 player.participant.vars['earnings'] = C.participation_fee class Player(BasePlayer): is_out = models.BooleanField(initial=False) giving = models.CurrencyField( doc="""Amount player decided to give to a colleague""", min=0, max=C.ENDOWMENT, label="I will provide help to my partner, incurring a personal cost of", ) policy_vote = models.StringField( choices=['Office', 'Remote'], widget=widgets.RadioSelect ) def get_other(self): for p in self.group.get_players(): if p.participant.vars['pair_number'] == self.participant.vars['pair_number'] and p.id_in_group != self.id_in_group: return p def other_giving(self): other_player = self.get_other() if other_player: other_giving = other_player.giving self.participant.vars['other_giving'] = other_giving return other_giving def calculate_payoff(self): if self.is_out: self.participant.vars['earnings'] = C.participation_fee else: self.participant.vars['earnings'] = C.participation_fee + C.ENDOWMENT + self.other_giving() - self.giving return self.participant.vars['earnings'] def get_payoff(self): self.payoff = self.participant.vars['earnings'] self.participant.vars['payoff'] = self.payoff return self.payoff def dollar_payoff(self): self.dollar_payoff = self.payoff.to_real_world_currency(self.session) self.participant.vars['dollar_payoff'] = self.dollar_payoff def custom_export(players): field_names = get_fields_for_csv(Player) #get participant variable names participant_vars = set().union(*(p.participant.vars.keys() for p in players)) #crate list of column headers headers = ['session_code', 'participant_code', 'participant_id', *field_names, *participant_vars] yield headers for p in players: participant = p.participant session = p.session #create dictionary to hold data for this player row = {fn: getattr(p, fn) for fn in field_names} #add all participant vars to the row row.update(p.participant.vars) #create list of values in teh same order as the headers values = [session.code, participant.code, participant.id] + [row.get(key) for key in headers[3:]] yield values # FUNCTIONS # function to calculate the individual player payoff #def payoff(self): # other_player = self.get_others_in_group()[0] # return C.participation_fee + C.ENDOWMENT + other_player.giving - self.giving # PAGES class PolicyVote(Page): form_model = 'player' form_fields = ['policy_vote'] def vars_for_template(self): return { 'office': 'Office', 'remote': 'Remote', 'group_size': C.PLAYERS_PER_GROUP } class PolicyVoteWaitPage(WaitPage): def after_all_players_arrive(self): self.group.set_pairings() def app_after_this_page(self, upcoming_apps): if self.is_out == 1: self.get_payoff() self.dollar_payoff() self.participant.vars['dollar_payoff'] = self.dollar_payoff.to_real_world_currency(self.session) return "payment_info" class instructions(Page): pass class Introduction(Page): pass class Offer(Page): form_model = 'player' form_fields = ['giving'] def vars_for_template(self): player = self for p in self.group.get_players(): if p.participant.vars['pair_number'] == self.participant.vars['pair_number'] and p.id_in_group != self.id_in_group: other_player = p self.participant.vars['other_player_id'] = other_player.id #other_player = next(p for p in self.group.get_players() if p.participant.vars['pair_number'] == player.participant.vars['pair_number'] and p.id_in_group != player.id_in_group) #determine the winning policy player_vote_result = 'won' if player.participant.vars['policy_vote_result'] == 'winner' else 'lost' other_vote_result = 'won' if other_player.participant.vars['policy_vote_result'] == 'winner' else 'lost' return{ 'endowment': C.ENDOWMENT, 'other_player_vote': other_player.policy_vote, 'player_vote': player.participant.vars['policy_vote'], 'vote_result': 'similar' if player.participant.vars['policy_vote'] == other_player.policy_vote else 'different', 'player_vote_result': player_vote_result, 'other_vote_result': other_vote_result, 'conversion': player.session.real_world_currency_per_point, } class ResultsWaitPage(WaitPage): def after_all_players_arrive(self): for player in self.group.get_players(): player.other_giving() player.calculate_payoff() player.get_payoff() player.dollar_payoff() player.participant.vars['dollar_payoff'] = player.dollar_payoff print("LL Pairs") print(self.session.vars['num_loser_loser_pairs']) print("WW Pairs") print(self.session.vars['num_winner_winner_pairs']) print("WL Pairs") print(self.session.vars['num_winner_loser_pairs']) class Results(Page): @staticmethod def vars_for_template(player: Player): participant = player.participant return { 'redemption code': participant.label or participant.code, 'player_earnings': player.participant.vars['earnings'], 'dollar_payoff': player.participant.vars['dollar_payoff'], 'conversion': player.session.config['real_world_currency_per_point'] } page_sequence = [PolicyVote, PolicyVoteWaitPage, Offer, ResultsWaitPage, Results]