第二部分:公共品博弈

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

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

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

The full code for the app we will write is here.

创建应用

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

常量

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

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

我们就有了下面的常量:

players_per_group = 3
num_rounds = 1
endowment = cu(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)

Put this line in the part of your code that’s not working, such as the payoff function defined above. When you play the game in your browser and that code gets executed, your print statement will be displayed in your command prompt window (not in your web browser).

你可以在代码中添加很多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)