from otree.api import * import numpy as np import time doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'study1D' PLAYERS_PER_GROUP = None NUM_ROUNDS = 81 # should be 2*num rounds per game + 1 # NUM_ROUNDS = 11 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): if subsession.round_number == 1: subsession.session.enough_passed = {} subsession.session.actions = {} subsession.session.players_per_role = {} subsession.session.group_members = {} subsession.session.waiting_players = {1:[]} for g in range(1, subsession.session.config['n_groups']+1): subsession.session.enough_passed[g] = {1:False} subsession.session.actions[g] = {1:{1:[],2:[]}} subsession.session.players_per_role[g] = {1:0, 2:0} subsession.session.group_members[g] = [] pid = 0 for p in subsession.get_players(): p.participant.dropped_tails = False p.participant.num_timeouts = 0 p.participant.prev_partner = -1 pid += 1 p.participant.pid = pid p.participant.grupp = 0 # p.participant.player_role = pid % 2 + 1 p.participant.player_role = -1 # This is just temporary p.participant.round_payoff = [] p.participant.full_experiment = False else: for g in subsession.session.enough_passed.keys(): subsession.session.enough_passed[g][subsession.round_number] = False subsession.session.actions[g][subsession.round_number] = {1:[],2:[]} for p in subsession.get_players(): p.player_role = p.participant.player_role subsession.session.waiting_players[subsession.round_number] = [] class Group(BaseGroup): pass class Player(BasePlayer): timeout = models.BooleanField(initial=False) opp_timeout = models.BooleanField(initial=False) other = models.IntegerField(initial=0) round = models.IntegerField() choice = models.StringField(initial='') opp_choice = models.StringField(initial='') player_role= models.IntegerField(initial=0) wait_time = models.FloatField(initial=0) join_time = models.FloatField(initial=0) rand_val = models.FloatField(initial=0) forgone_payoff = models.FloatField(initial=0) forgone_action = models.StringField(initial='') p1_tails = models.IntegerField(initial=0) p1_right = models.IntegerField(initial=0) p1_left = models.IntegerField(initial=0) p1_left_prob = models.FloatField(initial=0) p1_left_otherwise = models.IntegerField(initial=0) p2_tails = models.IntegerField(initial=0) p2_left = models.IntegerField(initial=0) p2_right = models.IntegerField(initial=0) grupp = models.IntegerField(initial=0) def calc_payoff(player, choice, opp_choice): if player.player_role == 1: if choice == 'TAILS': payoff = player.p1_tails else: if opp_choice == 'RIGHT': payoff = player.p1_right else: if player.rand_val < player.p1_left_prob: payoff = player.p1_left else: payoff = player.p1_left_otherwise elif player.player_role == 2: if opp_choice == 'TAILS': payoff = player.p2_tails else: if choice == 'RIGHT': payoff = player.p2_right else: payoff = player.p2_left return payoff #Calcuate the payoff def set_payoff(player: Player): player.rand_val = np.random.rand() player.payoff = calc_payoff(player, player.choice, player.opp_choice) # action not taken if player.player_role == 1: player.forgone_action = 'TAILS' if player.choice == 'HEADS' else 'HEADS' else: player.forgone_action = 'RIGHT' if player.choice == 'LEFT' else 'LEFT' player.forgone_payoff = calc_payoff(player, player.forgone_action, player.opp_choice) player.participant.round_payoff.append(player.payoff) # if player.player_role == 1: # if player.choice == 'TAILS': # player.payoff = 0 # else: # if player.opp_choice == 'RIGHT': # player.payoff = -5 # else: # if player.rand_val < 0.9: # player.payoff = 5 # else: # player.payoff = -100 # elif player.player_role == 2: # if player.opp_choice == 'TAILS': # player.payoff = -5 # else: # if player.choice == 'RIGHT': # player.payoff = 10 # else: # player.payoff = 5 # PAGES class MyPage(Page): form_model = "player" @staticmethod def is_displayed(player: Player): if player.participant.game_stage == 1: player.p1_tails = player.session.config['p1_tails'] player.p1_right = player.session.config['p1_right'] player.p1_left = player.session.config['p1_left'] player.p1_left_prob = player.session.config['p1_left_prob'] player.p1_left_otherwise = player.session.config['p1_left_otherwise'] player.p2_tails = player.session.config['p2_tails'] player.p2_right = player.session.config['p2_right'] player.p2_left = player.session.config['p2_left'] elif player.participant.game_stage == 2: player.p1_tails = player.session.config['stage2_p1_tails'] player.p1_right = player.session.config['stage2_p1_right'] player.p1_left = player.session.config['stage2_p1_left'] player.p1_left_prob = player.session.config['stage2_p1_left_prob'] player.p1_left_otherwise = player.session.config['stage2_p1_left_otherwise'] player.p2_tails = player.session.config['stage2_p2_tails'] player.p2_right = player.session.config['stage2_p2_right'] player.p2_left = player.session.config['p2_left'] # return True if (player.other != -1): if len(player.group.get_players()) == 2: other_player = player.get_others_in_group()[0] if player.other != other_player.id_in_group: player.other = other_player.id_in_group else: player.other = -1 return player.round_number > 1 @staticmethod def live_method(player: Player, data): if data['type'] == 'act': if player.other != -1: other_player = player.group.get_player_by_id(player.other) other_player.opp_choice = data['choice'] player.choice = data['choice'] player.session.actions[player.grupp][player.round_number][player.player_role].append(player.choice) # if player.choice != "" and player.opp_choice != "" and other_player == -1: # set_payoff(player) if player.choice != "" and player.opp_choice != "": set_payoff(player) set_payoff(other_player) return {player.id_in_group: dict(type='act', choice=player.choice, opp_choice=player.opp_choice, payoff=player.payoff, forgone_action=player.forgone_action, forgone_payoff=player.forgone_payoff), other_player.id_in_group: dict(type='act', choice=other_player.choice, opp_choice=other_player.opp_choice, payoff=other_player.payoff, forgone_action=other_player.forgone_action, forgone_payoff=other_player.forgone_payoff)} elif player.choice != "" and player.other == -1: opp_choice = np.random.choice(player.session.actions[player.grupp][player.round_number][(player.player_role % 2) + 1]) player.opp_choice = opp_choice set_payoff(player) return {player.id_in_group: dict(type='act', choice=player.choice, opp_choice=player.opp_choice, payoff=player.payoff, forgone_action=player.forgone_action, forgone_payoff=player.forgone_payoff)} else: return {player.id_in_group: dict(type='act', choice=player.choice, opp_choice=player.opp_choice)} elif data['type'] == 'load': return{player.id_in_group: dict(type='load', choice=player.choice, opp_choice=player.opp_choice, payoff=player.payoff, timeout=player.timeout, opp_timeout=player.opp_timeout, forgone_action=player.forgone_action, forgone_payoff=player.forgone_payoff)} elif data['type'] == 'timeout': player.payoff = 0 player.participant.num_timeouts += 1 player.timeout = True if player.participant.num_timeouts >= player.session.config['max_timeouts']: player.participant.dropped_tails = True if player.other != -1: other_player = player.group.get_player_by_id(player.other) other_player.payoff = 10 other_player.opp_timeout = True return{player.id_in_group: dict(type='timeout', num_timeouts=player.participant.num_timeouts), other_player.id_in_group: dict(type='opp_timeout', payoff=10)} else: return {player.id_in_group: dict(type='timeout', num_timeouts=player.participant.num_timeouts)} elif data['type'] == 'opp_timeout': player.payoff = 10 player.opp_timeout = True return{player.id_in_group: dict(type='opp_timeout', payoff=player.payoff)} @staticmethod def vars_for_template(player: Player): if player.player_role == 1: opt1 = "HEADS" opt2 = "TAILS" elif player.player_role == 2: opt1 = "LEFT" opt2 = "RIGHT" elif player.player_role == -1: opt1 = "WRONG" opt2 = "WRONG" return dict( player_role=player.player_role, opt1=opt1, opt2=opt2, ghost=player.other == -1, ) class DropOut(Page): @staticmethod def is_displayed(player: Player): return player.participant.dropped_tails class FullExperiment(Page): @staticmethod def is_displayed(player: Player): return player.participant.full_experiment class MatchWaitPage(WaitPage): group_by_arrival_time = True # body_text = "Waiting to be matched with another player. Remember that you are being paid for the time spent on this waiting page, so please be patient." template_name = 'oneshotDisaster/MyWaitPage.html' @staticmethod def is_displayed(player: Player): if player.join_time == 0: player.join_time = time.time() return True class GameChange(Page): @staticmethod def is_displayed(player: Player): if player.subsession.round_number == (C.NUM_ROUNDS -1)/2 +1: player.participant.vars['game_stage'] = 2 return True else: return False @staticmethod def vars_for_template(player: Player): return dict( num_rounds_per_game=int(round((C.NUM_ROUNDS -1)/2)), ) def group_by_arrival_time_method(subsession: Subsession, waiting_players): return_players = [] print("Körs 1") waiting_ids = [p.participant.id_in_session for p in waiting_players] if waiting_ids == subsession.session.vars["waiting_players"][subsession.round_number]: return [] else: subsession.session.vars["waiting_players"][subsession.round_number] = waiting_ids print("Körs 2") # for p in waiting_players: # if p.join_time == 0: # p.join_time = time.time() round_number = subsession.round_number # print(f"Number of waiting players: {len(waiting_players)}") if subsession.round_number == 1: waiting_players_no_group = [p for p in waiting_players if p.grupp == 0] now = time.time() if len(waiting_players_no_group) > 0: max_wait_time = max([now - p.join_time for p in waiting_players_no_group]) # print(f"max wait time for no group is: {round(max_wait_time)}") else: max_wait_time = 0 n_full_groups = len([g for g in subsession.session.enough_passed.keys() if subsession.session.enough_passed[g][1]]) if n_full_groups >= subsession.session.config['n_groups']: for p in waiting_players_no_group: p.participant.full_experiment = True if len(waiting_players_no_group) > 0: return waiting_players_no_group # return waiting_players_no_group if (len(waiting_players_no_group) >= subsession.session.config['n_per_group']) or ((max_wait_time > subsession.session.config['round_1_wait_time']) and len(waiting_players_no_group) >= 2): group_members = subsession.session.group_members max_g = max([g for g in group_members.keys() if len(group_members[g]) > 0] + [0]) new_g = max_g + 1 n_waiting = len(waiting_players_no_group) n_range = min(n_waiting, subsession.session.config['n_per_group']) new_players = waiting_players_no_group[0:n_range] for p in new_players: p.participant.grupp = new_g p.grupp = new_g group_members[new_g] = [p.participant.pid for p in new_players] subsession.session.enough_passed[new_g][1] = True for p in new_players: if p.participant.player_role == -1: num_p1 = subsession.session.players_per_role[new_g][1] num_p2 = subsession.session.players_per_role[new_g][2] if num_p1 <= num_p2: p.participant.player_role = 1 subsession.session.players_per_role[new_g][1] += 1 else: p.participant.player_role = 2 subsession.session.players_per_role[new_g][2] += 1 p.player_role = p.participant.player_role groups = np.unique([p.participant.grupp for p in waiting_players if p.participant.grupp != 0]) g_to_send = -1 for g in groups: if subsession.session.enough_passed[g][round_number]: g_to_send = g waiting_in_group = [p for p in waiting_players if p.participant.grupp == g] break if g_to_send == -1: waiting_per_group = {g: [p for p in waiting_players if p.participant.grupp == g] for g in groups} n_per_group = {g: len(waiting_per_group[g]) for g in groups} now = time.time() for g in groups: if len(waiting_per_group[g]) > 0: max_wait_time = max([now - p.join_time for p in waiting_per_group[g]]) # print(f"max wait time for group {g} is: {round(max_wait_time)}") else: max_wait_time = 0 if (n_per_group[g] >= len(subsession.session.group_members[g])) or ((max_wait_time > subsession.session.config['round_n_wait_time']) and (n_per_group[g] >= 2)): if subsession.session.enough_passed[g][round_number] == False: subsession.session.group_members[g] = [p.participant.pid for p in waiting_per_group[g]] subsession.session.enough_passed[g][round_number] = True for g in groups: if subsession.session.enough_passed[g][round_number]: g_to_send = g waiting_in_group = waiting_per_group[g] break # if subsession.round_number == 1: # for p in waiting_players: # if p.participant.player_role == -1: # num_p1 = subsession.session.players_per_role[1] # num_p2 = subsession.session.players_per_role[2] # if num_p1 <= num_p2: # p.participant.player_role = 1 # subsession.session.players_per_role[1] += 1 # else: # p.participant.player_role = 2 # subsession.session.players_per_role[2] += 1 # p.player_role = p.participant.player_role if g_to_send == -1: return [] # print(waiting_per_group) p1s = list(filter(lambda p: p.participant.player_role == 1, waiting_in_group)) p2s = list(filter(lambda p: p.participant.player_role == 2, waiting_in_group)) # print(f"antal p1 {len(p1s)} och antal p2 {len(p2s)}") # if subsession.session.enough_passed[subsession.round_number]: if (len(p1s) > 0) and (len(p2s) > 0): iterations = 1 if (len(p1s) == 1) and (len(p2s) == 1) and (p1s[0].participant.prev_partner != p2s[0].participant.pid) and (p2s[0].participant.prev_partner != p1s[0].participant.pid): return_players = [p1s[0], p2s[0]] elif len(p1s) == 2 and len(p2s) == 2: to_match = True while to_match: if iterations > 100: return_players = [] to_match = False break np.random.shuffle(waiting_players) np.random.shuffle(p1s) np.random.shuffle(p2s) iterations += 1 if (p1s[0].participant.prev_partner != p2s[0].participant.pid) and (p2s[0].participant.prev_partner != p1s[0].participant.pid): return_players = [p1s[0], p2s[0]] to_match = False elif (len(p1s) >= 1) and (len(p2s) >= 1): to_match = True while to_match: if iterations > 100: return_players = [] to_match = False break np.random.shuffle(waiting_players) np.random.shuffle(p1s) np.random.shuffle(p2s) iterations += 1 # see if the first p1 and first p2 have played together in the previous round if p1s[0].participant.prev_partner != p2s[0].participant.pid: return_players = [p1s[0], p2s[0]] to_match = False # elif subsession.round_number ==1: # return_players = [waiting_players[0]] # elif len(waiting_per_group[g_to_send]) >= 1: # p = waiting_per_group[g_to_send][0] # if (time.time() - p.join_time > subsession.session.config['single_wait_time']) and (len(subsession.session.actions[subsession.round_number][1]) > 0) and (len(subsession.session.actions[subsession.round_number][2]) > 0): # return_players = [p] # p.other = -1 remaining_players = p1s + p2s # print(f"return players är : {return_players}") if (len(remaining_players) > 0) and (len(return_players) == 0): p = remaining_players[0] opp_role = (p.participant.player_role % 2) + 1 if len(p.session.actions[p.participant.grupp][p.round_number][opp_role]) > 0: return_players = [remaining_players[0]] if len(return_players) == 2: return_players[0].participant.prev_partner = return_players[1].participant.pid return_players[1].participant.prev_partner = return_players[0].participant.pid return_players[0].other = return_players[1].id_in_group return_players[1].other = return_players[0].id_in_group elif len(return_players) == 1: return_players[0].participant.prev_partner = -1 return_players[0].other = -1 if len(return_players) > 0: for p in return_players: p.wait_time = time.time() - p.join_time p.participant.tot_wait_time += p.wait_time p.player_role = p.participant.player_role p.grupp = p.participant.grupp # print(return_players) return return_players # # Version below is for asynchronous matching # def group_by_arrival_time_method(subsession: Subsession, waiting_players): # return_players = [] # for p in waiting_players: # if p.join_time == 0: # p.join_time = time.time() # # if subsession.round_number == 1: # for p in waiting_players: # if p.participant.player_role == -1: # num_p1 = subsession.session.players_per_role[1] # num_p2 = subsession.session.players_per_role[2] # if num_p1 <= num_p2: # p.participant.player_role = 1 # subsession.session.players_per_role[1] += 1 # else: # p.participant.player_role = 2 # subsession.session.players_per_role[2] += 1 # p.player_role = p.participant.player_role # p1s = list(filter(lambda p: p.participant.player_role == 1, waiting_players)) # p2s = list(filter(lambda p: p.participant.player_role == 2, waiting_players)) # if (len(p1s) >= subsession.session.config['min_group_size']/2) and (len(p2s) >= subsession.session.config['min_group_size']/2): # subsession.session.enough_passed[subsession.round_number] = True # if subsession.session.enough_passed[subsession.round_number]: # if len(waiting_players) >= 2: # iterations = 1 # if (len(p1s) == 1) and (len(p2s) == 1) and (p1s[0].participant.prev_partner != p2s[0].participant.pid) and (p2s[0].participant.prev_partner != p1s[0].participant.pid): # return_players = [p1s[0], p2s[0]] # elif len(p1s) == 2 and len(p2s) == 2: # to_match = True # while to_match: # if iterations > 100: # return_players = [] # to_match = False # break # np.random.shuffle(waiting_players) # np.random.shuffle(p1s) # np.random.shuffle(p2s) # iterations += 1 # if (p1s[0].participant.prev_partner != p2s[0].participant.pid) and (p2s[0].participant.prev_partner != p1s[0].participant.pid): # return_players = [p1s[0], p2s[0]] # to_match = False # elif (len(p1s) >= 1) and (len(p2s) >= 1): # to_match = True # while to_match: # if iterations > 100: # return_players = [] # to_match = False # break # np.random.shuffle(waiting_players) # np.random.shuffle(p1s) # np.random.shuffle(p2s) # iterations += 1 # # see if the first p1 and first p2 have played together in the previous round # if p1s[0].participant.prev_partner != p2s[0].participant.pid: # return_players = [p1s[0], p2s[0]] # to_match = False # elif subsession.round_number ==1: # return_players = [waiting_players[0]] # elif len(waiting_players) >= 1: # p = waiting_players[0] # if (time.time() - p.join_time > subsession.session.config['single_wait_time']) and (len(subsession.session.actions[subsession.round_number][1]) > 0) and (len(subsession.session.actions[subsession.round_number][2]) > 0): # return_players = [p] # p.other = -1 # if len(return_players) == 2: # return_players[0].participant.prev_partner = return_players[1].participant.pid # return_players[1].participant.prev_partner = return_players[0].participant.pid # return_players[0].other = return_players[1].id_in_group # return_players[1].other = return_players[0].id_in_group # elif len(return_players) == 1: # return_players[0].participant.prev_partner = -1 # return_players[0].other = -1 # if len(return_players) > 0: # for p in return_players: # p.wait_time = time.time() - p.join_time # p.participant.tot_wait_time += p.wait_time # print(return_players) # return return_players page_sequence = [MatchWaitPage, FullExperiment, MyPage, DropOut, GameChange]