Python framework for multiplayer decision games, behavioral experiments, and surveys
Switch branches/tags
Nothing to show
Clone or download
oTree-org
Latest commit 0bb77e0 Sep 24, 2018
Permalink
Failed to load latest commit information.
_rooms add example rooms and participant label file Aug 28, 2016
_static move static files from app to _static folder Jun 7, 2018
_templates/global templates static tag is now part of otree Sep 23, 2018
bargaining templates static tag is now part of otree Sep 23, 2018
bertrand templates static tag is now part of otree Sep 23, 2018
common_value_auction templates static tag is now part of otree Sep 23, 2018
cournot templates static tag is now part of otree Sep 23, 2018
dictator templates static tag is now part of otree Sep 23, 2018
guess_two_thirds templates static tag is now part of otree Sep 23, 2018
lemon_market templates static tag is now part of otree Sep 23, 2018
matching_pennies templates static tag is now part of otree Sep 23, 2018
payment_info templates static tag is now part of otree Sep 23, 2018
principal_agent templates static tag is now part of otree Sep 23, 2018
prisoner templates static tag is now part of otree Sep 23, 2018
public_goods templates static tag is now part of otree Sep 23, 2018
public_goods_simple templates static tag is now part of otree Sep 23, 2018
quiz templates static tag is now part of otree Sep 23, 2018
real_effort templates static tag is now part of otree Sep 23, 2018
survey templates static tag is now part of otree Sep 23, 2018
traveler_dilemma templates static tag is now part of otree Sep 23, 2018
trust templates static tag is now part of otree Sep 23, 2018
trust_simple templates static tag is now part of otree Sep 23, 2018
ultimatum templates static tag is now part of otree Sep 23, 2018
vickrey_auction templates static tag is now part of otree Sep 23, 2018
volunteer_dilemma templates static tag is now part of otree Sep 23, 2018
.gitignore simplification Mar 28, 2018
LICENSE . Jul 13, 2017
Procfile update procfile Nov 18, 2017
README.md code cleanup Mar 18, 2018
manage.py first commit Aug 23, 2016
requirements.txt delete requirements_server.txt Jan 16, 2018
requirements_base.txt remove timeout_submission because it is rarely used, use {% formfield… Aug 24, 2018
settings.py principal_agent: fix bug that contract could not be rejected Jul 12, 2018

README.md

oTree

oTree is a Python framework that lets you build:

  • Multiplayer strategy games, like the prisoner's dilemma, public goods game, and auctions
  • Controlled behavioral experiments in economics, psychology, and related fields
  • Surveys and quizzes

Live demo

http://demo.otree.org/

Homepage

http://www.otree.org/

Docs

http://otree.readthedocs.org

Quick start

Rather than cloning this repo directly, run these commands:

pip3 install -U otree
otree startproject oTree
cd oTree
otree devserver

Example game: guess 2/3 of the average

Below is a full implementation of the Guess 2/3 of the average game, where everyone guesses a number, and the winner is the person closest to 2/3 of the average. The game is repeated for 3 rounds. You can play the below game here.

models.py

from otree.api import (
    models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer,
    Currency
)

class Constants(BaseConstants):
    players_per_group = 3
    num_rounds = 3
    name_in_url = 'guess_two_thirds'

    jackpot = Currency(100)
    guess_max = 100


class Subsession(BaseSubsession):
    pass


class Group(BaseGroup):
    two_thirds_avg = models.FloatField()
    best_guess = models.IntegerField()
    num_winners = models.IntegerField()

    def set_payoffs(self):
        players = self.get_players()
        guesses = [p.guess for p in players]
        two_thirds_avg = (2 / 3) * sum(guesses) / len(players)
        self.two_thirds_avg = round(two_thirds_avg, 2)

        self.best_guess = min(guesses,
            key=lambda guess: abs(guess - self.two_thirds_avg))

        winners = [p for p in players if p.guess == self.best_guess]
        self.num_winners = len(winners)

        for p in winners:
            p.is_winner = True
            p.payoff = Constants.jackpot / self.num_winners

    def two_thirds_avg_history(self):
        return [g.two_thirds_avg for g in self.in_previous_rounds()]


class Player(BasePlayer):
    guess = models.IntegerField(min=0, max=Constants.guess_max)
    is_winner = models.BooleanField(initial=False)

pages.py

from otree.api import Page, WaitPage


class Introduction(Page):
    def is_displayed(self):
        return self.round_number == 1


class Guess(Page):
    form_model = 'player'
    form_fields = ['guess']


class ResultsWaitPage(WaitPage):
    def after_all_players_arrive(self):
        self.group.set_payoffs()


class Results(Page):
    def vars_for_template(self):
        sorted_guesses = sorted(p.guess for p in self.group.get_players())

        return {'sorted_guesses': sorted_guesses}


page_sequence = [Introduction,
                 Guess,
                 ResultsWaitPage,
                 Results]

HTML templates

Instructions.html Introduction.html Guess.html Results.html

tests.py (optional)

Test bots for multiplayer games run in parallel, and can run either from the command line, or in the browser, which you can try here.

from otree.api import Bot, SubmissionMustFail
from . import pages
from .models import Constants

class PlayerBot(Bot):
    cases = ['p1_wins', 'p1_and_p2_win']

    def play_round(self):
        if self.round_number == 1:
            yield (pages.Introduction)

        if self.case == 'p1_wins':
            if self.player.id_in_group == 1:
                for invalid_guess in [-1, 101]:
                    yield SubmissionMustFail(pages.Guess, {"guess": invalid_guess})
                yield (pages.Guess, {"guess": 9})
                assert self.player.payoff == Constants.jackpot
                assert 'you win' in self.html
            else:
                yield (pages.Guess, {"guess": 10})
                assert self.player.payoff == 0
                assert 'you did not win' in self.html
        else:
            if self.player.id_in_group in [1, 2]:
                yield (pages.Guess, {"guess": 9})
                assert self.player.payoff == Constants.jackpot / 2
                assert 'you are one of the 2 winners' in self.html
            else:
                yield (pages.Guess, {"guess": 10})
                assert self.player.payoff == 0
                assert 'you did not win' in self.html

        yield (pages.Results)

See docs on bots.

Features

  • Easy-to-use admin interface for launching games & surveys, managing participants, monitoring data, etc.
  • Simple yet extensive API
  • Publish your games to Amazon Mechanical Turk

Contact & support

Help & discussion mailing list

Contact chris@otree.org with bug reports.

Related repositories

The oTree core libraries are here.