# Part 1: Public goods game¶

We will now create a simple public goods game. The public goods game is a classic game in economics,

This is a three player game where each player is initially endowed with 100 points. Each player individually makes a decision about how many of their points they want to contribute to the group. The combined contributions are multiplied by 2, and then divided evenly three ways and redistributed back to the players.

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

pip3 install -U otree


## Create the app¶

Use your command line to cd to the oTree project folder you created, the one that contains requirements_base.txt.

In this folder, create the public goods app:

otree startapp my_public_goods  Then in PyCharm, go to the folder my_public_goods that was created. ## Define models.py¶ Open models.py. This file contains the game’s data models (player, group, subsession) and constant parameters. First, let’s modify the Constants class to define our constants and parameters – things that are the same for all players in all games. (For more info, see Constants.) • There are 3 players per group. So, change players_per_group to 3. oTree will then automatically divide players into groups of 3. • The endowment to each player is 100 points. So, let’s define endowment and set it to c(100). (c() means it is a currency amount; see Money & Payoffs). • Each contribution is multiplied by 2. So let’s define multiplier and set it to 2: Now we have: class Constants(BaseConstants): name_in_url = 'my_public_goods' players_per_group = 3 num_rounds = 1 endowment = c(100) multiplier = 2  Note Python is case-sensitive, so pay attention to which letters are capitalized. Also, make sure to indent your code properly. All lines in a block of code must be aligned along the left edge. When you’re inside a code block (e.g. “if”, “for”, “def”; see below), you need to indent by 4 spaces. Now let’s think about the main entities in this game: the Player and the Group. After the game is played, what data points will we need about each player? It’s important to know how much each person contributed. So, we define a field contribution, which is a currency (see Money & Payoffs): class Player(BasePlayer): contribution = models.CurrencyField(min=0, max=Constants.endowment)  What data points are we interested in recording about each group? We might be interested in knowing the total contributions to the group, and the individual share returned to each player. So, we define those 2 fields: class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField()  Now let’s define our payoff function. The argument to the function should be a group whose payoffs should be calculated. class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() 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 p in group.get_players(): p.payoff = Constants.endowment - p.contribution + group.individual_share  Now, we will change one small thing. We will rename the argument group to self, because in Python, a method’s first argument should always be named self (however, self still represents a group). For more explanation, see What is “self”?. Anyway, rename group in the argument, as well as the 6 usages inside the function. class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() def set_payoffs(self): players = self.get_players() contributions = [p.contribution for p in players] self.total_contribution = sum(contributions) self.individual_share = self.total_contribution * Constants.multiplier / Constants.players_per_group for p in self.get_players(): p.payoff = Constants.endowment - p.contribution + self.individual_share  ## Define the template¶ This game has 2 pages: • Page 1: players decide how much to contribute • Page 2: players are told the results In this section we will define the HTML templates to display the game. So, let’s make 2 HTML files under templates/my_public_goods/. The first is Contribute.html, which contains a brief explanation of the game, and a form field where the player can enter their contribution. {% extends "global/Page.html" %} {% load staticfiles otree %} {% block title %} Contribute {% endblock %} {% block content %} <p> This is a public goods game with {{ Constants.players_per_group }} players per group, an endowment of {{ Constants.endowment }}, and an efficiency factor of {{ Constants.multiplier }}. </p> {% formfield player.contribution label="How much will you contribute?" %} {% next_button %} {% endblock %}  Side note: if you are using PyCharm, when you type {%, PyCharm automatically inserts the closing %} and then gives auto-suggestions for what to type in between. If you are not seeing this, make sure you enabled Django support. (For more info on how to write a template, see Templates.) The second template will be called Results.html. {% extends "global/Page.html" %} {% load staticfiles otree %} {% block title %} Results {% endblock %} {% block content %} <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 %} {% endblock %}  ## Define pages.py¶ Now we define our pages, which contain the logic for how to display the HTML templates. (For more info, see Pages.) Since we have 2 templates, we need 2 Page classes in pages.py. The names should match those of the templates (Contribute and Results). First let’s define Contribute. This page contains a form, so we need to define form_model and form_fields. Specifically, this form should let you set the contribution field on the player. (For more info, see Forms.) class Contribute(Page): form_model = 'player' form_fields = ['contribution']  Now we define Results. This page doesn’t have a form so our class definition can be empty (with the pass keyword). class Results(Page): pass  We are almost done, but one more page is needed. After a player makes a contribution, they cannot see the results page right away; they first need to wait for the other players to contribute. You therefore need to add a WaitPage. When a player arrives at a wait page, they must wait until all other players in the group have arrived. Then everyone can proceed to the next page. (For more info, see Wait pages). When all players have completed the Contribute page, the players’ payoffs can be calculated. You can trigger this calculation inside the the after_all_players_arrive method on the WaitPage, which automatically gets called when all players have arrived at the wait page. Another advantage of putting the code here is that it only gets executed once, rather than being executed separately for each participant, which is redundant. We write self.group.set_payoffs() because earlier we decided to name the payoff calculation method set_payoffs, and it’s a method under the Group class. That’s why we prefix it with self.group. class ResultsWaitPage(WaitPage): def after_all_players_arrive(self): self.group.set_payoffs()  Now we define page_sequence to specify the order in which the pages are shown: page_sequence = [ Contribute, ResultsWaitPage, Results ]  ## Define the session config in settings.py¶ Now we go to settings.py in the project’s root folder and add an entry to SESSION_CONFIGS. SESSION_CONFIGS = [ { 'name': 'my_public_goods', 'display_name': "My Public Goods (Simple Version)", 'num_demo_participants': 3, 'app_sequence': ['my_public_goods'], }, # other session configs ... ]  ## Reset the database and run¶ Enter:  otree resetdb
\$ otree runserver


Then open your browser to http://127.0.0.1:8000 to play the game.

(You can also run otree resetdb --noinput, which skips the prompt about whether you want to delete your database.)

## Troubleshoot with print()¶

If your code is still not behaving properly, you can isolate the problem using print() just as you would to debug any Python program. For example, you could add some print statements to set_payoffs:

def set_payoffs(self):
players = self.get_players()
contributions = [p.contribution for p in players]
self.total_contribution = sum(contributions)
self.individual_share = self.total_contribution * Constants.multiplier / Constants.players_per_group
for p in self.get_players():
p.payoff = Constants.endowment - p.contribution + self.individual_share
print('@@@@@@p.payoff is', p.payoff)


The output will be displayed in the console window where you ran otree runserver (not in your web browser).

## Make changes while the server is running¶

Once you have the server running, try changing some text in Contribute.html or Results.html, then save the file and refresh your page. You will see the changes immediately.

## Write a bot¶

Let’s write a bot that simulates a player playing the game we just programmed. Having a bot will save us a lot of work, because it can automatically test that the game still works each time we make changes.

Go to tests.py, and add this code in PlayerBot:

class PlayerBot(Bot):

def play_round(self):
yield (pages.Contribute, {'contribution': c(42)})
yield (pages.Results)


This bot first submits the Contribute page with a contribution of 42, then submits the results page (to proceed to the next app).

otree test my_public_goods


You will see the output of the bots in the command line. To make the bot play in your web browser, go to settings.py and add 'use_browser_bots': True to the session config, like this:

SESSION_CONFIGS = [
{
'name': 'my_public_goods',
'display_name': "My Public Goods (Simple Version)",
'num_demo_participants': 3,
'app_sequence': ['my_public_goods'],
'use_browser_bots': True
},
# other session configs ...
]


Now, when you create a new session and open the start links, it will play automatically.

Bots can do many more things; to learn more, see the section Bots.