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_modelform_fields を定義する必要があります。貢献額 contribution はプレイヤーモデルのフィールドなので、 form_modelplayer と設定します。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 }}

ページの順番の定義

表示するページの順番を以下のようにして設定します。

page_sequence = [
    Contribute,
    ResultsWaitPage,
    Results
]

セッション構成の定義

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)