Wait page¶
WaitPageは、1人のプレイヤーが先に進む前に、他のプレイヤーの実行を待つ必要がある場合に必要です。たとえば、最後通牒ゲームでは、プレイヤー2は、プレイヤー1のオファーを見る前に受け入れたり拒否したりすることはできません。
ページのシーケンスに WaitPage
が含まれている場合、oTreeは、グループ内のすべてのプレイヤーが WaitPage
に到着するまで待機します。
サブセッションで複数のグループが同時に作業をしており、すべてのグループ(サブセッション内のすべてのプレイヤー)を待機させるWaitPageが必要な場合は、wait_for_all_groups = True
にする必要があります。
グループの詳細については、 グループ を参照してください。
after_all_players_arrive¶
after_all_players_arrive
はすべてのプレイヤーがWaitPageに到着したときに実行される関数です。プレイヤーの利得の設定や、勝者を決定したりするのに適した関数です。最初に、必要な計算を行うグループ関数を定義します。例えば:
def set_payoffs(group):
for p in group.get_players():
p.payoff = 100
そして、次のようにしてこの関数を呼び出します。
class MyWaitPage(WaitPage):
after_all_players_arrive = set_payoffs
wait_for_all_groups = True
に設定した場合は、 after_all_players_arrive
は、サブセッションの関数である必要があります。
テキストエディタを使用している場合、 after_all_players_arrive
は、WaitPageで直接定義することもできます。:
class MyWaitPage(WaitPage):
@staticmethod
def after_all_players_arrive(group: Group):
for p in group.get_players():
p.payoff = 100
文字列で指定することもできます。
class MyWaitPage(WaitPage):
after_all_players_arrive = 'set_payoffs'
is_displayed()¶
通常のページと同じように機能します。
group_by_arrival_time¶
WaitPageで group_by_arrival_time = True
に設定すると、プレイヤーはWaitPageに到着した順序でグループ化されます。
class MyWaitPage(WaitPage):
group_by_arrival_time = True
たとえば、 PLAYERS_PER_GROUP = 2
の場合、待機ページに到着した最初の2人のプレイヤーがグループ化され、次に到着した2人のプレイヤーが別のグループに編成されます。
これは、一部の参加者が脱落する可能性のあるセッション(たとえば、オンライン実験、または参加者を早期に終了させる同意ページを使用した実験)、一部の参加者が他の参加者よりもはるかに時間がかかるセッションで役立ちます。
group_by_arrival_time
の一般的な使用方法は、参加者を除外するアプリの後に配置することです。たとえば、実験に参加するかどうかの同意ページがセッションにある場合、同意ページのみを含む "consent" アプリを作成し、 ['consent', 'my_game']
ような app_sequence
を作成します。なお、my_game
は group_by_arrival_time
利用します。これは、誰かが同意しなかった場合、 my_game
のグループ化から除外されることを意味します。
ゲームに複数のラウンドがある場合は、ラウンド1の到着時間のみでグループ化することをお勧めします。
class MyWaitPage(WaitPage):
group_by_arrival_time = True
@staticmethod
def is_displayed(player):
return player.round_number == 1
これを行うと、後続のラウンドはラウンド1と同じグループ構造を維持します。それ以外の場合、プレイヤーは各ラウンドの到着時間によって再びグループ化されます。( group_by_arrival_time
は、グループ構造を将来のラウンドにコピーします。)
注意
- If a participant arrives at the wait page but subsequently switches to a different window or browser tab, they will be excluded from grouping after a short period of time.
id_in_group
は、プレイヤーがページに到着した順序で必ずしも割り当てられるとは限りません。group_by_arrival_time``は、 ``page_sequence
において、WaitPageが 最初のページである場合にのみ使用できますgroup_by_arrival_time
を持つページでis_displayed
を使う場合、ラウンド数のみに基づく必要があります。一部のプレイヤーにだけページを表示するために使用しないでください。group_by_arrival_time = True
の場合、すべてのプレイヤーが最初は同じグループに属します。グループは、プレイヤーがWaitPageに到着したときに "on the fly" で作成されます。
プレイヤーをグループに配置することをさらに制御する必要がある場合は、 group_by_arrival_time_method() を使用します。
group_by_arrival_time_method()¶
group_by_arrival_time
を使用していて、どのプレイヤーを一緒に割り当てるかをより細かく制御したい場合は、 group_by_arrival_time_method()
を使用することもできます。
到着時間によるグループ化に加えて、各グループが2人の男性と2人の女性で構成される必要があるとします。
group_by_arrival_time_method
と呼ばれる関数を定義すると、新しいプレイヤーがWaitPageに到達するたびに呼び出されます。関数の2番目の引数は、WaitPageで現在待機しているプレイヤーのリストです。これらのプレイヤーの一部を選択してリストとして返すと、それらのプレイヤーはグループに割り当てられ、先に進みます。何も返さない場合、グループ化のための処理は発生しません。
これは、各グループに2人の男性と2人の女性がいる例です。各参加者に participant.category
が割り当てられていることを前提としています。
# note: this function goes at the module level, not inside the WaitPage.
def group_by_arrival_time_method(subsession, waiting_players):
print('in group_by_arrival_time_method')
m_players = [p for p in waiting_players if p.participant.category == 'M']
f_players = [p for p in waiting_players if p.participant.category == 'F']
if len(m_players) >= 2 and len(f_players) >= 2:
print('about to create a group')
return [m_players[0], m_players[1], f_players[0], f_players[1]]
print('not enough players yet to create a group')
WaitPageのタイムアウト¶
WaitPageにタイムアウトを設定するために group_by_arrival_time_method
を使用することもできます。たとえば、参加者が5分以上待機している場合に、個別に続行できるようにすることができます。まず、アプリの前の最後のページの group_by_arrival_time
内で time.time()
を利用して時間を記録しておき、それを participant field に保管してください。
次に、Player関数を定義します。
def waiting_too_long(player):
participant = player.participant
import time
# assumes you set wait_page_arrival in PARTICIPANT_FIELDS.
return time.time() - participant.wait_page_arrival > 5*60
そして、これを使用してください:
def group_by_arrival_time_method(subsession, waiting_players):
if len(waiting_players) >= 3:
return waiting_players[:3]
for player in waiting_players:
if waiting_too_long(player):
# make a single-player group.
return [player]
これが機能するのは、WaitPageが1分に1〜2回自動的に更新され、 group_by_arrival_time_method
が再実行されるためです。
プレイヤーがWaitPageで待機し続けるのを防ぐ¶
特にオンライン実験でよくある問題は、グループ内の別のプレイヤーが脱落したり、遅すぎたりするのを待っているプレイヤーが待機し続けることになることです。
この問題を軽減するためにできることがいくつかあります。
group_by_arrival_time
の利用¶
上記のように、同じ時間にアクティブにプレイしているプレイヤーだけがグループ化されるように group_by_arrival_time
を使用できます。
group_by_arrival_time
は、 "lock-in" タスクの後に使用するとうまく機能します。つまり、マルチプレイヤーゲームの前に、単体のプレイヤーによるタスクを実行できます。参加者はこの最初のタスクを完了するために作業を行うため、その時点以降に脱落する可能性は低くなります。
ページタイムアウトの利用¶
各ページで timeout_seconds を使用して、プレイヤーが遅れているか非アクティブの場合に、ページが自動的に進むようにします。または、管理画面の "Advance slowest participants" ボタンをクリックして、手動でタイムアウトを強制することもできます。
timeout_happenedの確認¶
timeout_seconds
の前にそのページでの作業を完了する必要があり、そうしないと脱落してしまうことをユーザーに伝えるという方法もあります。また、 "次のボタンをクリックして、まだプレイしていることを確認してください" というページを作ることも考えられます。そして、 timeout_happened がTrueの場合、そのプレイヤー/グループに脱落を示すフィールドを設定したり、ラウンドの残りのページをスキップしたりするなど、さまざまな操作を実行できます。
脱落したプレイヤーをボットに置き換える¶
上記のテクニックのいくつかを組み合わせ、プレイヤーが脱落した場合でも、ボットのように自動再生が継続されるような処理の例を次に示します。まず、 is_dropout
と呼ばれる participant field を定義し、 creating_session
で、その初期値を False
に設定します。次に、すべてのページで get_timeout_seconds
と before_next_page
を使用します。:
class Page1(Page):
form_model = 'player'
form_fields = ['contribution']
@staticmethod
def get_timeout_seconds(player):
participant = player.participant
if participant.is_dropout:
return 1 # instant timeout, 1 second
else:
return 5*60
@staticmethod
def before_next_page(player, timeout_happened):
participant = player.participant
if timeout_happened:
player.contribution = cu(100)
participant.is_dropout = True
注意
- プレイヤーが時間どおりにページを送信できない場合は、
is_dropout
をTrue
に設定します。 - 一度
is_dropout
が設定されると、各ページは、自動的に送信するようになります。 - ページが自動送信される場合、
timeout_happened
は、ユーザーに代わって送信される値を決定するために使用します。
WaitPageの外観のカスタマイズ¶
title_text
および body_text
属性を設定することにより、WaitPageに表示されるテキストをカスタマイズできます。例:
class MyWaitPage(WaitPage):
title_text = "Custom title text"
body_text = "Custom body text"
カスタム wait page テンプレート を参照してください。