Part 2: 公共財ゲーム¶
YouTube のビデオチュートリアル(英語)も参照してください。
ここでは、 公共財ゲーム を作成します。公共財ゲームは経済学の古典的なゲームです。
3人1組でプレイされる公共財ゲームを考えましょう。各プレイヤーには初期保有として1000ポイントが与えられます。プレイヤーは、グループに貢献したいポイント数を個別に決定します。3人のプレイヤーによる貢献額の合計は2倍された後、3人に均等に分割され、プレイヤーに再分配されます。
以下で解説する公共財ゲームの完全なコードは ここ にあります。
アプリの作成¶
新たにアプリを作成し、名前を my_public_goods
とします。
定数¶
定数を定義するクラス class C
に移動します。(詳細については、 Constants を参照してください。)
- 埋め込み変数
PLAYERS_PER_GROUP
を3に設定します。oTreeは自動的にプレイヤーを3人1組にグループ分けします。 - 各プレイヤーへの初期保有は1000ポイントです。新たに
ENDOWMENT
なる変数を定義してその値を1000
に設定しましょう。 - プレイヤーの貢献額は2倍されます。倍率として、新たに
MULTIPLIER
なる変数を定義してその値を2
に設定しましょう。
class C
の中身は以下のようになります。
PLAYERS_PER_GROUP = 3
NUM_ROUNDS = 1
ENDOWMENT = cu(1000)
MULTIPLIER = 2
モデル (記録すべき実験データの定義)¶
実験参加者の行動を後々分析するために、各プレイヤーが貢献したポイント数を記録しなければなりません。データベースに記録する変数(フィールド)でプレイヤー一人ひとりについて定義されるものは class Player
(プレイヤーモデルと呼ばれます)の中で定義します。class Player
に移動し、 contribution
なるフィールドを以下のように定義します。
class Player(BasePlayer):
contribution = models.CurrencyField(
min=0,
max=C.ENDOWMENT,
label="How much will you contribute?"
)
実験参加者への謝金の支払いのためには、各プレイヤーの利得を記録する必要がありますが、oTreeではあらかじめプレイヤーモデルで payoff
なるフィールドが定義されているため、改めてフィールドを明示的に定義する必要はありません。
各プレイヤーの意思決定データだけでなく、各グループについて、グループへの貢献額の合計と、各プレイヤーに分配されるポイント数を記録したい場合は、グループモデル class Group
でこれら2つのフィールドを定義します。
class Group(BaseGroup):
total_contribution = models.CurrencyField()
individual_share = models.CurrencyField()
ページ¶
このゲームでは3つのページを表示します。
- ページ1: プレイヤーはグループへの貢献額を決定し入力します。
- ページ2: 待機ページ。プレイヤーはグループ内の他の参加者の操作が終わるまで待機します。
- ページ3: プレイヤーに結果が通知されます。
ページ1: Contribute¶
貢献額を入力するページの名前は Contribute
としましょう。まず、 クラス class Contribute
を定義します。Contribute
ページには貢献額の入力フォームが含まれているため、 form_model
と form_fields
を定義する必要があります。貢献額 contribution
はプレイヤーモデルのフィールドなので、 form_model
は player
と設定します。form_fields
には具体的なフィールド( contribution
)をリストで渡します。(詳細については、 フォーム を参照してください。)
class Contribute(Page):
form_model = 'player'
form_fields = ['contribution']
次に、HTMLテンプレートを作成します。
{{ title block }}
ブロック内にタイトルとして Contribute
と入力し、 {{ content block }}
ブロック内を以下のように設定します。
<p>
This is a public goods game with
{{ C.PLAYERS_PER_GROUP }} players per group,
an endowment of {{ C.ENDOWMENT }},
and a multiplier of {{ C.MULTIPLIER }}.
</p>
{{ formfields }}
{{ next_button }}
ページ2: ResultsWaitPage¶
すべてのプレイヤーが Contribute
ページを完了した後、各プレイヤーの利得を計算します。利得の計算は、set_payoffs
なる関数を定義して、そこで実行することにしましょう。関数 set_payoffs
はグループモデルの変数や関数を利用するため、引数として group
なるオブジェクトを受け取ることにします。関数 set_payoffs
は具体的に以下のように定義します。
def set_payoffs(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 player in players:
player.payoff = C.ENDOWMENT - player.contribution + group.individual_share
プレイヤーが貢献額を意思決定した後、他のプレイヤーが意思決定し終わるのを待ってから関数 set_payoffs
を呼び出す必要があります。グループ内の他のプレイヤーが意思決定を完了するまで待機するページ WaitPage
を追加します。ここでは WaitPage
の名前を ResultsWaitPage
としましょう( class ResultsWaitPage (WaitPage):
とクラスを定義する)。プレイヤーが ResultsWaitPage
ページに到達すると、グループ内の他のすべてのプレイヤーが ResultsWaitPage
ページに到達するまで待機します(詳細は、 Wait page を参照してください)。
class ResultsWaitPage
内で、動かしたい関数の名前( set_payoffs
)を組み込み変数 after_all_players_arrive
に渡します。この設定により、すべてのプレイヤーが ResultsWaitPage
ページに到達したことをトリガーとして関数 set_payoffs
が呼び出されます。
after_all_players_arrive = 'set_payoffs'
ページ3: Results¶
次に、 結果を表示する Results
という名前のページを作成します(クラス class Results
を定義する)。テンプレートの {{ content block }}
ブロックを以下のように設定します。
<p>
You started with an endowment of {{ C.ENDOWMENT }},
of which you contributed {{ player.contribution }}.
Your group contributed {{ group.total_contribution }},
resulting in an individual share of {{ group.individual_share }}.
Your profit is therefore {{ player.payoff }}.
</p>
{{ next_button }}
セッション構成の定義¶
settings.py の SESSION_CONFIGS
に 新しいセッションを追加し、 app_sequence
にアプリ名 my_public_goods
をリストとして渡します。
プログラムの実行¶
oTreeを(再)起動し、ブラウザを開いて http://localhost:8000
に接続してください。
print()を利用したトラブルシューティング¶
プログラミングフォーラムで、「プログラムが機能していません。コードを何時間も見ても、間違いを見つけることができません」などのメッセージがしばしば投稿されます。
エラーが見つかるまでコードをじっくり目で追っていてはいけません。プログラムをインタラクティブにテストすることが肝心です。
最も簡単な方法は、 print()
を使用することです。このテクニックを学ばないと、ゲームを効果的にプログラムすることができません。
たとえば、利得を計算する関数 set_payoffs
の中で、次のような print
文を挿入してみましょう。
print('group.total_contribution is', group.total_contribution)
機能していないコードの部分にこの print
文を配置します。ブラウザでゲームをプレイし、そのコードが実行されると、 print
コマンドによる出力がコンソールログに表示されます(Webブラウザでは表示されません)。
print
文はいくらでも挿入することができます。
print('in payoff function')
contributions = [p.contribution for p in players]
print('contributions:', contributions)
group.total_contribution = sum(contributions)
group.individual_share = group.total_contribution * C.MULTIPLIER / C.PLAYERS_PER_GROUP
print('individual share', group.individual_share)
if group.individual_share > 100:
print('inside if statement')
for player in players:
player.payoff = C.ENDOWMENT - p.contribution + group.individual_share
print('payoff after', p.payoff)
printによる出力がコンソールログに表示されない¶
コンソールログに print
の出力が表示されない場合は、その行が実行されていないことを意味します。その箇所に、プログラムが機能しない理由があります。
コードが return
の後などの到達できない場所にあるか、あるいは常に False
である if
文の中にあるなどの原因が考えられます。関数の先頭から print
文を配置していくことで、問題の箇所を特定できます。
または、コードが呼び出されることのない関数に含まれている可能性があります。 creating_session
や before_next_page
のようなoTreeに組み込まれている関数は自動的に実行されますが、 set_payoffs
のような独自に定義した関数は、その関数を明示的に呼び出さなければならないことを覚えておく必要があります。