quatrope / ajiaco Goto Github PK
View Code? Open in Web Editor NEWEasy social science experiments in a web environment
License: BSD 3-Clause "New" or "Revised" License
Easy social science experiments in a web environment
License: BSD 3-Clause "New" or "Revised" License
integration with read-the-docs
add travis integration
This document defines how to define a Model-View-Controller game using the
Ajiaco-Social Experiment Framework. This document only aims the
syntax to be used to define the game and not the internal implementation
details.
Also some definition will be referenced to the oTree
documentation to be used as inspiration.
The game is defined as an instance of the class ajiaco.Game
. This instance
contains all the configuration utilities to run the game and a series of
constants defined by the user. For example if we wan to define the
Matching Pennies Game from the oTree Tutorial
Example:
# we import the ajiaco module as ajc
import ajiaco as ajc
# we define the game and store it into the
# game variable
game = ajc.Game(
"mpennies", display="Matching Pennies",
groups_size=2, rounds=4, demo_participants=2,
stakes=100)
The Game
class only has one required parameter called name
. Name is
the unique string that identify the game inside the project. This value will
be prefixed in every database table (in our case the internal name of the
player model will be mpennies_player) and will be used to create the
urls to navigate the experiment.
From the oTree point of view this value replaces the
SESSIONS_CONFIG
name
key and theConstants.name_in_url
value.
display
the name to be displayed in the Ajiaco Admin and demo
interface. By default is the same values as name
.
oTree
SESSIONS_CONFIG
display_name
key
group_size
The number of player of the groups (default None
). If
The value is None
all the players are in only one group, and also
the programmer can manipulate the players manually to make arbitrary number
of groups whit arbitrary sizes.
Equivalent to oTree
Constants.players_per_group
rounds
The number of times to repeat this game (default 1
)
Equivalent to oTree
Constants.num_rounds
demo_participants
How many participants are needed to create a
game for the demo page. By default is the number needed to fill a group.
The Game class also accept any another parameter and store it as constant
inside the game instance. For example the stakes=100
.
The game configuration is reand only and you can access any value as regular
attribute of any Python object. Example
game.name
game.display
game.stakes
game.name = "foo" # this will fail
game.nonexistent_constant = "foo" # this also will fail
Ajiaco offers 4 model to store your data
Game.participants_storage
Data about the participant of the gameGame.sessions_storage
Data about all the sessions of the game.Game.rounds_storage
Data about the rounds of all the sessions ofGame.groups_storage
Data about all groups inside every round ofGame.players_storage
Data about the player of every group.To add attributes to the any of the models you need to call method
add_fields
just after the instantiation of the Game class. After this
Ajiaco will prevent any attempt of the modification from inside any "hook"
or "page". The syntax of this method is:
game.model_storage.add_fields(
field_name1=field_type1,
field_name2=field_type2,
....)
Where field type can be the Python classes:
int
: integersfloat
: floating point numbers.string
: textbool
: True
or False
.list
: a sorted sequencedict
: a map.ajiaco.binary
: binary data.ajiaco.money
: currency values.For example if we want to continue with the oTree tutorial 3, and add the
suggested attributes is_winner
, and tails
inside the players; and
paying_round
inside the session, the code will be:
game.sessions_storage.add_fields(
paying_round=int)
game.players_storage.add_fields(
tails=bool,
is_winner=bool)
The hooks are functions to be executed in some special cases. In this
development stage only one hooks are designed the round creation hook
Equivalent to oTree
Subsession.creating_session
method.
Allows you to initialize the round, by setting initial values on fields on
players, groups, participants, or the rounds. For example to replicate
the same of the oTree tutorial 3:
@game.when
def creating_round(session_round):
if session_round.is_first:
# ask if this round is the first
# in this case a random round is selected and stored
# inside the session paying_round attribute
paying_round = random.randint(1, game.rounds)
session_round.session.paying_round = paying_round
elif session_round.number == game.half_way:
# if the round number is the first of the second half
# invert all the roles
matrix = session_round.get_group_matrix()
for row in matrix:
row.reverse()
session_round.set_group_matrix(matrix)
elif session_round.number > game.half_way:
# if the current round is higher than the first
# of the second half, this simply copy the order of the
# previous rounds
session_round.group_like_round(game.half_way)
The final part of the game is design what the participant will see in their
screen. And this is defined (as before) as functions.
To create the tutorial requested choice page, Ajiaco suggest the next code:
@game.page.show
def choice(page):
page.player.tails = page.ask(
"Choice tails or heads",
choices={"Tails": True, "Heads": False}
).check(
page.player.number == 1 and page.player.tails == True,
"player number 1 can only choice 'tails'")
@game.page.show
register the function choice
as a pagepage
.page
parameter is utility that allow the user to send andpage.ask
) that ask the user to select from a combo box between@game.page.wait_for("group")
def wait(page):
matcher = page.group.player_number(1)
mismatcher = page.group.player_number(2)
matcher.is_winner = matcher.tails == mismatcher.tails
mismatcher.is_winner = not matcher.is_winner
pround = page.session.paying_round
for player in [matcher, mismatcher]:
if group.round.number == pround and player.is_winner:
player.payoff = game.stakes
else:
player.payoff = 0
@game.page.wait_for(...)
. The decorator always ask for the players"group"
), or the"round"
)@game.page.show
def results(page):
if not page.round.is_last:
page.skip()
selections = []
for p in page.player.in_all_rounds():
selections.append(p.tails)
page.set_vars(selections=selections)
In this case the page will be skipped whit page.skip()
(equivalent to
oTree Page.is_displayed()
) if the current round is not the last one.
Otherwise the page render the list of previous selections.
The timer can be implemented as an optional parameter of the decorator
page.show. For example
game.page.show(timeout=60)
def pagename(page):
...
Maybe we can implement helpers to create tables without using the template
language something like
page.set_vars(
selections=page.Table(headers="selections", rows=selections))
Functionalities like page.set_title
can be easily implemented to the
page.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.