from otree.api import * # ====================================================================================================================== doc = """ Section B (Part II) of the strategy choice and complexity task experiment. This includes 3 shortened rounds of the choice task, the implementation task and then the results. This section includes the start of Section C (Part III) of the experiment. """ # ====================================================================================================================== # 1. Defining Constants # ====================================================================================================================== class Constants(BaseConstants): name_in_url = 'practice' players_per_group = None num_rounds = 3 # Define the continuation probability and an error rate ----------------------------------------------------------- continuation_probability = 0.875 error_rate = 8 # Define game lengths game_len = [12, 6, 4, 14, 2, 9, 6, 6, 19, 3, 13, 3, 14, 9, 3] # Include the instructions template INSTRUCTIONS_TEMPLATE = __name__ + '/instructions.html' PAYOFF_TABLE = __name__ + '/payoff_table.html' # Define the strategies used in the practice round d_key_opp = ['ALLC', 'GRIM1', 'TF2T'] d_key_a = ['ALLD', 'ALLC', 'CDALT'] d_key_b = ['TFT', 'GRIM2', 'TF2T'] # Define a dictionary for payoffs --------------------------------------------------------------------------------- d_payoff = { 'CC': 5, 'DC': 7, 'CD': 0, 'DD': 2 } # Create a dictionary for strategies ------------------------------------------------------------------------------- d_strategies = { 'ALLC': {'OOOO': 'C1', 'C1CC': 'C1', 'C1CD': 'C1', 'C1DC': 'C1', 'C1DD': 'C1', }, 'ALLD': {'OOOO': 'D1', 'D1CC': 'D1', 'D1CD': 'D1', 'D1DC': 'D1', 'D1DD': 'D1', }, 'CDALT': {'OOOO': 'C1', 'C1CC': 'D1', 'C1CD': 'D1', 'C1DC': 'C1', 'C1DD': 'C1', 'D1CC': 'D1', 'D1CD': 'D1', 'D1DC': 'C1', 'D1DD': 'C1', }, 'GRIM1': {'OOOO': 'C1', 'C1CC': 'C1', 'C1CD': 'D1', 'C1DC': 'D1', 'C1DD': 'D1', 'D1CC': 'D1', 'D1CD': 'D1', 'D1DC': 'D1', 'D1DD': 'D1', }, 'GRIM2': {'OOOO': 'C1', 'C1CC': 'C1', 'C1CD': 'C2', 'C1DC': 'C2', 'C1DD': 'C2', 'C2CC': 'C1', 'C2CD': 'D1', 'C2DC': 'D1', 'C2DD': 'D1', 'D1CC': 'D1', 'D1CD': 'D1', 'D1DC': 'D1', 'D1DD': 'D1', }, 'TFT': {'OOOO': 'C1', 'C1CC': 'C1', 'C1CD': 'D1', 'C1DC': 'C1', 'C1DD': 'D1', 'D1CC': 'C1', 'D1CD': 'D1', 'D1DC': 'C1', 'D1DD': 'D1', }, 'TF2T': {'OOOO': 'C1', 'C1CC': 'C1', 'C1CD': 'C2', 'C1DC': 'C1', 'C1DD': 'C2', 'C2CC': 'C1', 'C2CD': 'D1', 'C2DC': 'C1', 'C2DD': 'D1', 'D1CC': 'C1', 'D1CD': 'D1', 'D1DC': 'C1', 'D1DD': 'D1', }, } # Create a dictionary for descriptions ----------------------------------------------------------------------------- d_description = { 'ALLC': 'Choose A in the first game. Continue to choose A for all remaining games.', 'ALLD': 'Choose B in the first game. Continue to choose B for all remaining games.', 'GRIM1': 'Choose A in the first game. In subsequent games, choose A if in the last game both players played A. If in the last game either player played B, choose B for all remaining games.', 'GRIM2': 'Choose A in the first two games. In subsequent games, choose A if in either of the last two games both players played A. If in both of the last two games either player played B, choose B for all remaining games.', 'TFT': 'Choose A in the first game. In subsequent games, choose A if in the last game your counterpart played A. Otherwise, choose B if in the last game your counterpart played B.', 'TF2T': 'Choose A in the first two games. In subsequent games, choose A if in either of the last two games your counterpart played A. Otherwise, choose B if in both of the last two games your counterpart played B.', 'CDALT': 'Choose A in the first game. In subsequent games, choose A if in the last game you played B. Otherwise, choose B if in the last game you played A.', } # Create a dictionary for image paths ----------------------------------------------------------------------------- d_image = { 'ALLC': 'strategies/ALLC.jpg', 'ALLD': 'strategies/ALLD.jpg', 'CDALT': 'strategies/CDALT.jpg', 'GRIM1': 'strategies/GRIM1.jpg', 'GRIM2': 'strategies/GRIM2.jpg', 'TFT': 'strategies/TFT.jpg', 'TF2T': 'strategies/TF2T.jpg', } # Create a dictionary for image paths ----------------------------------------------------------------------------- d_image_opp = { 'ALLD': 'strategies/opposing/ALLD_opp.jpg', 'ALLC': 'strategies/opposing/ALLC_opp.jpg', 'GRIM1': 'strategies/opposing/GRIM1_opp.jpg', 'GRIM2': 'strategies/opposing/GRIM2_opp.jpg', 'GRIM3': 'strategies/opposing/GRIM3_opp.jpg', 'TFT': 'strategies/opposing/TFT_opp.jpg', 'TF2T': 'strategies/opposing/TF2T_opp.jpg', 'TF3T': 'strategies/opposing/TF3T_opp.jpg', '2TF2T': 'strategies/opposing/2TF2T_opp.jpg', 'DTFT': 'strategies/opposing/DTFT_opp.jpg', } class Player(BasePlayer): # Define form fields practice = models.BooleanField(label="", choices=[[True, 'Option A'], [False, 'Option B']], widget=widgets.RadioSelectHorizontal) # Create fields to store histories end_history = models.StringField(initial='') # Creating fields for the implementation -------------------------------------------------------------------------- history = models.StringField(initial='OOOO') state_p = models.StringField(initial='OO') state_c = models.StringField(initial='OO') # Create a store for mistakes ind_mistake = models.BooleanField(initial=False) ind_error = models.BooleanField(initial=False) outcome = models.IntegerField(initial=0) turn_mistake = models.IntegerField(initial=0) mistake_true = models.StringField(initial='') mistake_actual = models.StringField(initial='') # Create stores for strategies strategy_p = models.StringField(initial='') strategy_c = models.StringField(initial='') class Subsession(BaseSubsession): pass class Group(BaseGroup): pass # ====================================================================================================================== # 2. Defining functions # ====================================================================================================================== # Define a function to report histories def report_history(string): string = string.replace('C', 'A') string = string.replace('D', 'B') first_string = string[4:][::2] second_string = string[5:][::2] return first_string, second_string def error(string): import random ind_error = False if random.randint(1, Constants.error_rate) == 1: string = 'C' if string == 'D' else 'D' ind_error = True return string, ind_error def live_method_function(player: Player, data): # Check if the interaction has ended ------------------------------------------------------------------------------- if len(report_history(player.history)[0]) < Constants.game_len[player.participant.practice_counter]: # Call respective update_turn functions based on treatments ---------------------------------------------------- ind_game_end = update_turn(player, data) else: # Set the value to indicate the interaction has ended ---------------------------------------------------------- ind_game_end = True print('The number of games played have been: ', len(report_history(player.history)[0])) # Create the turn_number variable if len(report_history(player.history)[0]) == 1: turn_number_message = str(len(report_history(player.history)[0])) + ' game has' else: turn_number_message = str(len(report_history(player.history)[0])) + ' games have' # Return relevant values ------------------------------------------------------------------------------------------- return {player.id_in_group: dict(history_player=report_history(player.history)[0], history_comput=report_history(player.history)[1], ind_error_player=player.ind_error, gameEnd=ind_game_end, turn_number=turn_number_message)} def update_turn(player: Player, data): # Step 1: Update the states ---------------------------------------------------------------------------------------- player.state_p = Constants.d_strategies[player.strategy_p][player.state_p + player.history[-2:]] player.state_c = Constants.d_strategies[player.strategy_c][player.state_c + player.history[-1:] + player.history[-2:-1]] # Step 2: Update the states choice_p = player.state_p[0] choice_c = player.state_c[0] # Step 3: If the implementation is manual, then check player choice against required choice if choice_p != ['D', 'C'][data['coop']]: # Intermediate if code noting the mistake that was made, on the first mistake made if not player.ind_mistake: player.turn_mistake = len(report_history(player.history)[0]) + 1 player.mistake_true = 'A' if choice_p == 'C' else 'B' player.mistake_actual = 'A' if ['D', 'C'][data['coop']] == 'C' else 'B' # Change the choice to what the user chose, and indicate a mistake was made choice_p = ['D', 'C'][data['coop']] player.ind_mistake = True # Step 4: Introduce the chance of an error that chances the action actual_p, player.ind_error = error(choice_p) actual_o = error(choice_c)[0] # Step 5: Add the actions to the history player.history += actual_p + actual_o # Step 6: Add the results to the payoff player.outcome += Constants.d_payoff[player.history[-2:]] player.outcome = [player.outcome, 0][player.ind_mistake] # Step 7: Return a True value if the game has ended return len(report_history(player.history)[0]) >= Constants.game_len[player.participant.practice_counter] # ====================================================================================================================== # 3. Defining Pages # ====================================================================================================================== class bChoice(Page): form_model = 'player' form_fields = ['practice'] @staticmethod def vars_for_template(player: Player): return dict(description_opp=Constants.d_description[Constants.d_key_opp[player.round_number - 1]], description_a=Constants.d_description[Constants.d_key_a[player.round_number - 1]], description_b=Constants.d_description[Constants.d_key_b[player.round_number - 1]], image_opp=Constants.d_image_opp[Constants.d_key_opp[player.round_number - 1]], image_a=Constants.d_image[Constants.d_key_a[player.round_number - 1]], image_b=Constants.d_image[Constants.d_key_b[player.round_number - 1]]) @staticmethod def before_next_page(player: Player, timeout_happened): player.strategy_p = [Constants.d_key_b[player.round_number - 1], Constants.d_key_a[player.round_number - 1]][ player.practice] player.strategy_c = Constants.d_key_opp[player.round_number - 1] class bInteraction(Page): form_model = 'player' live_method = live_method_function @staticmethod def vars_for_template(player: Player): return dict(counter=player.participant.practice_counter % 5 + 1, image_strategy=Constants.d_image[player.strategy_p], description_strategy=Constants.d_description[player.strategy_p]) class bResult(Page): @staticmethod def vars_for_template(player: Player): return dict(counter=player.participant.practice_counter % 5 + 1, history_player=report_history(player.history)[0], history_comput=report_history(player.history)[1], length=Constants.game_len[player.participant.practice_counter], correct=['Yes', 'No'][player.ind_mistake], payoff=player.outcome) @staticmethod def before_next_page(player: Player, timeout_happened): # Reset values to their starting value ------------------------------------ player.history = 'OOOO' player.state_c = 'OO' player.state_p = 'OO' player.ind_mistake = False player.outcome = 0 # Move the practice counter forward one ----------------------------------- player.participant.practice_counter += 1 page_sequence = [bChoice, bInteraction, bResult, bInteraction, bResult, bInteraction, bResult, bInteraction, bResult, bInteraction, bResult, ]