from otree.api import * import random, time doc = """ This is a 2-player extensive form six-move centipede game. (Since the free-of-charge Heroku server is not stable, please refresh the page if an error occurs.) """ class C(BaseConstants): NAME_IN_URL = 'hot_test' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 2 NUM_NODES = 2 GRAPH_TEMPLATE = '_templates/global/graph.html' large_pile = 12 small_pile = 3 base = 2 total_player = 2 # change this before running the experiment time_out = 1000 seconds = round(time.time()) random.seed(seconds) order = [0.4,0.6,0.75,0.9] # order = [0.4]*5+[0.6]*5+[0.75]*5+[0.9]*5 # order_new = random.sample(order,4) random.shuffle(order) selected_round = random.randint(1,NUM_ROUNDS) payoff_red = { 0.4 : [150,90,230,170,310,250,390], 0.6 : [150,110,270,230,390,350,510], 0.75 : [150,125,300,275,450,425,600], 0.9 : [150,140,330,320,510,500,690], } payoff_blue = { 0.4 : [50,190,130,270,210,350,290], 0.6 : [50,210,170,330,290,450,410], 0.75 : [50,225,200,375,350,525,500], 0.9 : [50,240,230,420,410,600,590], } class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): role_color = models.StringField() payoffs = models.IntegerField(initial=0) oppo_payoffs = models.IntegerField(initial=0) total_payoff = models.IntegerField(initial=50) node = models.IntegerField(initial=0) opponent_node = models.IntegerField(initial=0) take = models.BooleanField(initial = False) opponent_take = models.BooleanField(initial = False) decide_too_long = models.BooleanField(initial = False) wait_too_long = models.BooleanField(initial = False) time_stamp_0 = models.FloatField() time_stamp_1 = models.FloatField() time_stamp_2 = models.FloatField() time_stamp_3 = models.FloatField() count_valid = models.IntegerField(initial=0) single_player = models.BooleanField(initial=0) page_number = models.IntegerField(initial=0) dropout = models.BooleanField(initial=0) in_app_id = models.IntegerField(initial=0) oppo_in_app_id = models.IntegerField(initial=0) final_round = models.IntegerField(initial=0) # FUNCTIONS #def sent_back_amount_max(group: Group): # return group.sent_amount * C.MULTIPLIER def set_payoffs(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p1.opponent_node = p2.node p2.opponent_node = p1.node p1.opponent_take = p2.take p2.opponent_take = p1.take if p1.take == 0 and p2.take == 0: p1.payoffs = C.payoff_red[C.order[p1.round_number-1]][-1] p2.payoffs = C.payoff_blue[C.order[p1.round_number-1]][-1] else: if p2.take == 1 and p1.node == 0 and p2.node == 0: # blue player 在introduction斷線 p1.payoffs = 12 p2.payoffs = 0 elif p1.take == 1: p1.payoffs = C.payoff_red[C.order[p1.round_number-1]][2 * p2.node] p2.payoffs = C.payoff_blue[C.order[p1.round_number-1]][2 * p2.node] else: p1.payoffs = C.payoff_red[C.order[p1.round_number-1]][2 * p2.node - 1] p2.payoffs = C.payoff_blue[C.order[p1.round_number-1]][2 * p2.node - 1] p1.oppo_payoffs = p2.payoffs p2.oppo_payoffs = p1.payoffs p1.participant.payoffs_list.append(p1.payoffs) p2.participant.payoffs_list.append(p2.payoffs) p1.participant.oppo_payoffs_list.append(p1.oppo_payoffs) p2.participant.oppo_payoffs_list.append(p2.oppo_payoffs) p1.participant.nodes_list.append(p1.node) p2.participant.nodes_list.append(p2.node) p1.participant.oppo_nodes_list.append(p1.opponent_node) p2.participant.oppo_nodes_list.append(p2.opponent_node) p1.participant.takes_list.append(p1.take) p2.participant.takes_list.append(p2.take) p1.participant.oppo_takes_list.append(p1.opponent_take) p2.participant.oppo_takes_list.append(p2.opponent_take) # if p1.decide_too_long == True: # p1.total_payoff = 0 # else: # p1.total_payoff = p1.payoffs + 50 # if p2.decide_too_long == True: # p2.total_payoff = 0 # else: # p2.total_payoff = p2.payoffs + 50 def set_payoffs_final(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) p1.total_payoff = p1.participant.payoffs_list[4+C.selected_round-1] + 100 p2.total_payoff = p2.participant.payoffs_list[4+C.selected_round-1] + 100 p1.payoffs = p1.total_payoff - 100 p2.payoffs = p2.total_payoff - 100 p1.oppo_payoffs = p1.participant.oppo_payoffs_list[4+C.selected_round-1] p2.oppo_payoffs = p2.participant.oppo_payoffs_list[4+C.selected_round-1] p1.node = p1.participant.nodes_list[C.selected_round-1] p2.node = p2.participant.nodes_list[C.selected_round-1] p1.opponent_node = p1.participant.oppo_nodes_list[C.selected_round-1] p2.opponent_node = p2.participant.oppo_nodes_list[C.selected_round-1] p1.take = p1.participant.takes_list[4+C.selected_round-1] p2.take = p2.participant.takes_list[4+C.selected_round-1] p1.opponent_take = p1.participant.oppo_takes_list[4+C.selected_round-1] p2.opponent_take = p2.participant.oppo_takes_list[4+C.selected_round-1] p1.oppo_in_app_id = p1.participant.oppo_list[4+C.selected_round-1] p2.oppo_in_app_id = p2.participant.oppo_list[4+C.selected_round-1] p1.final_round = 1 p2.final_round = 1 # PAGES def graph_parameters(final_round,round,node): if final_round != 1: return dict( red1 = C.payoff_red[C.order[round-1]][0], red2 = C.payoff_red[C.order[round-1]][1], red3 = C.payoff_red[C.order[round-1]][2], red4 = C.payoff_red[C.order[round-1]][3], red5 = C.payoff_red[C.order[round-1]][4], red6 = C.payoff_red[C.order[round-1]][5], red7 = C.payoff_red[C.order[round-1]][6], blue1 = C.payoff_blue[C.order[round-1]][0], blue2 = C.payoff_blue[C.order[round-1]][1], blue3 = C.payoff_blue[C.order[round-1]][2], blue4 = C.payoff_blue[C.order[round-1]][3], blue5 = C.payoff_blue[C.order[round-1]][4], blue6 = C.payoff_blue[C.order[round-1]][5], blue7 = C.payoff_blue[C.order[round-1]][6], cont_rect_x = 45 + (node - 1) * 100, cont_text_x = 59 + (node - 1) * 100, stop_rect_x = 1 + (node - 1) * 100, stop_text_x = 15 + (node - 1) * 100, color_hover = 'LightPink' if node % 2 == 1 else 'LightSkyBlue', color_active = 'PaleVioletRed' if node % 2 == 1 else 'RoyalBlue', ) else : return dict( red1 = C.payoff_red[C.order[C.selected_round-1]][0], red2 = C.payoff_red[C.order[C.selected_round-1]][1], red3 = C.payoff_red[C.order[C.selected_round-1]][2], red4 = C.payoff_red[C.order[C.selected_round-1]][3], red5 = C.payoff_red[C.order[C.selected_round-1]][4], red6 = C.payoff_red[C.order[C.selected_round-1]][5], red7 = C.payoff_red[C.order[C.selected_round-1]][6], blue1 = C.payoff_blue[C.order[C.selected_round-1]][0], blue2 = C.payoff_blue[C.order[C.selected_round-1]][1], blue3 = C.payoff_blue[C.order[C.selected_round-1]][2], blue4 = C.payoff_blue[C.order[C.selected_round-1]][3], blue5 = C.payoff_blue[C.order[C.selected_round-1]][4], blue6 = C.payoff_blue[C.order[C.selected_round-1]][5], blue7 = C.payoff_blue[C.order[C.selected_round-1]][6], cont_rect_x = 45 + (node - 1) * 100, cont_text_x = 59 + (node - 1) * 100, stop_rect_x = 1 + (node - 1) * 100, stop_text_x = 15 + (node - 1) * 100, color_hover = 'LightPink' if node % 2 == 1 else 'LightSkyBlue', color_active = 'PaleVioletRed' if node % 2 == 1 else 'RoyalBlue', ) # note: this function goes at the module level, not inside the WaitPage. # def group_by_arrival_time_method(subsession, waiting_players): # wait_players = [p for p in waiting_players] # # red_players = [p for p in waiting_players if p.participant.id_in_group == 1] # # blue_players = [p for p in waiting_players if p.participant.id_in_group == 2] # if len(wait_players) >= 2: # # print('about to create a group') # return [wait_players[0], wait_players[1]] def waiting_too_long(player): session = player.session #total_subject = session.config['total_subject'] participant = player.participant player.page_number = participant.page_number player.dropout = participant.drop for i in range(0,session.num_participants-1): if player.get_others_in_subsession()[i].page_number == 6 or player.get_others_in_subsession()[i].dropout == True: player.count_valid += 1 if session.num_participants-1 == player.count_valid and participant.drop == False: participant.single = True player.single_player = participant.single elif participant.drop == False: player.count_valid = 0 else: player.count_valid = player.count_valid * 2 player.total_payoff = 0 return participant.single == True or participant.drop == True def group_by_arrival_time_method(subsession, waiting_players): for player in waiting_players: if waiting_too_long(player): return [player] red_players = [p for p in waiting_players if p.participant.id_in_session % 2 == 1] blue_players = [p for p in waiting_players if p.participant.id_in_session % 2 == 0] if len(red_players) > 0 and len(blue_players) > 0: for p in blue_players: if (p.participant.id_in_session // 2) % C.NUM_ROUNDS == (red_players[0].participant.id_in_session // 2 + red_players[0].round_number) % C.NUM_ROUNDS: red_players[0].page_number = 6 red_players[0].in_app_id = red_players[0].participant.id_in_session // 2 + 1 red_players[0].oppo_in_app_id = p.participant.id_in_session // 2 # red_players[0].participant.uni_id = red_players[0].participant.id_in_session p.page_number = 6 p.in_app_id = p.participant.id_in_session // 2 p.oppo_in_app_id = red_players[0].participant.id_in_session // 2 + 1 # print('about to create a group') return [red_players[0], p] class wait_page_group(WaitPage): group_by_arrival_time = True title_text = "Please wait" body_text = "System is paring" @staticmethod def app_after_this_page(player, upcoming_apps): participant = player.participant if participant.drop == True: return 'survey' class Introduction(Page): #title_text = "系統已經幫你完成配對" #body_text = "請按下一頁,開始正式實驗,請謹慎做出你的決定" # session = Player.session # timeout_seconds = session.config['experiment_time']*60 timeout_seconds = C.time_out*60 #@staticmethod #def is_displayed(player:Player): # return player.id_in_group == 1 timer_text = '剩餘時間:' #@staticmethod #def get_timeout_seconds(player): #participant = player.participant #if participant.drop == False: #return 10*60 @staticmethod def before_next_page(player, timeout_happened): #player.node += 1 player.time_stamp_0 = time.time() player.participant.oppo_list.append(player.get_others_in_group()[0].participant.id_in_session // 2) if timeout_happened: player.take = True player.decide_too_long = True participant = player.participant if participant.single == False and participant.drop == False: player.get_others_in_group()[0].wait_too_long = True @staticmethod def app_after_this_page(player, upcoming_apps): participant = player.participant if participant.single == True: return 'survey' class WaitPage_initial(WaitPage): title_text = "Please wait" body_text = "We are waiting for your opponent to enter this round." @staticmethod def is_displayed(player:Player): return player.decide_too_long == False #player.id_in_group == 1 and class Red_one(Page): form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_1 = time.time() if timeout_happened: player.take = True player.decide_too_long = True player.get_others_in_group()[0].wait_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 1 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) # class WaitPage1_red(WaitPage): # title_text = "敬請稍待" # body_text = "你的對手正在做決定,如果你的對手在10分鐘內未做出決定,系統將視同棄權,你仍舊能獲得相對應的報酬。" # def is_displayed(player:Player): # return player.id_in_group == 1 and player.take == False and player.get_others_in_group()[0].take == False class WaitPage1_blue(WaitPage): template_name = 'Hot/WaitPage_blue.html' title_text = "Please wait" body_text = "你的對手正在做決定,你至多需要等待10分鐘。" @staticmethod def is_displayed(player:Player): return player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Blue_one(Page): #body_text = "輪到你做決定了,請謹慎做出你的選擇" form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_1 = time.time() if timeout_happened: player.take = True player.decide_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 2 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=2) class WaitPage1_red(WaitPage): template_name = 'Hot/WaitPage_red.html' title_text = "Please wait" body_text = "你的對手正在做決定,你至多需要等待10分鐘。" @staticmethod def is_displayed(player:Player): return player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Red_two(Page): form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_2 = time.time() if timeout_happened: player.take = True player.decide_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 1 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=3) class WaitPage2_blue(WaitPage): template_name = 'Hot/WaitPage_blue2.html' title_text = "Please wait" body_text = "你的對手正在做決定,你至多需要等待10分鐘。" @staticmethod def is_displayed(player:Player): return player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Blue_two(Page): form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_2 = time.time() if timeout_happened: player.take = True player.decide_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 2 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=4) class WaitPage2_red(WaitPage): template_name = 'Hot/WaitPage_red2.html' title_text = "Please wait" # body_text = "你的對手正在做決定,你至多需要等待10分鐘。" @staticmethod def is_displayed(player:Player): return player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Red_three(Page): form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_3 = time.time() if timeout_happened: player.take = True player.decide_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 1 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=5) class WaitPage3_blue(WaitPage): template_name = 'Hot/WaitPage_blue3.html' title_text = "Please wait" body_text = "你的對手正在做決定,你至多需要等待10分鐘。" @staticmethod def is_displayed(player:Player): return player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Blue_three(Page): form_model = 'player' form_fields = ['take'] timeout_seconds = C.time_out*60 # refer https://otree.readthedocs.io/en/latest/timeouts.html for more timer details timer_text = '剩餘時間:' @staticmethod def before_next_page(player, timeout_happened): player.node += 1 player.time_stamp_3 = time.time() if timeout_happened: player.take = True player.decide_too_long = True @staticmethod def is_displayed(player:Player): return player.id_in_group == 2 and player.take == False and player.get_others_in_group()[0].take == False @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=6) class WaitPage3_red(WaitPage): template_name = 'Hot/WaitPage_red3.html' title_text = "Please wait" body_text = "你的對手正在做決定,你至多需要等待10分鐘。" after_all_players_arrive = set_payoffs @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number,node=6) class Results_Red(Page): """This page displays the earnings of each player""" @staticmethod def is_displayed(player:Player): return player.id_in_group == 1 @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Results_Blue(Page): """This page displays the earnings of each player""" @staticmethod def is_displayed(player:Player): return player.id_in_group == 2 @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class WaitPage_last(WaitPage): title_text = "Please wait" body_text = "We are waiting for everyone to finish this round" wait_for_all_groups = True class WaitPage_last2(WaitPage): title_text = "Please wait" body_text = "We are waiting for everyone to finish this round" after_all_players_arrive = set_payoffs_final @staticmethod def is_displayed(player:Player): return player.round_number == C.NUM_ROUNDS class Results_Red_final(Page): """This page displays the earnings of each player""" @staticmethod def is_displayed(player:Player): return player.id_in_group == 1 and player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) class Results_Blue_final(Page): """This page displays the earnings of each player""" @staticmethod def is_displayed(player:Player): return player.id_in_group == 2 and player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player): return graph_parameters(final_round = player.final_round,round=player.round_number, node=1) page_sequence = [ wait_page_group, Introduction, WaitPage_initial, Red_one, WaitPage1_blue, Blue_one, WaitPage1_red, Red_two, WaitPage2_blue, Blue_two, WaitPage2_red, Red_three, WaitPage3_blue, Blue_three, WaitPage3_red, Results_Red, Results_Blue # WaitPage_last, # WaitPage_last2, # Results_Red_final, # Results_Blue_final ] # The below are all done. # WaitPage1_red is not used; rename WaitPage2 as WaitPage1_red; rename WaitPage3 as WaitPage2_blue; rename WaitPage4 as WaitPage2_red; rename WaitPage5 as WaitPage3_blue; rename ResultsWaitPage as WaitPage3_red_result; # In waitpage, player.id_in_group == x should all be deleted.