Week 4 practical, maze task

Some thoughts on how you could code up a maze task

The question

In the practical I set the following problem - note that it’s marked as optional and challenging!

mazes

How would you adapt the self-paced reading code we gave you to do this? The first most obvious problem is that the self-paced reading code presents a single word on-screen at each reading trial, but here you want 2 - the correct continuation and the distractor (for ease I’ll use non-word distractors, so I’ll give you code for an L-maze, but changing it to a G-maze just involves plugging in different distractors). So we need a way to pass two options in to be paired up and displayed on screen somehow.

There are a couple of ways (at least!) we could do this, I’ll show you them below. Ideally we’d also show those two options on screen in random order, rather than e.g. always the correct option on the left (which participants would quickly notice!), so I’ll implement that too. In terms of how participants give their responses, we could get them to click on buttons (so the two possible continuations are the two buttons they see), or we could show the two words on the left and right and get a key-press to indicate which continuation the participant wants (e.g. z key to indicate the left continuation, m key for the right, since those are keys on the left and right of a QWERTY keyboard respectively). Finally, following my suggestion, I am going to skip the code for aborting a trial when the participant gives an incorrect response - but in the next practical you’ll see how to do contingent trials where what a participant selects on one trial influences what they see next, which would be a crucial ingredient of the trial-aborting process.

Option 1: two lists

One way to pass in two options for every reading trial (the correct continuation plus a non-word distractor) is just to pass in two lists of words - one is the list of correct continuations, the other is the list of distractors. For instance, if we want our correct continuations to be “The”, “dog”, “chased”, “the” and “cat” (i.e. the sentence is “The dog chased the cat.” as in the L-maze image above) and our distractors are “x-x-x”, “thon”, “pirths”, “swaz”, and “yits” (i.e. just a sequence of non-words of the same length), we could represent those two sets of options like this:

var correct_continuations = ["The","dog","chased","the","cat."];
var distractors =           ["x-x-x","thon","pirths","swax","yits."];

Then we’ll need to work through both lists so that on the first trial we display the first item from both lists (so the participant chooses between “The” and “x-x-x”), on the second trial we display the second item from both lists (“dog” vs “thon”) and so on. That’s nice and easy with a for-loop where we count through the positions in the list, pairing up the items as we go. So we could write a make_maze_trial function that does this for us - we give it two lists, it uses a for-loop to work through them and pair up our items. At this point I have to decide what kind of response I am going to take - for this implementation I will use html-button-response, the word choices will be the buttons (and the html will just be an instruction to select a continuation). So the two options on every trial will end up being the choices parameter. The code for that would look something like this:

function make_maze_trial(sentence_as_word_list, distractors_as_word_list) {
  var choices_sequence = []; //empty choice sequence to start
  for (var i = 0; i < sentence_as_word_list.length; i++) {
    //for each value of the counter i=0 to i=length-1
    correct_continuation = sentence_as_word_list[i];
    incorrect_continuation = distractors_as_word_list[i];
    ordered_choices = [correct_continuation, incorrect_continuation]; //put the two options together
    choices_sequence.push({ choices: ordered_choices }); //these will be our choices
  }
  var trial = {
    type: jsPsychHtmlButtonResponse, //make an html-button-response trial with an embedded timeline
    timeline: [
      {
        stimulus: "<em>Select a continuation</em>", //boring stimulus
        timeline: choices_sequence, //each trial varies only in the choices offered
      },
    ],
  }; 
  return trial; //return the trial you have built
};

To use this to generate our trial we would then do:

var maze_trial_1 = make_maze_trial(
  ["The", "dog", "chased", "the", "cat."],
  ["x-x-x", "thon", "pirths", "swax", "yits."]
);

This is very similar to the make_spr_trial function we used in the main code, except that:

That would be OK, but the correct continuation would always be on the left, which is a bit rubbish. Instead we can use a built-in jspsych function, jsPsych.randomization.shuffle, to shuffle the ordered_choices list so that the correct continuation will sometimes be on the left and sometimes on the right. That revised function looks like this:

function make_maze_trial(sentence_as_word_list, distractors_as_word_list) {
  var choices_sequence = []; //empty choice sequence to start
  for (var i = 0; i < sentence_as_word_list.length; i++) {
    //for each value of the counter i=0 to i=length-1
    correct_continuation = sentence_as_word_list[i];
    incorrect_continuation = distractors_as_word_list[i];
    ordered_choices = [correct_continuation, incorrect_continuation]; //put the two options together
    shuffled_choices = jsPsych.randomization.shuffle(ordered_choices); //randomise the order
    choices_sequence.push({ choices: shuffled_choices }); //these will be our choices
  }
  var trial = {
    type: jsPsychHtmlButtonResponse, //make an html-button-response trial with an embedded timeline
    timeline: [
      {
        stimulus: "<em>Select a continuation</em>", //boring stimulus
        timeline: choices_sequence, //each trial varies only in the choices offered
      },
    ],
  }; 
  return trial; //return the trial you have built
};

You can download all the code for this implementation through the following two links:

Option 2: one list of pairs

Option 1 is OK, but I don’t love the fact that the correct and incorrect continuations are in completely separate lists - so e.g. if I want to see which distractor is going to be paired with “chased” I have to count through the lists. An alternative is to pass our make_maze_trial function a single list which contains all the words already paired up. That is actually really easy to do because lists can themselves contain lists - so we can have a list of items, where each item in that list is itself a list containing the correct continuation and its associated distractor. That would be represented as follows:

var one_list = [
  ["The","x-x-x"],
  ["dog","thon"],
  ["chased","pirths"],
  ["the","swax"],
  ["cat.","yits."]
  ];

So one_list is a list of 5 items, each of which is a list containing the correct continuation and the distractor it is to be shown with. We could then use a for-loop to work through this list, simply shuffling each pair and using them as the choices parameter in our reading trials as we go, like this:

function make_maze_trial(sentence_distractor_pairs) {
  var choices_sequence = []; //empty stimulus sequence to start
  for (var ordered_choices of sentence_distractor_pairs) {
    shuffled_choices = jsPsych.randomization.shuffle(ordered_choices); //randomise the order
    choices_sequence.push({ choices: shuffled_choices }); //these will be our choices
  }
  var trial = {
    type: jsPsychHtmlButtonResponse, //make a trial with an embedded timeline
    timeline: [
      {
        stimulus: "<em>Select a continuation</em>", //boring stimulus
        timeline: choices_sequence,
      },
    ],
  };
  return trial; //return the trial you have built
};

Then we use our function like this:

var maze_trial_1 = make_maze_trial([
  ["The", "x-x-x"],
  ["dog", "thon"],
  ["chased", "pirths"],
  ["the", "swax"],
  ["cat.", "yits."],
]);

Notice that since I only need to loop through one list here, not two, I am using the for (var item of list) type of for-loop to grab the pair of choices on each trial. Notice also that specifying our trial in a slightly more logical representation (a pair of words for each trial, rather than two lists of words to be paired up) simplified the code for us a bit.

The code above is using button responses though, and I said I’d show you how to do this kind of thing using keyboard response trials to. The way we’d do that is by taking our shuffled_choices and pasting together the two words in there into a single string that we can display on-screen using the html-keyboard-response plugin. Sticking together a list of strings into a single big string is something that you quite frequently need to do, so javascript provides a function called join - you apply it to a list, specify what separator you want between things in the joined string, and it’ll join them up for you. Here’s how we’d use join to convert our list of choices into a single string, then plug that into the timeline of an html-keyboard-response trial as the stimulus:

function make_maze_trial(sentence_distractor_pairs) {
  var choices_sequence = []; //empty stimulus sequence to start
  for (var ordered_choices of sentence_distractor_pairs) {
    shuffled_choices = jsPsych.randomization.shuffle(ordered_choices); //randomise the order
    combined_choices = shuffled_choices.join(" "); //paste the two choices together in one string separated by space
    choices_sequence.push({ stimulus: combined_choices }); //this will be our stimulus
  }
  var trial = {
    type: jsPsychHtmlKeyboardResponse, //make a trial with an embedded timeline
    timeline: [
      {
        prompt: "<em>Press z for the left choice, m for the right choice",
        choices: ["z", "m"],
        timeline: choices_sequence,
      },
    ],
  };
  return trial; //return the trial you have built
};

You can download all the code for this implementation through the following two links:

References

Boyce, V., Futrell, R., & Levy, R. P. (2020). Maze Made Easy: Better and easier measurement of incremental processing difficulty. Journal of Memory and Language, 111, 104082.

Forster, K. I., Guerrera, C., & Elliot, L. (2009). The maze task: Measuring forced incremental sentence processing time. Behavior Research Methods, 41, 163-171.

Re-use

All aspects of this work are licensed under a Creative Commons Attribution 4.0 International License.


Course main page

XXProject maintained by kennysmithed Hosted on GitHub Pages — Theme by mattgraham