Parte 3: Juego de confianza

Ahora vamos a crear un juego de Confianza para 2 jugadores y aprender algunas características más de oTree.

Para empezar, el Jugador 1 recibe 10 puntos; el Jugador 2 no recibe nada. El Jugador 1 puede enviar algunos o todos sus puntos al Jugador 2. Antes de que P2 reciba estos puntos, se triplicarán. Una vez que P2 reciba los puntos triplicados, puede decidir enviar algunos o todos sus puntos a P1.

La aplicación completa está aquí.

Crear la aplicación

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

Constantes

Ve a la clase de constantes de tu aplicación (C).

Primero definimos las constantes de nuestra aplicación. La dotación es de 10 puntos y la donación se triplica.

class C(BaseConstants):
    NAME_IN_URL = 'my_trust'
    PLAYERS_PER_GROUP = 2
    NUM_ROUNDS = 1

    ENDOWMENT = cu(10)
    MULTIPLICATION_FACTOR = 3

Modelos

Luego añadimos campos a player y group. Hay 2 puntos de datos críticos que registrar: la cantidad «enviada» de P1 y la cantidad «devuelta» de P2.

Tu primer instinto puede ser definir los campos en el Player así:

# Don't copy paste this
class Player(BasePlayer):

    sent_amount = models.CurrencyField()
    sent_back_amount = models.CurrencyField()

El problema con este modelo es que sent_amount solo se aplica a P1, y sent_back_amount solo se aplica a P2. No tiene sentido que P1 tenga un campo llamado sent_back_amount. ¿Cómo podemos hacer que nuestro modelo de datos sea más preciso?

Podemos hacerlo definiendo esos campos a nivel de Group. Esto tiene sentido porque cada grupo tiene exactamente 1 sent_amount y exactamente 1 sent_back_amount.

class Group(BaseGroup):

    sent_amount = models.CurrencyField(
        label="How much do you want to send to participant B?"
    )
    sent_back_amount = models.CurrencyField(
        label="How much do you want to send back?"
    )

También definimos una función llamada sent_back_amount_choices para llenar dinámicamente el menú desplegable. Esta es la característica llamada {field_name}_choices, que se explica aquí: Validación dinámica de campos de formulario.

def sent_back_amount_choices(group):
    return currency_range(
        0,
        group.sent_amount * C.MULTIPLICATION_FACTOR,
        1
    )

Definir las plantillas y páginas

Necesitamos 3 páginas:

  • Página de «Enviar» de P1

  • Página de «Devolver» de P2

  • Página de «Resultados» que ambos usuarios ven.

Página de Enviar

class Send(Page):

    form_model = 'group'
    form_fields = ['sent_amount']

    @staticmethod
    def is_displayed(player):
        return player.id_in_group == 1

Usamos is_displayed() para mostrar esto solo a P1; P2 salta la página. Para más información sobre id_in_group, consulta Grupos.

Para la plantilla, establece el title como Trust Game: Your Choice, y el content como:

<p>
You are Participant A. Now you have {{C.ENDOWMENT}}.
</p>

{{ formfields }}

{{ next_button }}

SendBack.html

Esta es la página que P2 ve para devolver dinero. Establezca el bloque title en Trust Game: Your Choice y el bloque content en:

<p>
    You are Participant B. Participant A sent you {{group.sent_amount}}
    and you received {{tripled_amount}}.
</p>

{{ formfields }}

{{ next_button }}

Aquí está el código de la página. Notas:

  • Usamos vars_for_template() para pasar la variable tripled_amount a la plantilla. No se pueden realizar cálculos directamente en el código HTML, por lo que este número necesita ser calculado en código Python y pasado a la plantilla.

class SendBack(Page):

    form_model = 'group'
    form_fields = ['sent_back_amount']

    @staticmethod
    def is_displayed(player):
        return player.id_in_group == 2

    @staticmethod
    def vars_for_template(player):
        group = player.group

        return dict(
            tripled_amount=group.sent_amount * C.MULTIPLICATION_FACTOR
        )

Resultados

La página de resultados necesita verse ligeramente diferente para P1 en comparación con P2. Por lo tanto, usamos la declaración {{ if }} para condicionar el id_in_group del jugador actual. Establezca el bloque title en Resultados y el bloque de contenido en:

{{ if player.id_in_group == 1 }}
    <p>
        You sent Participant B {{ group.sent_amount }}.
        Participant B returned {{ group.sent_back_amount }}.
    </p>
{{ else }}
    <p>
        Participant A sent you {{ group.sent_amount }}.
        You returned {{ group.sent_back_amount }}.
    </p>

{{ endif }}

<p>
Therefore, your total payoff is {{ player.payoff }}.
</p>
class Results(Page):
    pass

Páginas de espera y secuencia de páginas

Agregar 2 páginas de espera:

  • WaitForP1 (P2 necesita esperar mientras P1 decide cuánto enviar)

  • ResultsWaitPage (P1 necesita esperar mientras P2 decide cuánto devolver)

Después de la segunda página de espera, debemos calcular los pagos. Así que, definimos una función llamada set_payoffs:

def set_payoffs(group):
    p1 = group.get_player_by_id(1)
    p2 = group.get_player_by_id(2)
    p1.payoff = C.ENDOWMENT - group.sent_amount + group.sent_back_amount
    p2.payoff = group.sent_amount * C.MULTIPLICATION_FACTOR - group.sent_back_amount

Luego en ResultsWaitPage, establece after_all_players_arrive:

after_all_players_arrive = set_payoffs

Asegúrate de que estén ordenados correctamente en page_sequence:

page_sequence = [
    Send,
    WaitForP1,
    SendBack,
    ResultsWaitPage,
    Results,
]

Agrega una entrada a tu SESSION_CONFIGS

Crea una configuración de sesión con my_trust en la secuencia de la aplicación.

Ejecuta el servidor

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