第二部分:公共品博弈¶
(一个视频教程在这里 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 = cu(1000)
MULTIPLIER = 2
模型¶
在游戏结束之后,我们需要关于玩家的何种数据?我们需要记录每位玩家的贡献值。故在玩家模型中定义 contribution
字段:
class Player(BasePlayer):
contribution = models.CurrencyField(
min=0,
max=C.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_model
与 form_fields
。具体来说,这一表单让你能够设定玩家的 contribution
字段。(查看 表单 以获得更多信息。)
class Contribute(Page):
form_model = 'player'
form_fields = ['contribution']
现在我们来创建HTML模板。
设置 title
为 Contribute
, content
如下:
<p>
This is a public goods game with
{{ C.PLAYERS_PER_GROUP }} players per group,
an endowment of {{ C.ENDOWMENT }},
and a multiplier of {{ C.MULTIPLIER }}.
</p>
{{ formfields }}
{{ next_button }}
页面2:ResultsWaitPage¶
当所有玩家都完成了 Contribute
页面,玩家的收益就会被计算。添加一个group函数,命名为 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 * C.MULTIPLIER / C.PLAYERS_PER_GROUP
for player in players:
player.payoff = C.ENDOWMENT - player.contribution + group.individual_share
当玩家选择了贡献值后,并不能立刻看到结果页面;他们首先需要等待其他玩家选择贡献值。故你需要添加一个 WaitPage
。不妨将其命名为 ResultsWaitPage
。当一位玩家到达等待页面之后,他必须等待组内其他玩家到达。之后所有人才能继续前往下一页面。(查看 等待页面 以获得更多信息)。
在 ResultsWaitPage
中添加 after_all_players_arrive
字段,将其设置如下以触发函数 set_payoffs
:
after_all_players_arrive = 'set_payoffs'
页面3:Results¶
现在来创建 Results
页面。将模板内容设置如下:
<p>
You started with an endowment of {{ C.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 }}
定义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 * C.MULTIPLIER / C.PLAYERS_PER_GROUP
print('individual share', group.individual_share)
if group.individual_share > 100:
print('inside if statement')
for player in players:
player.payoff = C.ENDOWMENT - p.contribution + group.individual_share
print('payoff after', p.payoff)
print语句没有显示在控制台窗口/日志中¶
如果你在控制台窗口中没有看到print语句的输出,这意味着这行代码没有被执行!(也就是程序没有正常工作的原因)
可能这是因为你的代码处于不可达的位置,如 return
语句的后面,或在一个总是返回 False
的”if“语句中。尝试将一系列print语句从函数的开头开始向下放置,然后查看它们在何处停止了显示。
或者也可能你的代码在一个从未被调用(执行)的函数中。oTree的内置函数如 creating_session
与 before_next_page
会被自动执行,但是如果你定义了一个自定义方法如 set_payoffs
,你必须记得在某个内置方法中调用它。