Forum Replies Created
-
AuthorPosts
-
JeremyKeymasterHi,
At this point you’d be better off handling the shuffling part using javascript:
Template("dotmemorization.csv", row => newTrial("dot-baseline", defaultImage.size(40,40).css("border", "solid 1px gray") , newCanvas("grid", 200,200) , rs = [...new Array(25)].map((v,i)=>(i<row.nDots?true:false)) , fisherYates(rs) , rs.map((v,i)=>[ newImage('w'+i,'white.png').print(40*(i%5),40*Math.floor(i/5),getCanvas("grid"))[v?'hidden':'visible'](), newImage('b'+i,'black.png').print(40*(i%5),40*Math.floor(i/5),getCanvas("grid"))[v?'visible':'hidden']() ]) , getCanvas("grid", 200,200).center().print() , // display for X seconds newTimer("display-for-3", 3000) .start() .wait() , newCanvas("overGrid", 200,200).color("white").print(1,1,getCanvas("grid")) , newTimer("blank-for-3", 3000) .start() .wait() , (row.match=="False"?[ getImage('b'+rs.findIndex(v=>v===true)).hidden(), getImage('w'+rs.findIndex(v=>v===true)).visible(), getImage('b'+rs.findIndex(v=>v===false)).visible(), getImage('w'+rs.findIndex(v=>v===false)).hidden() ]:null) , getCanvas("overGrid").remove() , newText("prompt", "This grid matches the previous grid.") , newButton("True","True") , newButton("False","False") , newCanvas("choice", 200,200) .add(0, 0, getText("prompt")) .add(0, 50, getButton("True")) .add(50, 50, getButton("False")) .center() .print() , newVar("answer") , newSelector("choice") .add(getText("prompt")) .add(getButton("True")) .add(getButton("False")) .wait() .setVar("answer") .log() .test.selected( getButton(row.match) ) .failure( jump("no-interference") ) ) )Note that you need to add a column named
nDotsto your table, reporting how many cells on the grid should use black.png. I got rid of the Selector element, since you’re not making use of it (you’re not asking the participant to click on a cell). There also seems to be a bug with usinghiddenandvisibleon the Canvas element after using those same commands on the Image elements that it contains, so I resorted to printing a second, opaquely white Canvas element on top of it to mask its contentJeremy
April 11, 2022 at 10:50 am in reply to: html layout/event times/selector vs. scale/ compatibility #8030
JeremyKeymasterHi,
newVar("correct1").global().set(getVar("answer1")).set( v=>v==row.Correct1 )
Does this write in answer1 the answer only if it matches the one in the template?As you can see in your results file, the value of the Var element will be
trueif what the user typed matches what you indicated in the CSV file,falseotherwiseIf you want to condition your code on whether the participant typed the correct answer, just use
test.is(true)on the Var element and whatever you put insuccesswill run when they type the correct answer, otherwise what you put infailurewill runCould you suggest how to improve the syntax?
You could simply do
/^\S{1,6}$/to accept any sequence of 1 to 6 non-space characters (which would then include diacritic characters, but also other non-space characters like_or?for example)Jeremy
JeremyKeymasterApril 4, 2022 at 2:47 pm in reply to: html layout/event times/selector vs. scale/ compatibility #8011
JeremyKeymaster
JeremyKeymasterHi Inthat,
You might be interested in this guide on using Javascript in your code: you don’t need to create Var elements to execute different code depending on the value of the cells from the current row
Other than that, most of your trials do show a black square, but in some cases a white square is printed over the black square. For example, when
r5c1is 1 you print a black square at the bottom-left corner of the grid, and whenr5c5is 0 you also print a white square at the exact same position, resulting in a visually blank gridIndependently of that, I suspect that you mean to create 25 Image elements (one per cell on your grid) but you currently create two Image elements that you move around with each
test. My suggestion is, use Javascript ternary conditionals to create an Image element with the appropriate file for each cell:Template("dotmemorization.csv", row => newTrial("experimental-trial-pic", newSelector("input") , defaultImage.size(40,40).css("border", "solid 1px gray").selector("input") , newCanvas("grid", 200,200) // row 1 .add( 0, 0, row.r1c1==1?newImage("black.png"):newImage("white.png") ) .add( 40, 0, row.r1c2==1?newImage("black.png"):newImage("white.png") ) .add( 80, 0, row.r1c3==1?newImage("black.png"):newImage("white.png") ) .add(120, 0, row.r1c4==1?newImage("black.png"):newImage("white.png") ) .add(160, 0, row.r1c5==1?newImage("black.png"):newImage("white.png") ) // row 2 .add( 0, 40, row.r2c1==1?newImage("black.png"):newImage("white.png") ) .add( 40, 40, row.r2c2==1?newImage("black.png"):newImage("white.png") ) .add( 80, 40, row.r2c3==1?newImage("black.png"):newImage("white.png") ) .add(120, 40, row.r2c4==1?newImage("black.png"):newImage("white.png") ) .add(160, 40, row.r2c5==1?newImage("black.png"):newImage("white.png") ) // row 3 .add( 0, 80, row.r3c1==1?newImage("black.png"):newImage("white.png") ) .add( 40, 80, row.r3c2==1?newImage("black.png"):newImage("white.png") ) .add( 80, 80, row.r3c3==1?newImage("black.png"):newImage("white.png") ) .add(120, 80, row.r3c4==1?newImage("black.png"):newImage("white.png") ) .add(160, 80, row.r3c5==1?newImage("black.png"):newImage("white.png") ) // row 4 .add( 0, 120, row.r4c1==1?newImage("black.png"):newImage("white.png") ) .add( 40, 120, row.r4c2==1?newImage("black.png"):newImage("white.png") ) .add( 80, 120, row.r4c3==1?newImage("black.png"):newImage("white.png") ) .add(120, 120, row.r4c4==1?newImage("black.png"):newImage("white.png") ) .add(160, 120, row.r4c5==1?newImage("black.png"):newImage("white.png") ) // row 5 .add( 0, 160, row.r5c1==1?newImage("black.png"):newImage("white.png") ) .add( 40, 160, row.r5c2==1?newImage("black.png"):newImage("white.png") ) .add( 80, 160, row.r5c3==1?newImage("black.png"):newImage("white.png") ) .add(120, 160, row.r5c4==1?newImage("black.png"):newImage("white.png") ) .add(160, 160, row.r5c5==1?newImage("black.png"):newImage("white.png") ) .print() , newButton("press") .print() .wait() ) )Jeremy
April 3, 2022 at 10:23 pm in reply to: Target words preceded by a word randomly chosen among a specified set #8005
JeremyKeymasterHi,
What I would do is have one line per item, and one column listing all the possible prewords for that item, separated by a specific character. For example, using
.as the separating character, I would have this table:prewords,target,type she.he.it,discards,target her.their.his.your.our.my,disclaimers,target she,miscarried,filler she's.he's.they're.I'm.you're.we're,misconceiving,filler
And then generate my trials like this:
Template( "table.csv", row => newTrial( "trial" , prewords = row.prewords.split('.'), fisherYates(prewords) , newText(prewords[0]+" "+row.target).print() , newButton("Next") .print() .wait() ) .log("preword", prewords[0]) .log("target", row.target) )Jeremy
JeremyKeymasterHi,
I said it would freeze the experiment “if you don’t have enough items with each label to consistently break series of N items sharing the same label.” If your ratio is 1 experimental item for 2 fillers, the only constraint you have is you cannot go lower than series of 2 same-labeled trials, because then you would be forcing each filler to always be followed by at least one experimental item, but you would run out of experimental items after separating the first half of your filler items. Allowing series of 2 is fine though, because then you have enough items with both labels
Jeremy
JeremyKeymasterHi,
DebugOffhas been disabled for the demonstration link on the farm, but the debugger should never appear at the data-collection link. See this threadJeremy
JeremyKeymasterHi,
This is because the value of the global Var element named “correct” at that time is
"correct", because the previous trial set it to “correct.” ReplacenewVar("correct").global(),withnewVar("correct").global().set("NA"),if you want trials without a question to report “NA” insteadJeremy
March 30, 2022 at 11:04 am in reply to: html layout/event times/selector vs. scale/ compatibility #7993
JeremyKeymasterHi,
Can I impose a restriction on the blanks so that the participant cannot leave it empty, as well as a restriction that the participant cannot write something 20 characters long, I don’t know..to impose a range that the string the participant writes is between 1 and 6 characters long.
You could replace the ‘continue’ link of the Form controller with a Button element, and test the content of the Var elements in its
waitcommand:newController("Form", blank("It was so ea__ to get lost in an anc__ buil____")).print() , newButton("Continue").print().wait( getVar("answer1").test.is(v=>v.match(/^\w{1,6}$/)) .and( getVar("answer2").test.is(v=>v.match(/^\w{1,6}$/)) ) .and( getVar("answer3").test.is(v=>v.match(/^\w{1,6}$/)) ) .failure( newText("Please type between 1-6 characters in each box").print() ) )Just make sure you set
continueMessage: nullon the Form controller, either as a default, or by modifying the first line ofblank:blank = sentence => Object({continueMessage: null, html:Jeremy
March 29, 2022 at 1:33 pm in reply to: html layout/event times/selector vs. scale/ compatibility #7991
JeremyKeymasterHi,
Because now I get in the result something like:
blank1 = sy
blank2 = ient
blank3 = dingThis would be what you get from the code of the
blankfunction from your last message, right (egblank-1)? Because the code of theblankfunction I proposed would look something likeblank-12,blank-36andblank-43(the numbers correspond to the index in the string of each first_in a series)I am trying to code in a variable the expected answer so that I can compile the accuracy within the script.
Unless you need to show some feedback based on whether the participant answered accurately, it generally is a better idea to log raw responses and compute accuracy post data-collection. Logging raw responses is always a good idea anyway, and computing accuracy add unnecessary load during runtime
Now, assuming you always have exactly three textboxes, you could use global Var elements to store their content (by regularly inspecting the HTML elements, eg every 50ms) and log them as extra columns:
blank = sentence => Object({html: sentence.replace(/_+/g, (t,i)=>`<textarea name='blank${i}' rows=1 cols=${t.length+1} style='max-height: 1.7em'></textarea>`) }); newTrial("blank", newVar("answer1","").global(),newVar("answer2","").global(),newVar("answer3","").global() , newTimer("loop", 50).callback( getVar("answer1").set(v=>(document.querySelectorAll("textarea[name^='blank']")[0]||{value:v}).value ), getVar("answer2").set(v=>(document.querySelectorAll("textarea[name^='blank']")[1]||{value:v}).value ), getVar("answer3").set(v=>(document.querySelectorAll("textarea[name^='blank']")[2]||{value:v}).value ) , getTimer("loop").start() ).start() , newController("Form", blank("It was so ea__ to get lost in an anc__ buil____")) .print() .wait() ) .log("answer1", getVar("answer1")) .log("answer2", getVar("answer2")) .log("answer3", getVar("answer3"))Or, if you are using
Templateand have the expected responses in columns named, say, “Correct1,” “Correct2” and “Correct3”:Template( row => newTrial("blank", newVar("answer1","").global(),newVar("answer2","").global(),newVar("answer3","").global() , newTimer("loop", 50).callback( getVar("answer1").set(v=>(document.querySelectorAll("textarea[name^='blank']")[0]||{value:v}).value ), getVar("answer2").set(v=>(document.querySelectorAll("textarea[name^='blank']")[1]||{value:v}).value ), getVar("answer3").set(v=>(document.querySelectorAll("textarea[name^='blank']")[2]||{value:v}).value ) , getTimer("loop").start() ).start() , newController("Form", blank("It was so ea__ to get lost in an anc__ buil____")) .print() .wait() , newVar("correct1").global().set(getVar("answer1")).set( v=>v==row.Correct1 ), newVar("correct2").global().set(getVar("answer2")).set( v=>v==row.Correct2 ), newVar("correct3").global().set(getVar("answer3")).set( v=>v==row.Correct3 ) ) .log("answer1", getVar("answer1")) .log("answer2", getVar("answer2")) .log("answer3", getVar("answer3")) .log("correct1", getVar("correct1")) .log("correct2", getVar("correct2")) .log("correct3", getVar("correct3")) )Jeremy
JeremyKeymasterIt won’t be a problem that multiple participants take the the experiment at the same time: all the scripts and resources start loading at the beginning of the experiment (when the link is open) and then everything is executed locally, on the participant’s machine. Only when the end of the experiment is reached is the server contacted again, to submit the responses. The two first columns in the results file will report a timestamp for when the responses were received and a sequence of letters and digits unique to the browser and IP of the participant. It is quite unlikely that two students would submit at the exact same millisecond, but even if so, if each student uses a different device, the second column should report a different value anyway
Jeremy
JeremyKeymasterHi,
Indeed, you won’t need to use the Var method now that you’ve fixed the EventTime issue in R
.logis not a global PennController command: you either call it on a PennController element or on the closing parenthesis of anewTrialcommand. In either case,.logcomes immediately after a closing parenthesis (ignoring any space or linebreak character separating the parentehsis and the period). You get an error because you wrote.logafter a comma, the way you would insert a reference to an element usingnewXorgetXThis is how you would call
.logon the closing parenthesis ofnewTrial:newTrial("blocks-timer-trial", newText("blocks-text","Espere...") .cssContainer({ "font-size":"30px", "position": "absolute", "margin-top": "255px", //places it in the middle of the screen "white-space": "nowrap"}) .center() .print() , newTimer("blocks-text-timer",1500) .start() .wait() , getText("blocks-text").remove() , getVar("BLOCKS").global().set( v => Date.now() - v ) ) .log( "BlocksTimer" , getVar("BLOCKS") )Jeremy
JeremyKeymasterHi,
I actually don’t experience this problem for those trials (labeled “learning,” which lines 179-194 belong to) but I do experience it for the trials labeled “testing.” The
shufflecommand in those latter trials comes afterprinton the Canvas element, which is not hidden either at the time whenshuffleis executed, so that’s consistent with the issue as described in the other threadDo you experience the problem with both types of trials? If so, it might help to include a Timer of, say, 20ms between
getCanvas("learning_canvas").shuffle()andgetSelector("learning_selector").wait()if you don’t mind adding a very short delay inside your trialsJeremy
JeremyKeymasterHi,
Yes, you got it, that’s precisely the direction I had in mind when I suggested using the linear order of the rows rather than timestamps. Your solution seems to work beautifully
Re. your Var element: you correctly create it as a
.globalVar element at the end of the trial labeled “instructions_E” but when you refer back to it at the end of the trial labeled “blocks-timer-trial” you forget to call.global()again, so PennController cannot access the value of the Var element that you created in a previous trial. Then you call.log( "BlocksTimer" , getVar("BLOCKS") )on that same element, so you might have one results line for that trial (the one labeled “blocks-timer-trial”) that will report the value of the Var element. But given the two arguments you passed to.log, I think you meant to add a column rather than a row. If so, you need to calllogon the closing parenthesis ofnewTrial()rather than on the Var element itselfJeremy
-
AuthorPosts