import random from otree.api import * """ Sim for 'Matching Pennies' game """ class Constants(BaseConstants): name_in_url = 'pennies' players_per_group = 2 num_rounds = 12 instructions_template = 'pennies/instructions.html' row_role = 'Row' column_role = 'Column' class Subsession(BaseSubsession): stage = models.IntegerField() class Group(BaseGroup): pass class Player(BasePlayer): choice = models.StringField( choices=['Heads', 'Tails'], widget=widgets.RadioSelect, label="Please make your choice.", ) # FUNCTIONS def creating_session(subsession: Subsession): subsession.stage = 1 if subsession.round_number == 1: subsession.group_randomly() for p in subsession.get_players(): p.participant.vars['total'] = 0 else: if subsession.round_number >= 5 and subsession.round_number < 9: subsession.group_randomly() subsession.stage = 2 elif subsession.round_number >= 9: subsession.group_randomly() subsession.stage = 3 else: subsession.group_like_round(subsession.round_number - 1) def other_player(player: Player): return player.get_others_in_group()[0] def set_payoff(player: Player): subsession = player.subsession group = player.group if player.choice == 'Heads' and other_player(player).choice == 'Heads': if player.role == Constants.column_role: player.payoff = 0 else: # Row if subsession.stage == 1: player.payoff = 1 elif subsession.stage == 2: player.payoff = 9 else: player.payoff = 0.5 else: row_player = group.get_player_by_role(Constants.row_role) col_player = group.get_player_by_role(Constants.column_role) payoff = { 'Heads': {'Tails': [0, 1]}, 'Tails': {'Tails': [1, 0], 'Heads': [0, 1]}, } r = 0 if player.role == Constants.row_role else 1 # position of payoff in list player.payoff = payoff[row_player.choice][col_player.choice][r] # PAGES class Introduction(Page): @staticmethod def is_displayed(player: Player): return ( player.round_number == 1 or player.round_number == 5 or player.round_number == 9 ) class Main(Page): form_model = 'player' form_fields = ['choice'] @staticmethod def vars_for_template(player: Player): subsession = player.subsession if subsession.round_number % 4 != 0: return {'round': subsession.round_number % 4} else: return {'round': 4} class ResultsWaitPage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): for p in group.get_players(): set_payoff(p) p.participant.vars['total'] += int(p.payoff) class Results(Page): @staticmethod def vars_for_template(player: Player): subsession = player.subsession round_number = subsession.round_number opponent = other_player(player) r = (round_number % 4) or 4 return { 'player_payoff': int(player.payoff), 'opponent_choice': opponent.choice, 'stage': (round_number // 4) + 1, 'round': r, } @staticmethod def before_next_page(player: Player, timeout_happened): for p in player.group.get_players(): p.participant.vars['total'] = 0 # repeated sequence because groups must be rematched only after every 4 rounds page_sequence = [Introduction, Main, ResultsWaitPage, Results]