I have an array of 1000 data, but for some reason, I can only access 39.9% of these data.
This issue arises in a reaction time game that I am coding using a JavaScript library called jsPsych. In the game, a user must click a button before an image disappears from their screen. The game is rigged so that the user maintains a 20% success rate across trials.
I accomplish this rigging using ex-Gaussian data. Specifically, I have an array of 1000 ex-Gaussian data, and at the start of each trial, I choose a number from the bottom 20% of these data to represent how long the image will last on the user's screen. The result is that the user will have only a 20% chance of responding to the image quickly enough. The goal of this process is to force the user to have a 20% success rate across trials.
If the user's ongoing success rate happens to be greater than 20%, I decrease the likelihood of succeeding in each trial by 5% until the success rate is 0.2. Similarly, if the user's ongoing success rate is less than 20%, I increase the likelihood of succeeding in a trial by 5% until the success rate is 0.2.
The problem is that I cannot increase the success likelihood past 0.399. It should be able to go all the way up to 1.0, in which case I would be sampling from all of my ex-Gaussian data. However, if I allow my game to run without touching the button, the success likelihood increases in increments of 0.5 - as it should - until it reaches 0.399. Then, it stops increasing.
I do not know why that is happening, or why the success likelihood is even able to be 0.399 when it is supposed to increase in increments of 0.5.
You can view my code below. Please note that you would need to check the data I log to the console to get a clear picture of the problem I am having.
// this is just some supporting code
const jsPsych = initJsPsych();
const game = {};
// the ex-Gaussian data, sorted in ascending order
const dist = [];
for (let i = 0; i < 1000; i ) {
dist.push(jsPsych.randomization.sampleExGaussian(180, 20, 1, positive = true))
}
dist.sort(function(a, b) {
return a - b
})
console.log("Array of ex-Gaussian data:" dist)
// initial success likelihood = 20%
let success_likelihood = 0.2;
game.timeline = [{
timeline: [{
// wait for image to appear
type: jsPsychHtmlButtonResponse,
stimulus: "<div style='visibility:hidden;'>image</div>",
choices: ["Click me!"],
trial_duration: 1000,
response_ends_trial: false,
// place feedback element here, albeit hidden, to keep page formatting consistent
prompt: "<div>Click the button as soon as the image appears.</div><div style='visibility:hidden; background-color:green; padding:20px;'>Success</div>",
},
{
// show image
type: jsPsychHtmlButtonResponse,
stimulus: "<div>image</div>",
choices: ["Click me!"],
trial_duration: function() {
// player must click button within trial duration to succeed
// player has x% chance of success on each trial
// sample trial duration from lower x% of rt distribution
let cutoff = 1000 * success_likelihood;
// subtract cutoff - 1 because array index starts at 0
return dist[cutoff - 1];
},
data: {
type: "game"
},
prompt: "<div>Click the button as soon as the image appears.</div><div style='visibility:hidden; background-color:green; padding:20px;'>Success</div>",
},
{
// feedback
type: jsPsychHtmlButtonResponse,
stimulus: "<div style='visibility:hidden;'>image</div>",
choices: ["Click me!"],
trial_duration: 2000,
response_ends_trial: false,
prompt: function() {
var last_trial = jsPsych.data.getLastTrialData();
var rt = last_trial.trials[0].rt;
// if rt === null, player failed to press button within span of trial
if (rt != null) {
return "<div>Click the button as soon as the image appears.</div><div style='background-color:green; padding:20px;'>Success</div>";
} else {
return "<div>Click the button as soon as the image appears.</div><div style='background-color:red; padding:20px;'>Missed</div>";
}
},
}
],
on_timeline_finish: function() {
// re-evaluate success rate after each trial
let total_trials = jsPsych.data.get().filter({
type: "game"
});
let success_trials = total_trials.select('rt').subset(function(x) {
return x != null;
});
let success_rate = success_trials.count() / total_trials.count();
console.log("Success rate:" success_rate)
// adjust success likelihood -5% until it reaches 0.2
if (success_rate > 0.2) {
success_likelihood -= 0.05;
} else if (success_rate < 0.2) {
success_likelihood;
}
console.log("Success likelihood:" success_likelihood)
},
loop_function: function(data) {
// stop game once player attains 12 success trials
let success_trials = jsPsych.data.get().filter({
type: "game"
}).select('rt').subset(function(x) {
return x != null;
}).count();
if (success_trials != 12) {
return true;
} else {
return false;
}
}
}]
// run code
jsPsych.run([game])
<!DOCTYPE html>
<html lang="en-CA">
<head>
<meta charset="UTF-8" />
<title>Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/[email protected]/dist/index.browser.min.js"></script>
<link href="https://unpkg.com/[email protected]/css/jspsych.css" rel="stylesheet" />
<script src="https://unpkg.com/@jspsych/[email protected]/dist/index.browser.min.js"></script>
<script src="https://unpkg.com/@jspsych/[email protected]/dist/index.browser.min.js"></script>
</head>
<body></body>
</html>
Does anyone know why the access to my array is restricted?
CodePudding user response:
success_likelihood only gets smaller, never bigger according to this?
if (success_rate > 0.2) {
success_likelihood -= 0.05;
} else if (success_rate < 0.2) {
success_likelihood;
}
CodePudding user response:
First of all, you are not increasing your success_likelyhood
at all, but that is probably only a typo.
Your second problem is this piece of code
let cutoff = 1000 * success_likelihood;
return dist[cutoff - 1];
Because of how floating point arithmetic works, sooner or later your success_likelihood
will be a number that has more than 3 decimal places. And when that happens, 1000 * success_likelihood
will be something like 399.999999
and thus dist[cutoff -1]
will return undefined
which causes your code to stop. Use
let cutoff = Math.round(1000 * success_likelihood)