diff --git a/reward_learning/config.json b/reward_learning/config.json new file mode 100644 index 000000000..a2806a344 --- /dev/null +++ b/reward_learning/config.json @@ -0,0 +1,46 @@ +[ + { + "name": "Reward Learning", + "template":"jspsych", + "run": ["static/js/jspsych/jspsych.js", + "static/js/jspsych/plugins/jspsych-text.js", + "static/js/jspsych/plugins/jspsych-call-function.js", + "static/js/jspsych/poldrack_plugins/jspsych-poldrack-text.js", + "static/js/jspsych/poldrack_plugins/jspsych-poldrack-instructions.js", + "static/js/jspsych/poldrack_plugins/jspsych-attention-check.js", + "static/js/jspsych/poldrack_plugins/jspsych-poldrack-single-stim.js", + "static/js/jspsych/poldrack_plugins/jspsych-poldrack-categorize.js", + "static/js/jspsych/plugins/jspsych-survey-text.js", + "experiment.js", + "static/css/jspsych.css", + "static/css/default_style.css", + "style.css", + "list.js" + ], + "exp_id": "reward_learning", + "cognitive_atlas_task_id": "", + "contributors": [ + "G. Elliott Wimmer", + "Ian Eisenberg", + "Zeynep Enkavi", + "Patrick Bissett", + "Vanessa Sochat", + "Russell Poldrack" + ], + "time":15, + "reference":"http://science.sciencemag.org/content/338/6104/270", + "publish":"True", + "experiment_variables": [{ + "name":"credit_var", + "type":"credit", + "datatype": "boolean", + "description":"True if avg_rt > 200" + }], + "deployment_variables":{"jspsych_init": + {"fullscreen": true, + "display_element": "getDisplayElement", + "on_trial_finish": "addID()"} + } + + } +] diff --git a/reward_learning/experiment.js b/reward_learning/experiment.js new file mode 100644 index 000000000..9b68ed88d --- /dev/null +++ b/reward_learning/experiment.js @@ -0,0 +1,499 @@ +/* ***************************************** */ +/* Define helper functions */ +/* ***************************************** */ + + +/* get subject number entry, load stimuli, and report back to subject */ +var getSubjnum = function () { + var subjnum_trial = jsPsych.data.getLastTrialData() + var subjstring = subjnum_trial.responses[7] + subjnum_trial.responses[8] + subjnum_trial.responses[9] + var subjnum = Number(subjstring); + var stimArray = picArray[subjnum]; + var subjcode = subjcodeArray[subjnum]; + var listerrortext = []; + if (typeof stimArray == "undefined") { + stimArray = picArray[0]; + listerrortext = "

Participant number

" + subjnum + " not found!

Please quit and check the participant number!!

" + } else { + listerrortext = "

Participant number

" + subjnum + ", with codeword " + subjcode + " found!

Press enter to continue.

" + } + jsPsych.pluginAPI.preloadImages(stimArray); + answers = genLearnphasestims(stimArray) + return listerrortext +}; + + +var getSubjreport = function () { + var listerrortext = []; + if (typeof stimArray == "undefined") { + stimArray = picArray[0]; + listerrortext = "

Participant number

" + numtemp + " not found!

Please quit and check the participant number!!

" + } else { + stimArray = picArray(subjnum); + listerrortext = "

Participant number

" + numtemp + " found!

Press enter to continue.

" + } + return listerrortext +}; + + + +var getInstructFeedback = function() { + return '

' + feedback_instruct_text + + '

' +} + +function getDisplayElement() { + $('
').appendTo('body') + return $('
').appendTo('body') +} + +function addID() { + jsPsych.data.addDataToLastTrial({exp_id: 'reward_learning'}) +} + +var getStim = function() { + stim = learnPhaseStimsComplete.image.pop() + curr_data = learnPhaseStimsComplete.data.pop() + return stim +} + +var getResponse = function() { + return answers.answers.pop() +} + +var getYes = function() { + var throwaway = answers.nofdbk.pop() + return answers.yesfdbk.pop() +} + +var getNo = function() { + var throwaway = answers.yesfdbk.pop() + return answers.nofdbk.pop() +} + +var getMissed = function() { + var throwaway = answers.yesfdbk.pop() + throwaway = answers.nofdbk.pop() + return "
Too late! -$0.50
" +} + +var getITIdurstim = function() { + return itilist.itistim.pop() +} + +var getITIdurresp = function() { + return itilist.itiresp.pop() +} + + +var getITI = function() { + gap = ( Math.floor(Math.random() * 5000) + 500 ) + return gap +} + + +var genITIs = function () { + var ititemp = []; + for(var i = 0; i < Learn_trials; i++){ + ititemp.push( Math.floor(Math.random() * 5000) + 500 ); + }; + var itilist = []; + var itistim = []; + var itiresp = []; + for(var j = 0; j < Learn_trials; j++){ + itistim.push(ititemp[j]); + itiresp.push(ititemp[j]); + } + return { + itilist: itilist, + itistim: itistim, + itiresp: itiresp + }; +}; + + + +// generate a random list of numbers to add / subtract to the feedback amounts +var feedbacknoise = jsPsych.randomization.repeat([0.01, -0.01, 0.02, -0.02, 0.03, -0.03, 0.04, -0.04, 0.05, -0.05, 0.01, -0.01, 0.02, -0.02, 0.03, -0.03, 0.04, -0.04, 0.05, -0.05, 0.01, -0.01, 0.02, -0.02, 0.03, -0.03, 0.04, -0.04, 0.05, -0.05, 0.01, -0.01, 0.02, -0.02, 0.03, -0.03, 0.04, -0.04, 0.05, -0.05],1); + + +var genResponses = function(stimuli) { + var answers_stim1 = jsPsych.randomization.repeat([38, 38, 38, 38, 40], + eachRepNum / 10); + var answers_stim2 = jsPsych.randomization.repeat([40, 40, 40, 40, 38], + eachRepNum / 10); + var answers_stim3 = jsPsych.randomization.repeat([38, 38, 38, 38, 40], + eachRepNum / 10); + var answers_stim4 = jsPsych.randomization.repeat([40, 40, 40, 40, 38], + eachRepNum / 10); + var answers_stim5 = jsPsych.randomization.repeat([38, 38, 38, 38, 40], + eachRepNum / 10); + var answers_stim6 = jsPsych.randomization.repeat([40, 40, 40, 40, 38], + eachRepNum / 10); + var answers_stim7 = jsPsych.randomization.repeat([38, 38, 38, 38, 40], + eachRepNum / 10); + var answers_stim8 = jsPsych.randomization.repeat([40, 40, 40, 40, 38], + eachRepNum / 10); + + var count1 = 0; + var count2 = 0; + var count3 = 0; + var count4 = 0; + var count5 = 0; + var count6 = 0; + var count7 = 0; + var count8 = 0; + + var answers = []; + var yesfdbk = []; + var nofdbk = []; + + for (var i = 0; i < Learn_trials; i++) { + + if (stimuli.data[i].condition === 'stim1') { + answers.push(answers_stim1[count1]); + if (answers[i] == choices[0]) { + yesnumb = 0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.05 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + nonumb = 0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + yesnumb = -0.05 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + } + count1 = count1 + 1; + } else if (stimuli.data[i].condition === 'stim2') { + answers.push(answers_stim2[count2]); + if (answers[i] == choices[1]) { + yesnumb = -0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = 0.00 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + yesnumb = 0.00 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } + count2 = count2 + 1; + } else if (stimuli.data[i].condition === 'stim3') { + answers.push(answers_stim3[count3]); + if (answers[i] == choices[0]) { + yesnumb = 0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.05 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + nonumb = 0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + yesnumb = -0.05 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + } + count3 = count3 + 1; + } else if (stimuli.data[i].condition === 'stim4') { + answers.push(answers_stim4[count4]); + if (answers[i] == choices[1]) { + yesnumb = -0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = 0.00 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + yesnumb = 0.00 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } + count4 = count4 + 1; + } else if (stimuli.data[i].condition === 'stim5') { + answers.push(answers_stim5[count5]); + if (answers[i] == choices[0]) { + yesnumb = 0.45 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.05 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + nonumb = 0.45 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + yesnumb = -0.05 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + } + count5 = count5 + 1; + } else if (stimuli.data[i].condition === 'stim6') { + answers.push(answers_stim6[count6]); + if (answers[i] == choices[1]) { + yesnumb = -0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = 0.00 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + yesnumb = 0.00 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } + count6 = count6 + 1; + } else if (stimuli.data[i].condition === 'stim7') { + answers.push(answers_stim7[count7]); + if (answers[i] == choices[0]) { + yesnumb = 0.45 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.05 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + nonumb = 0.45 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + yesnumb = -0.05 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + } + count7 = count7 + 1; + } else { + answers.push(answers_stim8[count8]); + if (answers[i] == choices[1]) { + yesnumb = -0.25 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = 0.00 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } else { + yesnumb = 0.00 + feedbacknoise[i]; yesnumb = yesnumb.toFixed(2); + nonumb = -0.25 + feedbacknoise[i]; nonumb = nonumb.toFixed(2); + } + count8 = count8 + 1; + } + if (answers[i] == choices[0] & stimuli.data[i].optimal_response == choices[0]) { + yesfdbk.push("
" + yesnumb + "!
"); + nofdbk.push("
" + nonumb + "!
"); + } else if (answers[i] == choices[1] & stimuli.data[i].optimal_response == choices[0]) { + yesfdbk.push("
" + yesnumb + "!
"); + nofdbk.push("
" + nonumb + "!
"); + } else if (answers[i] == choices[1] & stimuli.data[i].optimal_response == choices[1]) { + yesfdbk.push("
" + yesnumb + "!
"); + nofdbk.push("
" + nonumb + "!
"); + } else if (answers[i] == choices[0] & stimuli.data[i].optimal_response == choices[1]) { + yesfdbk.push("
" + yesnumb + "!
"); + nofdbk.push("
" + nonumb + "!
"); + } + } + return { + answers: answers, + yesfdbk: yesfdbk, + nofdbk: nofdbk + }; +}; + + + +var getPrevresponse = function() { + var choice_trial = jsPsych.data.getLastTrialData() + var keypress = choice_trial.key_press + var rt = choice_trial.rt + if (keypress == -1) { + return getMissed + } else if (keypress == 38 ) { + return getYes + } else if (keypress == 40 ) { + return getNo + } +}; + + + +/*************************************************************************/ +/* DEFINE EXPERIMENTAL VARIABLES */ +/*************************************************************************/ +// task specific variables +var choices = [38, 40] +var curr_data = [] +var stim = '' +// specify the number of trials in the learning phase +var Learn_trials = 40; +var eachRepNum = 5; + + +var genLearnphasestims = function (stimArray) { + var stims = [['stim1', stimArray[0], choices[0]], + ['stim2', stimArray[1], choices[1]], + ['stim3', stimArray[2], choices[0]], + ['stim4', stimArray[3], choices[1]], + ['stim5', stimArray[4], choices[0]], + ['stim6', stimArray[5], choices[1]], + ['stim7', stimArray[6], choices[0]], + ['stim8', stimArray[7], choices[1]], + ['Yes', stimArray[8], 101], + ['No', stimArray[9], 102]] + learnPhaseStims = []; + for (var i = 0; i<8; i++) { + var list_stim = {} + list_stim.image = "
" + list_stim.data = { + trial_id: 'stim', + exp_stage: 'learning', + condition: stims[i][0], + optimal_response: stims[i][2] + } + learnPhaseStims.push(list_stim) + } + learnPhaseStimsComplete = jsPsych.randomization.repeat(learnPhaseStims, eachRepNum, true); + var answers = genResponses(learnPhaseStimsComplete) + return answers +}; + +var curr_data = '' + + +var itilist = genITIs() + + + +/* ************************************ */ +/* Set up jsPsych blocks */ +/* ************************************ */ + +// set up pre-task entry of subject number +var pre_task_block = { + type: 'survey-text', + data: { + trial_id: "subject number entry" + }, + questions: ['

Please enter your 3-digit participant number:

'], + rows: [1, 1], + columns: [3, 3] +}; + +// set up post task questionnaire +var post_task_block = { + type: 'survey-text', + data: { + trial_id: "post task questions" + }, + questions: ['

On a scale of 1 (easy) - 10 (very difficult), rate how difficult the session was today.

', + '

On a scale of 1 (awake) - 10 (very tired), rate how tired you felt during the session today.

'], + rows: [15, 15], + columns: [60,60] +}; + +/* define static blocks */ + +// introduction +var feedback_instruct_text = + 'Welcome back to the experiment! This part will take about 5 minutes. Press enter to review the instructions.' +var learning_instruct_block = { + type: 'poldrack-text', + data: { + trial_id: "instruction" + }, + cont_key: [13], + text: getInstructFeedback, + timing_post_trial: 0, + timing_response: 180000000 +}; + + +// subject number processing and picture list creation +var learning_participantexists = { + type: 'poldrack-text', + data: { + trial_id: "instruction" + }, + cont_key: [13], + text: getSubjnum, + timing_post_trial: 0, + timing_response: 180000000 +}; + + +// instructions part 1 +var instructions_block = { + type: 'poldrack-instructions', + data: { + trial_id: "instruction" + }, + pages: [ + '

Today you will continue learning about the lucky and unlucky scenes from the first session. As a reminder, you will see each scene with a "Yes" option above and "No" option below. For each scene, you must choose one option by using the up or down arrow key.

Each scene has a different chance of being "lucky". Your task is to maximize your winnings ($) by betting "Yes" on lucky scenes and betting "No" on unlucky scenes.

', + ], + allow_keys: false, + show_clickable_nav: true, + timing_post_trial: 1000 +}; + + +var learning_phase_trials = { + type: 'poldrack-categorize', + stimulus: getStim, + key_answer: getResponse, + choices: choices, + show_stim_with_feedback: false, + correct_text: "
", + incorrect_text: "
", + timeout_message: "
", + timing_stim: 1500, + timing_response: 1500, + timing_feedback_duration: 1000, + response_ends_trial: false, + timing_post_trial: 0, + is_html: true +}; + + +var learning_phase_feedback = { + type: 'poldrack-single-stim', + choices: 'none', + response_ends_trial: false, + stimulus: getPrevresponse, + is_html: true, + data: { + trial_id: "feedback", + exp_stage: "learn" + }, + timing_post_trial: 0, + timing_stim: 750, + timing_response: 750 +} + + +var learning_phase_itis = { + type: 'poldrack-single-stim', + choices: 'none', + response_ends_trial: false, + stimulus: "
+
", + is_html: true, + data: { + trial_id: "fixation_white", + exp_stage: "learn" + }, + timing_post_trial: 0, + timing_stim: getITIdurstim, + timing_response: getITIdurresp +} + + + +var learning_phase_prefix = { + type: 'poldrack-single-stim', + stimulus: "
+
", + is_html: true, + choices: 'none', + response_ends_trial: false, + data: { + trial_id: "fixation_black", + exp_stage: "learn" + }, + timing_post_trial: 0, + timing_stim: 250, + timing_response: 250 +} + + + +var learning_phase_start = { + type: 'poldrack-text', + timing_response: 1800000, + data: { + trial_id: "learning_phase_intro" + }, + text: '

Get ready!

Press Enter when you are ready.

', + cont_key: [13] +}; + +var end_block = { + type: 'poldrack-text', + data: { + trial_id: "end", + exp_id: 'reward_learning' + }, + timing_response: 180000, + text: '

Finished with this task!

Press enter to continue.

', + cont_key: [13] +}; + + + +/* create experiment definition array */ +var reward_learning_experiment = []; +reward_learning_experiment.push(pre_task_block); +reward_learning_experiment.push(learning_participantexists); +reward_learning_experiment.push(learning_instruct_block); +reward_learning_experiment.push(instructions_block); +reward_learning_experiment.push(learning_phase_start); +for(var i = 0; i