from otree.api import * import random doc = """ Your app description """ ###################### MODELS ####################### class C(BaseConstants): NAME_IN_URL = 'volunteer_dilemma' PLAYERS_PER_GROUP = 3 #左の数字はダミー(実際には下のgroup_matrixで制御) NUM_ROUNDS = 10 #左の数字はダミー(実際にはsettings.pyのnum_of_roundsでラウンド数を制御) seed = random.randint(0,1000) class Subsession(BaseSubsession): round_id = models.IntegerField() def creating_session(subsession: Subsession): ### ① 閾値、成功事報酬、失敗時報酬、協力コストを任意に固定したい場合 #### # ラウンド毎に値を変えたい時は、success_list = [60,100,30,50,120,80,70,100,80,100]のようにする # 全ラウンド固定したい時は、success_list = [100] * C.NUM_ROUNDSのようにする threshold_list = [3,3,1,2,2,3,2,6,1,5] success_list = [60,100,30,50,120,80,70,100,80,100] fail_list = [10,0,0,0,10,10,0,10,0,10] cost_list = [30,15,20,10,20,15,30,10,20,10] ### ② ランダムにしたい場合 #### # threshold_list = [random.randint(1, 5) for _ in range(C.NUM_ROUNDS)] # success_list = [random.randint(1, 10)*10 for _ in range(C.NUM_ROUNDS)] # fail_list = [random.choice([0, 0, 0, 0, 10]) for _ in range(C.NUM_ROUNDS)] # cost_list = [random.randint(1, 3)*10 for _ in range(C.NUM_ROUNDS)] # 同時手番ならsetting.pyのSESSION_CONFIGSででtreatment=0 # 逐次手番ならtreatment=1 # ラウンド毎に同時 or 逐次を任意に指定したいならtreatment=2 if subsession.session.config['treatment'] == 0: is_sequential_list = [0] * C.NUM_ROUNDS elif subsession.session.config['treatment'] == 1: is_sequential_list = [1] * C.NUM_ROUNDS else: is_sequential_list = [0,0,0,0,0,1,1,1,1,1] # 各ラウンドで同時(0)or逐次(1) # マッチングを制御 if subsession.session.num_participants == 6: r1 = [[1,2,3,4,5,6,]] #ラウンド1は2つの3人グループ(「参加者1〜3」と「参加者4〜6」) r2 = [[4,6,2],[3,5,1]] r3 = [[1,2],[3,4],[5,6]] r4 = [[1,2,3],[4,5,6,]] r5 = [[1,2,3],[4,5,6,]] r6 = [[4,6,2],[3,5,1]] r7 = [[1,2],[3,4],[5,6]] r8 = [[1,2,3,4,5,6,]] r9 = [[1,2],[3,4],[5,6]] r10 = [[1,2,3,4,5,6,]] else: r1 = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20],[21,22,23,24],[25,26,27,28]] r2 = [[1,10,7,8],[5,2,15,4],[9,6,3,16],[13,18,11,12],[17,14,27,24],[21,26,19,28],[25,22,23,20]] r3 = [[1,6,15,20],[5,10,23,24],[9,2,27,4],[13,26,19,8],[17,22,3,12],[21,14,11,16],[25,18,7,28]] r4 = [[1,18,11,24],[5,26,27,8],[9,22,19,20],[13,2,23,4],[17,6,15,28],[21,10,7,12],[25,14,3,16]] r5 = [[1,14,23,12],[5,18,3,16],[9,26,11,24],[13,10,27,20],[17,2,7,4],[21,22,15,28],[25,6,19,8]] r6 = [[1,2],[3,4],[5,10],[7,28],[9,14],[11,20],[13,18],[15,8],[17,26],[19,16],[21,6],[23,24],[25,22],[27,12]] r7 = [[1,10],[3,12],[5,14],[7,4],[9,2],[11,28],[13,6],[15,20],[17,18],[19,8],[21,22],[23,16],[25,26],[27,24]] r8 = [[1,14],[3,24],[5,22],[7,12],[9,6],[11,4],[13,10],[15,16],[17,2],[19,20],[21,18],[23,8],[25,26],[27,28]] r9 = [[1,22],[3,16],[5,6],[7,24],[9,18],[11,12],[13,14],[15,4],[17,10],[19,28],[21,26],[23,20],[25,2],[27,8]] r10 = [[1,2],[3,8],[5,18],[7,16],[9,22],[11,24],[13,26],[15,12],[17,14],[19,4],[21,10],[23,28],[25,6],[27,20]] group_matrix = [r1,r2,r3,r4,r5,r6,r7,r8,r9,r10] #ラウンドの並び順をランダムにしたいたいとき用のコード shuffle_matrix = [1,2,3,4,5,6,7,8,9,10] if subsession.session.config['is_rounds_fix'] != 1: random.seed(C.seed) random.shuffle(shuffle_matrix) lists_to_sort = [group_matrix, threshold_list, success_list, fail_list, cost_list, is_sequential_list] for i in range(len(lists_to_sort)): lists_to_sort[i] = [x for _, x in sorted(zip(shuffle_matrix, lists_to_sort[i]))] for i in range(1, len(lists_to_sort)): for j in range(3, len(lists_to_sort[i])): lists_to_sort[i][j] = None group_matrix, threshold_list, success_list, fail_list, cost_list, is_sequential_list = lists_to_sort round_id_list = [i for _,i in sorted(zip(shuffle_matrix, [i for i in range(1, len(group_matrix)+1)]))] subsession.set_group_matrix(group_matrix[subsession.round_number -1]) # subsession.group_randomly(fixed_id_in_group = True) subsession.round_id = round_id_list[subsession.round_number -1] for group in subsession.get_groups(): group.set_num_of_group() group.threshold = threshold_list[group.round_number-1] group.success = success_list[group.round_number-1] group.fail = fail_list[group.round_number-1] group.cost = cost_list[group.round_number-1] group.is_sequential = is_sequential_list[group.round_number-1] class Group(BaseGroup): num_of_group = models.IntegerField() threshold = models.IntegerField() success = models.IntegerField() fail = models.IntegerField() cost = models.IntegerField() is_sequential = models.IntegerField() total_contribution = models.IntegerField(initial=0) def set_num_of_group(group): group.num_of_group = len(group.get_players()) class Player(BasePlayer): contribution = models.IntegerField(initial=999) timeout = models.IntegerField() def set_payoffs(group: Group): players = group.get_players() contributions = [p.contribution for p in players] group.total_contribution = sum(contributions) for player in players: if group.total_contribution >= group.threshold: player.payoff = group.success else: player.payoff = group.fail if player.contribution == 1: player.payoff = player.payoff - group.cost ###################### PAGES ####################### class StartWaitPage(Page): def is_displayed(player): return player.round_number == 1 class Start(Page): def is_displayed(player): return player.round_number == 1 class RoundStart(Page): pass class Choice(Page): template_name = 'volunteer_dilemma/Choice.html' form_model = 'player' form_fields = ['contribution'] timeout_seconds = 90 timer_text = '' # タイムアウトした時、timeoutという変数を1にする @staticmethod def before_next_page(player, timeout_happened): if timeout_happened: player.timeout = 1 @staticmethod def vars_for_template(player: Player): players = player.group.get_players() contributions = [p.contribution for p in players] return dict( coop_success = player.group.success - player.group.cost, coop_fail = abs(player.group.fail - player.group.cost), coop_fail_minus = player.group.fail - player.group.cost, count_cont = len(players) - contributions.count(999) + 1, how_many = contributions.count(1), how_many_more = player.group.threshold - contributions.count(1), threshold_minus1 = player.group.threshold - 1, threshold_minus2 = player.group.threshold - 2, threshold_plus1 = player.group.threshold + 1 ) class ChoiceSimultaneous(Choice): @staticmethod def is_displayed(player): return player.group.is_sequential == 0 class ChoiceSequential1(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 1 and player.group.is_sequential != 0 class ChoiceSequential2(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 2 and player.group.is_sequential != 0 class ChoiceSequential3(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 3 and player.group.is_sequential != 0 class ChoiceSequential4(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 4 and player.group.is_sequential != 0 class ChoiceSequential5(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 5 and player.group.is_sequential != 0 class ChoiceSequential6(Choice): @staticmethod def is_displayed(player): return player.id_in_group == 6 and player.group.is_sequential != 0 class ResultsWaitPage(WaitPage): template_name = 'volunteer_dilemma/ResultsWaitPage.html' body_text = '' title_text = '' after_all_players_arrive = 'set_payoffs' @staticmethod def vars_for_template(player: Player): players = player.group.get_players() contributions = [p.contribution for p in players] return dict( count_cont = len(players) - contributions.count(999) + 1, how_many = contributions.count(1), how_many_more = player.group.threshold - contributions.count(1) ) class Results(Page): timeout_seconds = 20 timer_text = '' pass class RematchDummy(Page): def is_displayed(player): return player.round_number != player.session.config['num_of_rounds'] timeout_seconds = 3 class FinalResults(Page): def is_displayed(player): return player.round_number == player.session.config['num_of_rounds'] # page_sequence = [ # # StartWaitPage, # Start, RoundStart, Choice, ResultsWaitPage, Results, RematchDummy, FinalResults] page_sequence = [ # StartWaitPage, Start, # RoundStart, ChoiceSimultaneous, ChoiceSequential1, ResultsWaitPage, ChoiceSequential2, ResultsWaitPage, ChoiceSequential3, ResultsWaitPage, ChoiceSequential4, ResultsWaitPage, ChoiceSequential5, ResultsWaitPage, ChoiceSequential6, ResultsWaitPage, Results, RematchDummy, FinalResults]