第二部分:公共品博弈

(一个视频教程在这里 YouTube )

我们现在创建一个简单的 公共品博弈. 公共品博弈是经济学中的经典博弈。

这是一个三人游戏,每位玩家初始获得100点数。每位玩家独立决定为团队贡献出多少自己的点数。团队总贡献值将被乘以2并以均分的方式重新分配给三位玩家。

本应用的全部代码在 这里.

创建应用

正如教程的前一部分,创建另一个应用,起名为 my_public_goods.

常量

设置本应用的常量(查看 常量 可获得更多信息)。

  • 设置 players_per_group 为3.oTree会自动将玩家分为3组。
  • 每位玩家初始有1000点数。故定义 endowment 并将其设定为1000货币值。
  • 团队贡献值将被乘以2.故定义一个整数常量 multiplier = 2:

我们就有了下面的常量:

players_per_group = 3
num_rounds = 1
endowment = c(1000)
multiplier = 2

在游戏结束之后,我们需要关于玩家的何种数据?我们需要记录每位玩家的贡献值。故在玩家模型中定义 contribution 字段:

class Player(BasePlayer):
    contribution = models.CurrencyField(
        min=0,
        max=Constants.endowment,
        label="How much will you contribute?"
    )

我们还需要记录玩家在游戏结束时的收益,不过我们无需显式地定义一个 payoff 字段,因为在oTree中,Player类已经包含了一个 payoff 字段。

我们对每一组的何种数据感兴趣?我们可能会对组内总贡献值感兴趣,以及返还给每位玩家的份额。故我们在Group类中定义下面两个字段:

class Group(BaseGroup):
    total_contribution = models.CurrencyField()
    individual_share = models.CurrencyField()

页面

本游戏包含了3个页面:

  • 页面1:玩家决定贡献值
  • 页面2:等待页面:玩家等待组内其他玩家做选择
  • 页面3:玩家被告知结果

页面1:Contribute

首先我们来定义 Contribute.这一页面包含一个表单,所以我们需要定义 form_modelform_fields.具体来说,这一表单让你能够设定玩家的 contribution 字段。(查看 表单 以获得更多信息)

class Contribute(Page):

    form_model = 'player'
    form_fields = ['contribution']

现在我们来创建HTML模板。

设置 titleContributecontent 如下:

<p>
    This is a public goods game with
    {{ Constants.players_per_group }} players per group,
    an endowment of {{ Constants.endowment }},
    and a multiplier of {{ Constants.multiplier }}.
</p>

{% formfields %}

{% next_button %}

页面2:ResultsWaitPage

When all players have completed the Contribute page, the players’ payoffs can be calculated. Add a group function called 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 * Constants.multiplier / Constants.players_per_group
    for player in players:
        player.payoff = Constants.endowment - player.contribution + group.individual_share

当玩家选择了贡献值后,并不能立刻看到结果页面;他们首先需要等待其他玩家选择贡献值。故你需要添加一个 WaitPage.不妨将其命名为 ResultsWaitPage.当一位玩家到达等待页面之后,他必须等待组内其他玩家到达。之后所有人才能继续前往下一页面。(查看 等待页面 以获得更多信息)。

Add after_all_players_arrive to ResultsWaitPage, and set it to trigger set_payoffs:

after_all_players_arrive = 'set_payoffs'

页面3:Results

现在来创建 Results 页面。将模板内容设置如下:

<p>
    You started with an endowment of {{ Constants.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是正确的:

page_sequence = [
    Contribute,
    ResultsWaitPage,
    Results
]

定义session config

我们在应用序列中添加另一个属于 my_public_goods 的session config。

运行代码

载入你的项目并打开浏览器,网址为 http://localhost:8000.

使用print()解决问题

我经常在编程论坛上看到下面这样的帖子,”我的程序不能正常运行. 我找不到错误在哪, 尽管我已经花了数小时看我的代码”.

解决方案并不是重读你的代码直到你发现错误为止,而是交互式地 测试 你的程序。

最简单地方法就是利用 print() 语句。如果你没有学会这个技巧,那么你将不能高效地编程。

你只需要像下面这样在你的代码中插入一行:

print('group.total_contribution is', group.total_contribution)

将这一行代码插入到你代码无法正常工作的部分,如上面定义的payoff方法。当你在浏览器中进行游戏时,这行代码就会执行,你的print语句将会显示在命令行窗口中(不是你的浏览器中)

你可以在代码中添加很多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 * Constants.multiplier / Constants.players_per_group
print('individual share', group.individual_share)
if group.individual_share > 100:
    print('inside if statement')
    for player in players:
        player.payoff = Constants.endowment - p.contribution + group.individual_share
        print('payoff after', p.payoff)