第三部分:信任博弈¶
现在我们来创建一个2人 信任博弈, 并学习oTree的一些特性。
开始时,玩家1获得10点数;玩家2无点数。玩家1可以将他点数的一部分或者全部给与玩家2。之后玩家2收到的点数会变为原来的3倍。当玩家2收到3倍的点数之后,他可以决定将部分或者全部的点数给予玩家1.
应用完整的代码在 这里.
创建应用¶
正如教程的前一部分,创建另一个应用,命名为 my_trust
.
常量¶
转到本应用的常量。
首先我们定义应用的常量。初始点数为10,并且捐赠会变为3倍。
class Constants(BaseConstants):
name_in_url = 'my_trust'
players_per_group = 2
num_rounds = 1
endowment = c(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?"
)
We also define a function called sent_back_amount_choices
to populate the
dropdown menu dynamically. This is the feature called
{field_name}_choices
, which is explained here: 动态表单验证.
def sent_back_amount_choices(group):
return currency_range(
c(0),
group.sent_amount * Constants.multiplication_factor,
c(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 {{Constants.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 * Constants.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决定返还值)
在第二个等待页面之后,我们应当计算收益。所以我们在Group类中定义一个方法,命名为 set_payoffs
:
def set_payoffs(group):
p1 = group.get_player_by_id(1)
p2 = group.get_player_by_id(2)
p1.payoff = Constants.endowment - group.sent_amount + group.sent_back_amount
p2.payoff = group.sent_amount * Constants.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
.