ボット: 高度な機能

これらの機能の多くはは oTree スタジオにおいて、サポートされていません。

コマンドラインボット

Webブラウザーでボットを実行する代わりに、コマンドラインでボットを実行することもできます。コマンドラインボットはより高速に実行され、セットアップも少なくて済みます。

これを実行します。

otree test mysession

特定の数の参加者でテストするには(それ以外の場合はデフォルトで num_demo_participants ):

otree test mysession 6

すべてのセッション構成のテストを実行するには:

otree test

データのエクスポート

--export フラグを使用して、結果をCSVファイルにエクスポートします。

otree test mysession --export

データが保存されるフォルダを指定するには、次の手順を実行します。

otree test mysession --export=myfolder

コマンドラインブラウザボット

otree browser_bots を使用して、コマンドラインからブラウザボットを起動できます。

  • Google Chromeがインストールされていることを確認するか、 BROWSER_COMMANDsettings.py で設定してください。(下記詳細)。

  • REST の説明に従って、環境変数 OTREE_REST_KEY を設定します。

  • サーバを実行する。

  • すべてのChromeウィンドウを閉じます。

  • これを実行します。

    otree browser_bots mysession
    

これにより、いくつかのChromeタブが起動し、ボットが実行されます。終了すると、タブが閉じ、ターミナルにレポートが表示されます。

Chromeでウィンドウが正しく閉じられない場合は、コマンドを起動する前に、必ずすべてのChromeウィンドウを閉じてください。

リモートサーバー(Herokuなど)上のコマンドラインブラウザボット

サーバーが通常の http://localhost:8000 以外のホスト/ポートで実行されている場合は、 --server-url を渡す必要があります。例えば、Herokuにある場合は、次のようにします。

otree browser_bots mysession --server-url=https://YOUR-SITE.herokuapp.com

セッションの構成とサイズの選択

参加者の数を指定できます。

otree browser_bots mysession 6

次のコマンドを実行することですべてのセッション構成をテストすることができます。

otree browser_bots

ブラウザボット: その他の注意

settings.pyBROWSER_COMMAND を設定することで 、Chrome以外のブラウザを使用できます。そして、oTreeは subprocess.Popen(settings.BROWSER_COMMAND) のようなコマンドを実行してブラウザを開きます。

テストケース

さまざまなテストケースのリストである、属性 cases をPlayerBotクラスに定義できます。たとえば、公共財ゲームでは、次の3つのシナリオをテストすることができます。

  • すべてのプレイヤーが財産の半分を寄付するケース
  • すべてのプレイヤーが何も寄付しないケース
  • すべてのプレイヤーが全財産(100ポイント)を寄付するケース

これらの3つのテストケースをそれぞれ "basic" 、 "min" 、 "max" と呼び、 cases に格納します。次に、oTreeは各テストケースに対して1回ずつ、計3回ボットを実行します。毎回 、 cases から様々な値がボットの self.case に割り当てられます。

例:

class PlayerBot(Bot):

    cases = ['basic', 'min', 'max']

    def play_round(self):
        yield (pages.Introduction)

        if self.case == 'basic':
            assert self.player.payoff == None

        if self.case == 'basic':
            if self.player.id_in_group == 1:
                for invalid_contribution in [-1, 101]:
                    yield SubmissionMustFail(pages.Contribute, {'contribution': invalid_contribution})
        contribution = {
            'min': 0,
            'max': 100,
            'basic': 50,
        }[self.case]

        yield (pages.Contribute, {"contribution": contribution})
        yield (pages.Results)

        if self.player.id_in_group == 1:

            if self.case == 'min':
                expected_payoff = 110
            elif self.case == 'max':
                expected_payoff = 190
            else:
                expected_payoff = 150
            assert self.player.payoff == expected_payoff

注釈

ユースケースを使用する場合、ブラウザボットは単一のケースしか実行しないため、 コマンドラインボット を使用することをおすすめします。

cases はリストである必要がありますが、文字列、整数、さらには辞書など、任意のデータ型を格納することができます。下記のコードは、辞書をケースとして使用する、 trust ゲームのためのボットです。

class PlayerBot(Bot):

    cases = [
        {'offer': 0, 'return': 0, 'p1_payoff': 10, 'p2_payoff': 0},
        {'offer': 5, 'return': 10, 'p1_payoff': 15, 'p2_payoff': 5},
        {'offer': 10, 'return': 30, 'p1_payoff': 30, 'p2_payoff': 0}
    ]

    def play_round(self):
        case = self.case
        if self.player.id_in_group == 1:
            yield (pages.Send, {"sent_amount": case['offer']})

        else:
            for invalid_return in [-1, case['offer'] * C.MULTIPLICATION_FACTOR + 1]:
                yield SubmissionMustFail(pages.SendBack, {'sent_back_amount': invalid_return})
            yield (pages.SendBack, {'sent_back_amount': case['return']})

        yield (pages.Results)


        if self.player.id_in_group == 1:
            expected_payoff = case['p1_payoff']
        else:
            expected_payoff = case['p2_payoff']

        assert self.player.payoff == expected_payoff

error_fields

複数のフィールドを持つフォームで SubmissionMustFail を使用する場合は、 error_fields を使用することができます。

例えば、 age で有効な送信がなされ、 weightheight が無効な送信であったとします。

yield SubmissionMustFail(
    pages.Survey,
    dict(
        age=20,
        weight=-1,
        height=-1,
    )
)

ボットシステムは送信が失敗する理由を正確に教えてくれません。weightheight のどちらが無効なのか、またはその両方が無効であるのか、 error_fields はそのあいまいさを解決できます:

yield SubmissionMustFail(
    pages.Survey,
    dict(
        age=20,
        weight=-1,
        height=-1,
    ),
    error_fields=['weight', 'height']
)

これにより`` weight`` と height にエラーが含まれていることが確認されますが、 age は含まれていません。

error_message がエラーを返す場合、 error_fields['__all__'] になります。

その他の注意

ボットでは、その種のコードが他の場所で推奨されている場合でも、player = self.player (または、 participant = self.participant 等)を割り当てるのは危険です

yield の間にある場合、データが古くなる可能性があるためです。

player = self.player
expect(player.money_left, cu(10))
yield pages.Contribute, dict(contribution=cu(1))
# don't do this!
# "player" variable still has the data from BEFORE pages.Contribute was submitted.
expect(player.money_left, cu(9))

実行中の self.player がデータベースから最新のデータを取得するため、 self.player.money_left を直接使用する方が安全です。

ライブページ

ボットでライブページをテストするために、 tests.py で最上位関数として call_live_method を定義します。(oTree Studioでは使用できません。)この関数は live_method への呼び出しシーケンスをシミュレートします。引数 method は、Playerクラスのライブメソッドをシミュレートします。例えば、 method(3, 'hello') プレイヤー3のliveメソッドを呼び出し、data'hello' を設定します。例:

def call_live_method(method, **kwargs):
    method(1, {"offer": 50})
    method(2, {"accepted": False})
    method(1, {"offer": 60})
    retval = method(2, {"accepted": True})
    # you can do asserts on retval

kwargs には少なくとも以下のパラメータが含まれます。

  • case テストケース を参照。
  • page_class : 現在のページクラス。例えば、 pages.MyPage
  • round_number

call_live_method はグループ内で最速のボットが live_method を持つページに遷移すると自動的に実行されます。(WaitPage でこれを制限しない限り、他のボットはその時点で前のページにある可能性があります。)