I'm currently using JsPsych to create a behavioral experiment, particularly Matrix Reasoning, so the user chooses from a set of options based on a logic pattern.
My problem is that I need to repeat certain trials if the user fails when answering the first two trials after the practice blocks. Let's say, the first two trials are for practice purposes, and then the repetition rule is applied.
Since JsPsych's "timeline" is an array, its elements (aka trials) are created when the whole experiment starts, so looks like I can't just insert a new trial in between after the experiment has started.
Is there any way to force the execution of certain trials based on the user input? In other words, if the user chooses the right option, go to the next trial, if he/she chooses any of the wrong ones, return to a certain previous trial.
What I've tried so far:
(Based on JsPsych documentation and posts from JsPsych Google Group)
A) Creating a var using the call-function plugin and creating a boolean flag, which gets True if the answer is right, or wrong if isn't. Then I push another trial if the flag is false (ie. experiment.push(another_trial)
). Example:
var decide_next = {
type: 'call-function',
func: myfunction,
}
function myfunction(response_flag){
if (response_flag == False){
experiment.push(survey);
}else{
//just continue as normal
}
}
var survey={
//Here goes the specified trial if the user fails
}
This solutions fails completely, because of how the timeline is generated, as said earlier.
B) Using nested timelines. This is more like a brute force approach, where the main timeline contains the first questions, plus a flag, so if the flag gets certain value, I push a particular timeline. This one went really wrong, so I won't bother you with pointless code.
C) Using conditional timelines. Pretty much another brute force approach, but with a little more success. I've used this feature to display different feedback screens, for right and wrong answers respectively. Example:
//Conditional rules
var condition1 = false;
var condition2 = false;
var m_conditional1_1 = {
timeline: [m_wrong1],
conditional_function: function(data){
if(condition1 === false){
return true;
} else {
return false;
}
}
};
var m_conditional1_2 = {
timeline: [m_right1],
conditional_function: function(data){
if(condition1 === true){
return true;
} else {
return false;
}
}
};
var m_conditional2_1 = {
timeline: [m_wrong2],
conditional_function: function(data){
if(condition2 === false){
return true;
} else {
return false;
}
}
};
var m_conditional2_2 = {
timeline: [m_right2],
conditional_function: function(data){
if(condition2 === true){
return true;
} else {
return false;
}
}
};
// Feedback trials to display upon conditional trails's result
var m_wrong1 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso no es correcto. <br />Para responder correctamente debe mirar de izquierda a derecha en la fila de arriba.<br /><br />"+
"Cuando usted mira la fila de arriba la estrella azul cambia a un circulo amarillo. <br />"+
"Esto quiere decir que cuando usted va de izquierda a derecha en la fila de abajo la estrella azul también deberia cambiar a un circulo amarillo.<br /><br />"+
"Para obtener la respuesta correcta yendo de arriba hacia abajo, usted debe mirar los cuadros de la columna izquierda. "+
"Cuando usted va de arriba hacia abajo en la primera columna los cuadros tienen la misma forma y el mismo color: estrellas azules. <br /><br />"+
"Esto quiere decir que cuando usted va de arriba hacia abajo en la columna derecha los cuadros también deberian tener la misma forma y el mismo color: circulo amarillo. "+
"Usted obtiene la misma respuesta yendo de izquierda a derecha y yendo de arriba hacia abajo.<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_right1 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso es correcto.<br />Cuando usted va e izquierda a derecha en la fila de arriba la estrella azul cambia a un circulo amarillo. <br /><br />"+
"Esto quiere decir que cuando usted va de izquierda a derecha en la de abajo la estrella azul también deberia cambiar a un circulo amarillo. "+
"Cuando usted va de arriba hacia abajo en la primera columna los cuadros tienen la misma forma y el mismo color: estrellas azules. <br /><br />"+
"Esto quiere decir que cuando usted va de arriba hacia abajo en la segunda columna los cuadros también deben tener la misma forma y el mismo color: circulos amarillos. "+
"Usted obtiene la misma respuesta yendo de izquierda a derecha y yendo de arriba hacia abajo.<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_wrong2 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso no es correcto. <br />Cuando usted mira los cuadros de izquierda a derecha, usted puede ver que ellos<br />"+
"están en el siguiente orden: círculo grande, círculo pequeño, círculo grande, círculo pequeño, círculo grande.<br /><br />"+
"El círculo pequeño va en el cuadro con un signo de interrogación porque es la opción que mantiene el orden: un círculo pequeño va luego de un círculo grande.<br />"+
"<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_right2 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso es correcto.<br />Cuando usted mira los cuadros de izquierda a derecha, puede ver que ellos siguen este orden: "+
"círculo grande, círculo pequeño, círculo grande, círculo pequeño, círculo grande. <br /><br />"+
"El círculo pequeño va en el cuadro con un signo de interrogación porque es lo que mantiene el mismo orden que los anteriores.<br /><br />"+
"<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
// The two practice trials, here I capture the chosen answer and give it to the conditional rules variables. Matriz_practice_1 is associated to m_right_1 and
// m_wrong_1, which are its possible feedback screens.
// The same goes for Matriz_practice_2, with m_right_2 and m_wrong_2
var matriz_practice_1={
type: "survey-multi-choice1",
timeline:[
{
questions: ["<div class = centerbox>"+
"<p class = justified>"+
"Mire la siguiente figura. Usted debe escoger cual de las opciones que se encuentran abajo va en el"+
"cuadro con un signo de interrogación. La respuesta correcta es aquella que encaja yendo de "+
"izquierda a derecha y yendo de arriba hacia abajo. Usted sólo debe mirar de izquierda a derecha y "+
"de arriba hacia abajo. No mire diagonalmente. ¿Cuál de las opciones que se encuentran abajo va en"+
" el cuadro con un signo de interrogación?"+
"</p><br /><br /></div>"+
"<div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div>"],
data: {trialid: "P_MP_01"},
horizontal: true
},
],
options: [["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]],
horizontal: true,
required: 'true',
on_finish: function(data){
var test = data.responses;
if(test =='{"Q0":"Opcion5"}'){
condition1 = true;
} else {
condition1 = false;
}
}
};
var matriz_practice_2={
type: "survey-multi-choice1",
timeline:[
{
questions: ["<div class = centerbox>"+
"<p class = justified>"+
"Éste es otro tipo de problema. Los cuadros solo van de izquierda a derecha. La respuesta correcta "+
"seguirá el mismo orden que usted ve en los cuadros. ¿Cuál de las opciones que se encuentran abajo va "+
"en el cuadro con un signo de interrogación?"+
"</p><br /><br /></div>"+
"<div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div>"],
data: {trialid: "P_MP_02"},
horizontal: true
},
],
options: [["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]],
horizontal: true,
required: 'true',
on_finish: function(data){
var test = data.responses;
if(test =='{"Q0":"Opcion4"}'){
condition2 = true;
} else {
condition2 = false;
}
}
};
This works for short, linear structures, as in that case, I just need to decide which screen to display, but this solution fails when trying to go back to a previous trial if needed. From this solution, I could just define a really long set of binary conditions to "simulate" the repetition of trials when the user fails. But this can't be generated dynamically, so basically I'm forced to guess how many times an user will fail to answer properly, which isn't an option when the experiment is going to be run with 200+ volunteers. Plus, such brute force strategy will increase in complexity if the repetition condition is applied to more than 2 trials, which may actually happen in the future.
I'm guessing that deep modding of JsPsych's core javascript file may lead to a solution, but that goes beyond my current javascript skills and knowledge.
Help on this subject would be much appreciated.
Some extra notes to test this code:
A short, working version of the experiment code is provided below. Aesthetic issues may occur due to CSS being ignored this time. This code requires you to have jspsych folder on the same directory as the main HTML file.
<!doctype html>
<html>
<head>
<title>Condition Tester</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="jspsych-5.0.3/jspsych.js"></script>
<script src="jspsych-5.0.3/plugins/jspsych-survey-multi-choice.js"></script>
<script src="jspsych-5.0.3/plugins/jspsych-text.js"></script>
<script src="jspsych-5.0.3/plugins/jspsych-instructions.js"></script>
</head>
<body>
<div class="centered">
<script>
var condition1 = false;
var condition2 = false;
/* Texto Inicial */
var matrizexplanation={
type: "instructions",
pages: ["<div class = centerbox>"+
"<p class = center-block-text>"+
"A continuación, le presentaremos una serie de figuras, donde cada una muestra un patrón lógico.<br />"+
"Tendrá que elegir entre 5 alternativas para completar ese cada patrón."+
"</p></div>"],
allow_keys: false,
show_clickable_nav: true,
timing_post_trial: 50,
data:{trialid: "Instructions_Matriz"}
};
var matriz_practice_1={
type: "survey-multi-choice1",
timeline:[
{
questions: ["<div class = centerbox>"+
"<p class = justified>"+
"Mire la siguiente figura. Usted debe escoger cual de las opciones que se encuentran abajo va en el"+
"cuadro con un signo de interrogación. La respuesta correcta es aquella que encaja yendo de "+
"izquierda a derecha y yendo de arriba hacia abajo. Usted sólo debe mirar de izquierda a derecha y "+
"de arriba hacia abajo. No mire diagonalmente. ¿Cuál de las opciones que se encuentran abajo va en"+
" el cuadro con un signo de interrogación?"+
"</p><br /><br /></div>"+
"<div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div>"],
data: {trialid: "P_MP_01"},
horizontal: true
},
],
options: [["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]],
horizontal: true,
required: 'true',
on_finish: function(data){
var test = data.responses;
if(test =='{"Q0":"Opcion5"}'){
condition1 = true;
} else {
condition1 = false;
}
}
};
var matriz_practice_2={
type: "survey-multi-choice1",
timeline:[
{
questions: ["<div class = centerbox>"+
"<p class = justified>"+
"Éste es otro tipo de problema. Los cuadros solo van de izquierda a derecha. La respuesta correcta "+
"seguirá el mismo orden que usted ve en los cuadros. ¿Cuál de las opciones que se encuentran abajo va "+
"en el cuadro con un signo de interrogación?"+
"</p><br /><br /></div>"+
"<div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div>"],
data: {trialid: "P_MP_02"},
horizontal: true
},
],
options: [["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]],
horizontal: true,
required: 'true',
on_finish: function(data){
var test = data.responses;
if(test =='{"Q0":"Opcion4"}'){
condition2 = true;
} else {
condition2 = false;
}
}
};
var m_wrong1 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso no es correcto. <br />Para responder correctamente debe mirar de izquierda a derecha en la fila de arriba.<br /><br />"+
"Cuando usted mira la fila de arriba la estrella azul cambia a un circulo amarillo. <br />"+
"Esto quiere decir que cuando usted va de izquierda a derecha en la fila de abajo la estrella azul también deberia cambiar a un circulo amarillo.<br /><br />"+
"Para obtener la respuesta correcta yendo de arriba hacia abajo, usted debe mirar los cuadros de la columna izquierda. "+
"Cuando usted va de arriba hacia abajo en la primera columna los cuadros tienen la misma forma y el mismo color: estrellas azules. <br /><br />"+
"Esto quiere decir que cuando usted va de arriba hacia abajo en la columna derecha los cuadros también deberian tener la misma forma y el mismo color: circulo amarillo. "+
"Usted obtiene la misma respuesta yendo de izquierda a derecha y yendo de arriba hacia abajo.<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_right1 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso es correcto.<br />Cuando usted va e izquierda a derecha en la fila de arriba la estrella azul cambia a un circulo amarillo. <br /><br />"+
"Esto quiere decir que cuando usted va de izquierda a derecha en la de abajo la estrella azul también deberia cambiar a un circulo amarillo. "+
"Cuando usted va de arriba hacia abajo en la primera columna los cuadros tienen la misma forma y el mismo color: estrellas azules. <br /><br />"+
"Esto quiere decir que cuando usted va de arriba hacia abajo en la segunda columna los cuadros también deben tener la misma forma y el mismo color: circulos amarillos. "+
"Usted obtiene la misma respuesta yendo de izquierda a derecha y yendo de arriba hacia abajo.<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_wrong2 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso no es correcto. <br />Cuando usted mira los cuadros de izquierda a derecha, usted puede ver que ellos<br />"+
"están en el siguiente orden: círculo grande, círculo pequeño, círculo grande, círculo pequeño, círculo grande.<br /><br />"+
"El círculo pequeño va en el cuadro con un signo de interrogación porque es la opción que mantiene el orden: un círculo pequeño va luego de un círculo grande.<br />"+
"<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_right2 = {
type: "text",
text: "<div class = matrizlimit><div class= centered><div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div><br /><br /><div class='matrizfeedback'>Eso es correcto.<br />Cuando usted mira los cuadros de izquierda a derecha, puede ver que ellos siguen este orden: "+
"círculo grande, círculo pequeño, círculo grande, círculo pequeño, círculo grande. <br /><br />"+
"El círculo pequeño va en el cuadro con un signo de interrogación porque es lo que mantiene el mismo orden que los anteriores.<br /><br />"+
"<br /><br /><br /><br />Presione una tecla para continuar<br /><br /><br /><br /></div>"
};
var m_conditional1_1 = {
timeline: [m_wrong1],
conditional_function: function(data){
if(condition1 === false){
return true;
} else {
return false;
}
}
};
var m_conditional1_2 = {
timeline: [m_right1],
conditional_function: function(data){
if(condition1 === true){
return true;
} else {
return false;
}
}
};
var m_conditional2_1 = {
timeline: [m_wrong2],
conditional_function: function(data){
if(condition2 === false){
return true;
} else {
return false;
}
}
};
var m_conditional2_2 = {
timeline: [m_right2],
conditional_function: function(data){
if(condition2 === true){
return true;
} else {
return false;
}
}
};
var matrizstarter={
type: "instructions",
pages: ["<div class = centerbox>"+
"<p class = center-block-text>"+
"Ahora deberá seguir respondiendo, pero no recibirá avisos indicando <br />si su respuesta es correcta o incorrecta.<br /><br />"+
"</p></div>"],
allow_keys: false,
show_clickable_nav: true,
timing_post_trial: 50,
data:{trialid: "Instructions_Matriz"}
};
var matrices = {
//type: "survey-multi-choice",
type: "survey-multi-choice1",
timeline:[
/*{
questions: ["<div class='centered'><img src ='img/1A.png' /></div><br /><br /><div class='centered'><img src ='img/1B.png' </img></div>"],
data: {trialid: "MP_01"},
horizontal: true
//options: ["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]
},
{
questions: ["<div class='centered'><img src ='img/2A.png' /></div><br /><br /><div class='centered'><img src ='img/2B.png' </img></div>"],
data: {trialid: "MP_02"}
//options: ["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]
},*/
{
questions: ["<div class='centered'><img src ='img/3A.png' /></div><br /><br /><div class='centered'><img src ='img/3B.png' </img></div>"],
data: {trialid: "MP_03"}
//options: ["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]
},
],
options: [["Opcion1","Opcion2","Opcion3","Opcion4","Opcion5"]],
horizontal: true,
required: 'true',
};
var experimento_matrices = [];
experimento_matrices.push(matrizexplanation);
experimento_matrices.push(matriz_practice_1);
experimento_matrices.push(m_conditional1_1);
experimento_matrices.push(m_conditional1_2);
experimento_matrices.push(matriz_practice_2);
experimento_matrices.push(m_conditional2_1);
experimento_matrices.push(m_conditional2_2);
experimento_matrices.push(matrizstarter);
experimento_matrices.push(matrices);
jsPsych.init({
timeline: experimento_matrices,
on_finish: function(){
jsPsych.data.localSave('second_battery_results.csv', 'csv');
},
on_trial_start: function(){
console.log("***************************",test);
},
default_iti: 0
});
</script>
</div>
</body>
</html>