第三部分:信任博弈¶
现在我们来创建一个2人 信任博弈,并学习oTree的一些特性。
开始时,玩家1获得10点数;玩家2无点数。玩家1可以将他点数的一部分或者全部给与玩家2。之后玩家2收到的点数会变为原来的3倍。当玩家2收到3倍的点数之后,他可以决定将部分或者全部的点数给予玩家1。
The completed app is here.
创建应用¶
正如教程的前一部分,创建另一个应用,命名为 my_trust
。
常量¶
转到本应用的常量。
首先我们定义应用的常量。初始点数为10,并且捐赠会变为3倍。
class C(BaseConstants):
NAME_IN_URL = 'my_trust'
PLAYERS_PER_GROUP = 2
NUM_ROUNDS = 1
ENDOWMENT = cu(10)
MULTIPLICATION_FACTOR = 3
模型¶
现在我们添加player与group的字段。这里有两种关键的数据需要记录:玩家1的”捐赠“值与玩家2的“返还”值。
直觉上,你可能如下定义Player类中的字段:
# Don't copy paste this
class Player(BasePlayer):
sent_amount = models.CurrencyField()
sent_back_amount = models.CurrencyField()
此模型的问题在于, sent_amount
只对玩家1有意义, sent_back_amount
只对玩家2有意义。所以 sent_back_amount
字段对于玩家1无意义。我们如何才能使得模型更加精确呢?
我们可以将这些字段定义到 Group
层级。这非常合理,因为每一小组恰好有一个 sent_amount
与一个 sent_back_amount
:
class Group(BaseGroup):
sent_amount = models.CurrencyField(
label="How much do you want to send to participant B?"
)
sent_back_amount = models.CurrencyField(
label="How much do you want to send back?"
)
我们定义一个名为 sent_back_amount_choices
的函数为下拉菜单动态地添加数据。这一特性称为 {field_name}_choices
,在这里: 动态表单验证 被详细解释。
def sent_back_amount_choices(group):
return currency_range(
0,
group.sent_amount * C.MULTIPLICATION_FACTOR,
1
)
定义模板与页面¶
我们需要3个页面:
- 玩家1的“Send”页面
- 玩家2的“Send back”页面
- 两位玩家都能看到的“Results”页面。
Send页面¶
class Send(Page):
form_model = 'group'
form_fields = ['sent_amount']
@staticmethod
def is_displayed(player):
return player.id_in_group == 1
我们使用 is_displayed() 让此页面仅显示给玩家1;玩家2跳过此页面。获取更多 id_in_group
的信息,可查看 小组。
将模板的 title
设为 Trust Game: Your Choice
,并且将 content
设置如下:
<p>
You are Participant A. Now you have {{C.ENDOWMENT}}.
</p>
{{ formfields }}
{{ next_button }}
SendBack.html¶
这是玩家2需要查看的将钱返还的页面。将 title
设为 Trust Game: Your Choice
,并将 content
设置如下:
<p>
You are Participant B. Participant A sent you {{group.sent_amount}}
and you received {{tripled_amount}}.
</p>
{{ formfields }}
{{ next_button }}
下面是页面的代码。请注意:
- 我们使用 vars_for_template() 将变量
tripled_amount
传递给模板。你不能直接在HTML代码中做计算,所以这一数字需要使用Python代码计算并传给模板。
class SendBack(Page):
form_model = 'group'
form_fields = ['sent_back_amount']
@staticmethod
def is_displayed(player):
return player.id_in_group == 2
@staticmethod
def vars_for_template(player):
group = player.group
return dict(
tripled_amount=group.sent_amount * C.MULTIPLICATION_FACTOR
)
Results¶
玩家1与玩家2所看到的结果页面有细微的不同。所以我们使用 {{ if }}
语句来判断当前玩家的 id_in_group
。将 title
设置为 Results
,并将内容部分设置如下:
{{ if player.id_in_group == 1 }}
<p>
You sent Participant B {{ group.sent_amount }}.
Participant B returned {{ group.sent_back_amount }}.
</p>
{{ else }}
<p>
Participant A sent you {{ group.sent_amount }}.
You returned {{ group.sent_back_amount }}.
</p>
{{ endif }}
<p>
Therefore, your total payoff is {{ player.payoff }}.
</p>
class Results(Page):
pass
等待页面与页面序列¶
添加2个等待页面:
WaitForP1
(玩家2需要等待玩家1决定捐赠值)ResultsWaitPage
(玩家1需要等待玩家2决定返还值)
在第二个等待页面之后,我们应当计算收益。所以我们定义一个函数,命名为 set_payoffs
:
def set_payoffs(group):
p1 = group.get_player_by_id(1)
p2 = group.get_player_by_id(2)
p1.payoff = C.ENDOWMENT - group.sent_amount + group.sent_back_amount
p2.payoff = group.sent_amount * C.MULTIPLICATION_FACTOR - group.sent_back_amount
在 ResultsWaitPage
中,设置 after_all_players_arrive
:
after_all_players_arrive = set_payoffs
确保在page_sequence中页面顺序是正确的:
page_sequence = [
Send,
WaitForP1,
SendBack,
ResultsWaitPage,
Results,
]
在 SESSION_CONFIGS
中增加一个条目¶
在应用序列中创建一个属于 my_trust
的session config。
运行服务器¶
载入你的项目并打开浏览器,网址为 http://localhost:8000
。