Parte 2: Juego de bienes públicos

(Un video de este tutorial está en YouTube )

Ahora crearemos un simple juego de bienes públicos. El juego de bienes públicos es un juego clásico en economía.

Este es un juego para tres jugadores donde cada jugador comienza con 100 puntos. Cada jugador decide individualmente cuántos de sus puntos quiere contribuir al grupo. Las contribuciones combinadas se multiplican por 2, y luego se dividen equitativamente en tres partes y se redistribuyen a los jugadores.

El código completo para la aplicación que escribiremos está aquí.

Crear la aplicación

Al igual que en la parte anterior del tutorial, crea otra aplicación, llamada my_public_goods.

Constantes

Ve a la clase de constantes de tu aplicación (C). (Para más información, consulta Constantes.)

  • Establece PLAYERS_PER_GROUP en 3. oTree dividirá automáticamente a los jugadores en grupos de 3.

  • La dotación para cada jugador es de 1000 puntos. Así que definamos ENDOWMENT y establezcámoslo en un valor monetario de 1000.

  • Cada contribución se multiplica por 2. Así que define una constante entera llamada MULTIPLIER = 2:

Ahora tenemos las siguientes constantes:

PLAYERS_PER_GROUP = 3
NUM_ROUNDS = 1
ENDOWMENT = cu(1000)
MULTIPLIER = 2

Modelos

Después de que se juegue el juego, ¿qué puntos de datos necesitaremos sobre cada jugador? Es importante registrar cuánto contribuyó cada persona. Así que, ve al modelo Player y define una columna contribution:

class Player(BasePlayer):
    contribution = models.CurrencyField(
        min=0,
        max=C.ENDOWMENT,
        label="How much will you contribute?"
    )

También necesitamos registrar el pago que el usuario realiza al final del juego, pero no necesitamos definir explícitamente un campo payoff, porque en oTree, el Player ya contiene una columna payoff.

¿Qué puntos de datos nos interesa registrar sobre cada grupo? Podríamos estar interesados en conocer las contribuciones totales al grupo y la parte individual devuelta a cada jugador. Así que, definimos esos 2 campos en el Group:

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

Páginas

Este juego tiene 3 páginas:

  • Página 1: los jugadores deciden cuánto contribuir

  • Página 2: Página de espera: los jugadores esperan a que otros en su grupo terminen

  • Página 3: se les informa a los jugadores los resultados

Página 1: Contribute

Primero definamos Contribute. Esta página contiene un formulario, así que necesitamos definir form_model y form_fields. Específicamente, este formulario debe permitirte establecer el campo contribution en el jugador. (Para más información, consulta Formularios.)

class Contribute(Page):

    form_model = 'player'
    form_fields = ['contribution']

Ahora, creamos la plantilla HTML.

Establezca el bloque title a Contribute, y el bloque content a:

<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 }}

Página 2: ResultsWaitPage

Cuando todos los jugadores hayan completado la página Contribute, se pueden calcular los pagos de los jugadores. Agregue una función de grupo llamada 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

Después de que un jugador haga una contribución, no puede ver la página de resultados de inmediato; primero necesita esperar a que los otros jugadores contribuyan. Por lo tanto, necesita agregar un WaitPage. Llamémoslo ResultsWaitPage. Cuando un jugador llega a una página de espera, debe esperar hasta que todos los demás jugadores del grupo hayan llegado. Luego, todos pueden proceder a la siguiente página. (Para más información, consulte Páginas de espera.)

Agrega after_all_players_arrive a ResultsWaitPage, y configúralo para activar set_payoffs:

after_all_players_arrive = 'set_payoffs'

Página 3: Results

Ahora creamos una página llamada Results. Establece el contenido de la plantilla en:

<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 }}

Secuencia de páginas

Asegúrate de que tu page_sequence sea correcto:

page_sequence = [
    Contribute,
    ResultsWaitPage,
    Results
]

Define la configuración de la sesión

Agregamos otra configuración de sesión con my_public_goods en la secuencia de la aplicación.

Ejecuta el código

Carga el proyecto nuevamente y luego abre tu navegador en http://localhost:8000.

Soluciona problemas con print()

A menudo leo mensajes en foros de programación como: «Mi programa no funciona. No puedo encontrar el error, aunque he pasado horas mirando mi código».

La solución no es volver a leer el código hasta que encuentres un error; es probar tu programa de manera interactiva.

La forma más sencilla es usar declaraciones print(). Si no aprendes esta técnica, no podrás programar juegos de manera efectiva.

Solo necesitas insertar una línea en tu código como esta:

print('group.total_contribution is', group.total_contribution)

Coloca esta línea en la parte de tu código que no está funcionando, como la función de pagos definida arriba. Cuando juegues el juego en tu navegador y ese código se ejecute, tu declaración de print se mostrará en la ventana de tu terminal (no en tu navegador web).

Puedes añadir muchos prints en tu código.

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)