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]
