from otree.api import * import itertools doc = """ Your app description This is the Period 1 of the fishery game where participants plays the baseline (unregulated) game. """ class C(BaseConstants): NAME_IN_URL = 'P2' PLAYERS_PER_GROUP = 3 NUM_ROUNDS = 1 MAX_PUNISHMENT = 10 # to be adjusted later P_MULTIPLIER = 2 # punishment multiplier EFFORT_MULTIPLIER = 1.1 INSTRUCTIONS_TEMPLATE = 'fishery_game_P1/instructions.html' A_LOW = 20 B_LOW = 15 A_HIGH = 50 B_HIGH = 30 PUNISHMENT_SCHEDULE = { 0: 0, 1: -2, 2: -4, 3: -6, 4: -8, 5: -10, 6: -12, 7: -14, 8: -16, 9: -18, 10: -20, } LEADER_ROLE = 'Leader' FISHER_ROLE = 'Fisher' MAJORITY = int(PLAYERS_PER_GROUP / 2) + 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): low_state = models.BooleanField(initial=True, choices=[[True, 'Low state'], [False, 'High state']]) pr_low_state = models.BooleanField(initial=True) # if the state was low in previous round pr2_low_state = models.BooleanField(initial=True) # if the state was low in the round before previous fisher_entered = models.IntegerField(initial=0) # how many fishermen entered area A fisher_not_entered = models.IntegerField(initial=0) # how many entered area B fisher_high_effort = models.IntegerField(initial=0) # how many fisher chose high effort independent of area fisher_high_A = models.IntegerField(initial=0) # how many fisher chose high effort in area A fisher_high_B = models.IntegerField(initial=0) # how many chose high effort in area B prev_fisher_entered = models.IntegerField(initial=0) # how many fishermen entered area A in previous round probability = models.FloatField(initial=0) # probabilities change = models.BooleanField(initial=False) # if the area change has happened after random probability function was called pr_change = models.BooleanField(initial=False) # if the change has happened in the previous round area_recovered = models.IntegerField(initial=0, min=0, max=2 ) pr1_area_recovered = models.IntegerField(initial=0) # area_recovered previous round area_recovered_combined = models.IntegerField(initial=0) # area recovered combined (previous and current round total_payoff = models.CurrencyField(initial=0) decision_making = models.IntegerField(initial=0) term_limit = models.IntegerField(initial=0) dem_constitution = models.BooleanField(initial=True, choices=[[True, 'Democratic'], [False, 'Representative']] ) leader_whole_period = models.BooleanField(initial=True, choices=[[True, '8 rounds'], [False, '4 rounds']]) def make_punishment_field(id_in_group): return models.IntegerField(initial=0, min=0, max=C.MAX_PUNISHMENT, label="Punishment to player {}".format(id_in_group) ) def make_smiles_field(id_in_group): return models.IntegerField(initial=0, choices=[ [1, 'Yes'], [0, 'No'], ], widget=widgets.RadioSelectHorizontal, label="Send thumbs down smiley to player {}".format(id_in_group)) class Player(BasePlayer): area_choice = models.IntegerField(initial=None, choices=[[1, 'Area A'], [0, 'Area B']], widget=widgets.RadioSelectHorizontal, label="I choose to fish in:") effort_choice = models.IntegerField(initial=None, choices=[[1, 'High'], [0, 'Normal']], widget=widgets.RadioSelectHorizontal, label="Choosing high effort earns you an adtional 10% income from fishing in" "your chosen area.") payoff_before_punishment = models.CurrencyField(initial=0) punish_p1 = make_punishment_field(1) punish_p2 = make_punishment_field(2) punish_p3 = make_punishment_field(3) punish_p4 = make_punishment_field(4) punish_p5 = make_punishment_field(5) cost_of_punishing = models.CurrencyField() punishment_received = models.CurrencyField() smiley_p1 = make_smiles_field(1) smiley_p2 = make_smiles_field(2) smiley_p3 = make_smiles_field(3) smiley_p4 = make_smiles_field(4) smiley_p5 = make_smiles_field(5) sum_smileys_down_received = models.IntegerField() sum_smileys_down_sent = models.IntegerField() unified_player_id_r2 = models.IntegerField(initial=None, min= 1, max=20, label="Please enter your Player ID written on a tablet:") decision_making_choice = models.IntegerField(initial=None, choices=[[0, 'Representative constitution'], [1, 'Democratic constitution']], widget=widgets.RadioSelectHorizontal, ) term_limit_choice = models.IntegerField(initial=None, choices=[[0, '4 rounds'], [1, '8 rounds']], widget=widgets.RadioSelectHorizontal, ) def get_self_field_punish(player: Player): return 'punish_p{}'.format(player.id_in_group) def get_self_field_smile(player: Player): return 'smiley_p{}'.format(player.id_in_group) def punishment_fields(player: Player): return ['punish_p{}'.format(p.id_in_group) for p in player.get_others_in_group()] def smile_fields(player: Player): return ['smiley_p{}'.format(p.id_in_group) for p in player.get_others_in_group()] # FUNCTIONS def creating_session(subsession): new_structure = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] subsession.set_group_matrix(new_structure) def random_area_change(group: Group): players = group.get_players() entered_a = [p.area_choice for p in players] group.fisher_entered = sum(entered_a) group.fisher_not_entered = C.PLAYERS_PER_GROUP - group.fisher_entered print('entered:', group.fisher_entered) print('not entered:', group.fisher_not_entered) high_effort = [p.effort_choice for p in players] high_effort_a = [p.effort_choice*p.area_choice for p in players] group.fisher_high_effort = sum(high_effort) group.fisher_high_A = sum(high_effort_a) group.fisher_high_B = group.fisher_high_effort - group.fisher_high_A print('high effort total:', group.fisher_high_effort) print('high effort A:', group.fisher_high_A) print('high effort B:', group.fisher_high_B) import random random_numb = random.random() random_numb = round(random_numb, 2) group.probability = round(group.probability, 2) print('START RANDOM: Round', group.round_number) print('Area state is low:', group.low_state) # start with whether Area A can recover and got to a high state from a low state # Determine switches from Low to High: if group.low_state: if group.fisher_entered == 0 and group.fisher_high_effort == 0: group.area_recovered = group.area_recovered + 1 elif group.fisher_entered == 0 and group.fisher_high_effort == 1: group.probability = 0.05 if random_numb <= group.probability: group.area_recovered = 0 else: group.area_recovered = group.area_recovered + 1 print('Recovery status:', group.area_recovered) elif group.fisher_entered == 0 and group.fisher_high_effort == 2: group.probability = 0.1 if random_numb <= group.probability: group.area_recovered = 0 else: group.area_recovered = group.area_recovered + 1 print('Recovery status:', group.area_recovered) elif group.fisher_entered == 0 and group.fisher_high_effort == 3: group.probability = 0.15 if random_numb <= group.probability: group.area_recovered = 0 else: group.area_recovered = group.area_recovered + 1 print('Recovery status:', group.area_recovered) elif group.fisher_entered == 0 and group.fisher_high_effort == 4: group.probability = 0.3 if random_numb <= group.probability: group.area_recovered = 0 else: group.area_recovered = group.area_recovered + 1 print('Recovery status:', group.area_recovered) elif group.fisher_entered == 0 and group.fisher_high_effort == 5: group.probability = 0.5 if random_numb <= group.probability: group.area_recovered = 0 else: group.area_recovered = group.area_recovered + 1 print('Recovery status:', group.area_recovered) elif group.fisher_entered > 0: group.area_recovered = 0 print('Recovery status:', group.area_recovered) # Determine switches from High to Low: # Cases where no one entered but changes in the number of players applying high effort in area B else: print('program enters random') if group.fisher_entered == 0 and group.fisher_high_effort == 0: group.probability = 0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 0 and group.fisher_high_effort == 1: group.probability = 0.1+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 0 and group.fisher_high_effort == 2: group.probability = 0.1+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 0 and group.fisher_high_effort == 3: group.probability = 0.1+0.15 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 0 and group.fisher_high_effort == 4: group.probability = 0.1+0.3 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 0 and group.fisher_high_effort == 5: group.probability = 0.1+0.5 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) # Cases where players enter A but everyone choses normal efforts elif group.fisher_entered == 1 and group.fisher_high_effort == 0: group.probability = 0.1+0.3 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_effort == 0: group.probability = 0.1+0.5 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 3 and group.fisher_high_effort == 0: group.probability = 0.1+0.8 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered > 3: group.change = True print('Random:', random_numb, 'VS', 'Group probability: 1') # Combinations of entering A and high effort: 1 player enters A elif group.fisher_entered == 1 and group.fisher_high_A == 1 and group.fisher_high_B == 0: group.probability = 0.1+0.3 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 1 and group.fisher_high_B == 1: group.probability = 0.1+0.3+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 0 and group.fisher_high_B == 1: group.probability = 0.1+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 0 and group.fisher_high_B == 2: group.probability = 0.1+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 0 and group.fisher_high_B == 3: group.probability = 0.1+0.15 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 0 and group.fisher_high_B == 4: group.probability = 0.1+0.3 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 1 and group.fisher_high_B == 2: group.probability = 0.1+0.3+0.1+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 1 and group.fisher_high_B == 3: group.probability = 0.1+0.3+0.1+0.15 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 1 and group.fisher_high_A == 1 and group.fisher_high_B == 4: group.probability = 0.1+0.3+0.1+0.3 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) # Combinations of entering A and high effort: 2 players enters A elif group.fisher_entered == 2 and group.fisher_high_A == 1 and group.fisher_high_B == 0: group.probability = 0.1+0.5+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 2 and group.fisher_high_B == 0: group.probability = 0.1+0.5+0.2 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 0 and group.fisher_high_B == 1: group.probability = 0.1+0.5+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 0 and group.fisher_high_B == 2: group.probability = 0.1+0.5+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 0 and group.fisher_high_B == 3: group.probability = 0.1+0.5+0.15 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 1 and group.fisher_high_B == 1: group.probability = 0.1+0.5+0.1+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 1 and group.fisher_high_B == 2: group.probability = 0.1+0.5+0.1+0.1 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 2 and group.fisher_high_A == 1 and group.fisher_high_B == 3: group.probability = 0.1+0.5+0.1+0.15 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) # Combinations of entering A and high effort: 3 players enters A elif group.fisher_entered == 3 and group.fisher_high_A == 0 and group.fisher_high_B == 1: group.probability = 0.1+0.8+0.05 if random_numb <= group.probability: group.change = True else: group.change = False print('Random:', random_numb, 'VS', 'Group probability:', group.probability) elif group.fisher_entered == 3 and group.fisher_high_A > 0 and group.fisher_high_B >= 0: group.change = True print('Random:', random_numb, 'VS', 'Group probability: 1') # IF more than 3 players enter the resource goes for sure to a low state (see code above) else: print('problem') def set_area_state(group: Group): if group.round_number > 1: pr1 = group.in_round(group.round_number) pr2 = group.in_round(group.round_number - 1) group.pr_low_state = pr2.low_state group.prev_fisher_entered = pr2.fisher_entered print('low state in prev round', pr2.low_state) area_recovered_combined = pr1.area_recovered + pr2.area_recovered if group.low_state: if area_recovered_combined == 2: group.low_state = False print('Area recovered - nobody entered') else: group.low_state = True else: if group.change: group.low_state = True print('Area changed to low:', group.low_state) else: group.low_state = group.low_state def set_payoffs(group: Group): players = group.get_players() for p in players: if group.low_state: if p.area_choice == 1 and p.effort_choice == 0: p.payoff = C.A_LOW elif p.area_choice == 1 and p.effort_choice == 1: p.payoff = C.A_LOW * C.EFFORT_MULTIPLIER elif p.area_choice == 0 and p.effort_choice == 0: p.payoff = C.B_LOW else: p.payoff = C.B_LOW * C.EFFORT_MULTIPLIER else: if p.area_choice == 1 and p.effort_choice == 0: p.payoff = C.A_HIGH elif p.area_choice == 1 and p.effort_choice == 1: p.payoff = C.A_HIGH * C.EFFORT_MULTIPLIER elif p.area_choice == 0 and p.effort_choice == 0: p.payoff = C.B_HIGH else: p.payoff = C.B_HIGH * C.EFFORT_MULTIPLIER def set_payoffs_after_punishment(group: Group): players = group.get_players() for p in players: p.payoff_before_punishment = p.payoff self_field = get_self_field_punish(p) punishments_received = [getattr(other, self_field) for other in p.get_others_in_group()] p.punishment_received = sum(punishments_received) * C.P_MULTIPLIER punishments_sent = [getattr(p, field) for field in punishment_fields(p)] p.cost_of_punishing = sum(punishments_sent) self_field_smile = get_self_field_smile(p) smileys_down_received = [getattr(other, self_field_smile) for other in p.get_others_in_group()] p.sum_smileys_down_received = sum(smileys_down_received) smileys_down_sent = [getattr(p, field) for field in smile_fields(p)] p.sum_smileys_down_sent = sum(smileys_down_sent) p.payoff = p.payoff_before_punishment - p.punishment_received - p.cost_of_punishing if p.payoff < 0: p.payoff = 0 else: p.payoff = p.payoff def area_history(group: Group): if group.round_number > 1: pr1_round = group.in_round(group.round_number - 1) group.low_state = pr1_round.low_state group.pr_low_state = pr1_round.low_state group.pr_change = pr1_round.change group.fisher_entered = pr1_round.fisher_entered if group.round_number > 2: pr2_round = group.in_round(group.round_number - 2) group.pr2_low_state = pr2_round.pr2_low_state def set_constit_rules(group: Group): players = group.get_players() dm_choices = [p.decision_making_choice for p in players] group.decision_making = sum(dm_choices) tl_choices = [p.term_limit_choice for p in players] group.term_limit = sum(tl_choices) if group.decision_making >=3: group.dem_constitution = True if group.term_limit >= 3: group.leader_whole_period = True else: group.leader_whole_period = False else: group.dem_constitution = False if group.term_limit >= 3: group.leader_whole_period = True else: group.leader_whole_period = False # PAGES class GetID(Page): form_model = 'player' form_fields = ['unified_player_id_r2'] @staticmethod def is_displayed(player): return player.round_number == 1 class Introduction(Page): pass @staticmethod def is_displayed(player): return player.round_number == 1 class ConstitRules(Page): form_model = 'player' form_fields = ['decision_making_choice', 'term_limit_choice'] @staticmethod def is_displayed(player): return player.round_number == 1 class ResultsWaitPage1(WaitPage): after_all_players_arrive = set_constit_rules print('constitutional rules set') class LeaderElection(Page): pass class area_choice(Page): form_model = 'player' form_fields = ['area_choice'] @staticmethod def vars_for_template(player: Player): group = player.group print('new round starts') return area_history(group) class effort_choice(Page): form_model = 'player' form_fields = ['effort_choice'] @staticmethod def vars_for_template(player): return dict( a_low_high=C.A_LOW * 0.1, a_high_high=C.A_HIGH * 0.1, b_low_high=C.B_LOW * 0.1, b_high_high=C.B_HIGH * 0.1, ) class ResultsWaitPage2(WaitPage): body_text = "Waiting for the other participants to decide." after_all_players_arrive = set_payoffs print('payoffs set') class ResultsWaitPage3(WaitPage): after_all_players_arrive = random_area_change print('area status calculated') class Feedback(Page): pass class Random(Page): pass class ResultsWaitPage4(WaitPage): after_all_players_arrive = set_area_state print('area state set') class Social_Punishment(Page): form_model = 'player' get_form_fields = smile_fields @staticmethod def vars_for_template(player: Player): return dict( other_players=player.get_others_in_group(), ) class Monetary_Punishment(Page): form_model = 'player' get_form_fields = punishment_fields @staticmethod def vars_for_template(player: Player): return dict( other_players=player.get_others_in_group(), schedule=C.PUNISHMENT_SCHEDULE.items(), ) @staticmethod def error_message(player, values): tot_pun = sum([int(i) for i in values.values()]) if tot_pun > player.payoff: return 'You can\'t invest more than {} to reduce the other players earnings in total'.format(player.payoff) class ResultsWaitPage5(WaitPage): after_all_players_arrive = set_payoffs_after_punishment class FeedbackPunishmentP1(Page): pass page_sequence = [GetID, Introduction, ConstitRules, ResultsWaitPage1, LeaderElection, area_choice, effort_choice, ResultsWaitPage2, ResultsWaitPage3, Feedback, Random, ResultsWaitPage4, Social_Punishment, Monetary_Punishment, ResultsWaitPage5, FeedbackPunishmentP1 ]