oTreeで公共財ゲームを実装する際に、分配ポイントをランダムにする方法をまとめておきます。また、固定値ではなくフォームの最大値・最小値を動的に設定する方法もまとめます。
例えば、公共財ゲームで、初期の分配ポイントを固定20点から、変動型の20~30点の範囲にしたいときに、どのようにコードを書けばいいかまとめておきます(自分の備忘録です)。
oTreeに付属のpublic_goods_simpleをベースに作成しています。oTree 5.7.2での動作を確認しています。
randomの使用は禁止されている
import random class C(BaseConstants): #20~30点の範囲でランダムに配分する ENDOWMENT = random.randint(20, 30)
oTreeでは、関数以外の部分でrandomを使用することを禁止されています。つまり、上記の様なコードを書いて、class C(BaseConstants):
の中で、ランダムを使用することはできません。
公共財ゲームでランダムに分配ポイントを決定する
import random class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() P_ENDOWMENT = models.CurrencyField(initial=cu(0)) # 労働によって還元されるポイントの設定(今回はランダムに設定) def set_Endowment(group: Group): group.P_ENDOWMENT = group.P_ENDOWMENT + random.randint(20,30) #参加者が集合したら、利得を設定するWaitPageを用意する class PlayerWaitPage(WaitPage): after_all_players_arrive = set_Endowment
分配するポイントをGroupクラスに”P_ENDOWMENT”として定義します。Cクラスの関数は動的に定義することができないので、Groupクラスに定義しています。
そして、動的に変動させるために、set_Endowment関数を定義します。その後、ゲーム開始前に、分配ポイントを決定するために、PlayerWaitPageを新設し、after_all_players_arriveで参加者が集合したら実行します。
あとは、”Contribution.html”の”C.ENDOWMENT”を”group.P_ENDOWMENT”にしておきましょう。
フォームの最大値・最小値を動的に決定する
# フォームに最大値の制約をかける def contribution_max(player): return player.group.PENDOWMENT
oTree公式ページの動的なフォームの検証で、フォーム内に設定するminやmaxを動的に定義する方法が書かれています。
定義の方法は簡単で”{フォーム名}_max”や”{フォーム名}_min”で定義するだけです。特にどこかで実行する必要はなく、書くだけでOK (なぜこれでうまくいくのかは謎ですが)。
oTreeが5.7になり、selfが廃止され、ファイルもシンプルになり、非常に書きやすくなりました。
フルコード
from otree.api import * import random class C(BaseConstants): NAME_IN_URL = 'public_goods_simple_article' PLAYERS_PER_GROUP = 3 NUM_ROUNDS = 1 ENDOWMENT = cu(100) MULTIPLIER = 1.8 class Subsession(BaseSubsession): pass class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() P_ENDOWMENT = models.CurrencyField(initial=cu(0)) class Player(BasePlayer): contribution = models.CurrencyField( min=0, label="How much will you contribute?" ) # FUNCTIONS def set_payoffs(group: Group): players = group.get_players() contributions = [p.contribution for p in players] group.total_contribution = sum(contributions) group.individual_share = ( group.total_contribution * C.MULTIPLIER / C.PLAYERS_PER_GROUP ) for p in players: p.payoff = C.ENDOWMENT - p.contribution + group.individual_share # 労働によって還元されるポイントの設定(今回はランダムに設定) def set_Endowment(group: Group): group.P_ENDOWMENT = group.P_ENDOWMENT + random.randint(20,30) # フォームの最大値を動的に設定する。 def contribution_max(player): return player.group.P_ENDOWMENT # PAGES class PlayerWaitPage(WaitPage): after_all_players_arrive = set_Endowment class Contribute(Page): set_Endowment form_model = 'player' form_fields = ['contribution'] class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs class Results(Page): pass page_sequence = [PlayerWaitPage, Contribute, ResultsWaitPage, Results]