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_amounta 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.