下面介绍的是公共品博弈的编写例子,一些已在前面的例子中介绍的内容不再赘述;新建一个公共品博弈的app文件夹,在公共品博弈中,有两个全局层面的常数,一个是参加者的禀赋,另一个是投入禀赋之后的放大倍数,这种常数可以在C类中定义:

class C(BaseConstants):
    NAME_IN_URL = 'publicgood'
    PLAYERS_PER_GROUP = 3
    NUM_ROUNDS = 5

    ENDOWMENT = cu(200)
    MULTIPLIER = 2

这里的禀赋的写法表示这个数值是货币型的数值,如果设置了USE_POINTS为True,则表示200点(关于点数和收益的问题,参考(五)中的处理点数和收益以及官方文档Currency一节的内容),MULTIPLIER设置为2

NAME_IN_URL的字符会出现在进入实验后浏览器显示的网址中,PLAYERS_PER_GROUP设定了小组人数为3(若无小组,则设定为None),NUM_ROUNDS设定了轮次为5

注意python里面所有的常数的名字应该全部用大写字母

公共品博弈的决策是个体决定自己的贡献额,而计算公共品投资回报则需要在小组层面进行计算,因此需要在Group类下定义小组总贡献额和回报,在Player类下定义贡献额

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


class Player(BasePlayer):
    contribution = models.CurrencyField(
        min = 0,
        max = C.ENDOWMENT,
        label = "请输入您的贡献额"
    )

如果在Player类下定义总贡献额或回报也是可以的,这个时候就需要想办法调用同组内其他人的贡献额数据进行计算(可以自行尝试这种写法,参考官方文档Multiplayers games一节下面Group小节的内容,需要用到get_others_in_group()这个内置方法),写在Group类中调用更方便

到这里就定义完成所需要的字段了,在这一部分还剩余两个问题:①如何分组?②如何计算收益?

关于分组,这里设定5轮都是随机匹配,需要用到内置的随机分组方法:

def creating_session(subsession):
    subsession.group_randomly()

这里定义的creating_session是一个函数,不需要定义在某个类下面。def creating_session(subsession)是一个内置的函数,用于在开始实验前设定分组、角色等内容,名字写法固定。在启动实验时,名为creating_session的函数会被自动调用执行

subsession.group_randomly()是每轮随机分组的内置方法(关于分组和角色分配,参考(五)的“group和role的设定”相关内容)

关于计算收益,需要自行设定收益计算函数并调用(单独写收益计算函数可以提高代码可读性,也便于修改和查找问题)

def set_payoffs(group:Group):
    player_list = group.get_players() #获得包含小组内所有玩家的列表
    contribution_list = [p.contribution for p in player_list] #获得小组内所有贡献额列表
    group.total_contribution = sum(contribution_list) #求得贡献额总和
    group.individual_share = group.total_contribution * C.MULTIPLIER / C.PLAYERS_PER_GROUP
    for p in player_list: #对小组内的每一个玩家,减去自己的贡献额后加上分得的收益
        p.payoff = C.ENDOWMENT - p.contribution + group.individual_share

由于小组内的三人收益是共同决定的,因此从小组层面进行计算

(group:Group)表示的意思是:group是函数需要的参数的标记,Group是这个参数建议取的值为Group类,这样写是提醒作用,防止在后面调用的时候出错

下面是PAGES部分的内容,这一任务分为两个页面,第一个页面是输入贡献额,第二个页面是报告收益,由于输入贡献额的速度有快慢,因此需要添加等待页面等待小组内或所有人完成决策后再进入收益报告。

贡献决策页面如下:

class Contribution(Page):
    form_model = 'player'
    form_fields = ['contribution']

对应的html文件如下:

{{ block title }}
    第{{ player.round_number }}轮决策
{{ endblock }}

{{ block content }}

    <p>
        <ul>
            <li>在这个实验中你和另外两人随机配对,组内共有{{ C.PLAYERS_PER_GROUP }}人。</li>
            <li>每人都有实验点{{ C.ENDOWMENT }}。</li>
            <li>投入公共池中的实验点会乘以{{ C.MULTIPLIER }}。</li>
        </ul>
    </p>
    {{ formfields }}
    {{ next_button }}
{{ endblock }}

显示如下:

完成就决策后,需要一个WaitPage让参加者等待。WaitPage不需要在文件夹下写对应的html文件,只需要在py文件里设定即可。这里的等待有两种方式,第一种是等待同组三人都完成进入等待界面后,等待结束,进入下一页收益报告,第二种写法是等待在场所有人都完成后才一起进入下一页,下面给出两种写法:

class ResultsWaitPage(WaitPage):
    title_text = '请耐心等待!'
    body_text = '请保持安静,如果有问题,请询问实验员。'
    @staticmethod
    def after_all_players_arrive(group: Group):
        set_payoffs(group)
    
    #wait_for_all_groups = True
    #@staticmethod
    #def after_all_players_arrive(subsession: Subsession):
    #    group_list = subsession.get_groups()
    #    for g in group_list:
    #        set_payoffs(g)

title_textbody_text设定了等待页面显示的标题和文本内容。

after_all_players_arrive是一个内置的方法,表示等待所有人(group或subsession)到达这一页面后才执行,第一种方法是等待同组所有人都到达之后才执行,因此传入的参数是group,直接调用前面定义的收益计算函数即可。

第二种方法(注释掉的方法)中首先设定了wait_for_all_groups = True,表示要等所有小组都到达了才执行,这个时候after_all_players_arrive必须是一个subsession的方法,传入subsession参数,然后再对subsession里面的小组逐一执行收益计算函数。

等待页面显示如下:

最后的收益报告不需要在py文件里写代码,html如下:

{{ block title }}
    第{{ player.round_number }}轮收益报告
{{ endblock }}

{{ block content }}

    <p>
        <ul>
            <li>本轮你投入了{{ player.contribution }}。</li>
            <li>小组总的贡献额是{{ group.total_contribution }}。</li>
            <li>组内每人可分得{{ group.individual_share }}。</li>
            <li>你的本轮收益是{{ player.payoff }}。</li>
        </ul>
    </p>
    {{ if player.round_number == C.NUM_ROUNDS }}
    <p>
        你的{{ C.NUM_ROUNDS }}轮总收益是{{ participant.payoff }}。
    </p>
    {{ endif }}

    {{ next_button }}

{{ endblock }}

唯一需要说明的是,这里调用了payoff和participant下的payoff,payoff是内置的用于统计收益的字段,payoff和participant类下都有这个字段,直接调用即可(关于这个字段的说明,参考(五)中的处理点数和收益)

Public Result

最后的安排页面顺序:在重复多轮的实验中,这些页面会重复出现

page_sequence = [Contribution, ResultsWaitPage, Results]

至此完成一个简单的公共品博弈的编写。

原文地址: https://github.com/MarvinLuoGS/otree-crash-tutorial 和 https://slides-otree-tutorial.netlify.app/1 感谢 罗干松 (ZJU) 范徐航(Duke) 同学,如果内容涉及侵权,告知我后会立即删除。

Categories:

Tags:

No responses yet

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注