Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterHi,
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
nDots
to 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 usinghidden
andvisible
on 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 #8030Jeremy
KeymasterHi,
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
true
if what the user typed matches what you indicated in the CSV file,false
otherwiseIf 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 insuccess
will run when they type the correct answer, otherwise what you put infailure
will 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
Jeremy
KeymasterApril 4, 2022 at 2:47 pm in reply to: html layout/event times/selector vs. scale/ compatibility #8011Jeremy
KeymasterJeremy
KeymasterHi 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
r5c1
is 1 you print a black square at the bottom-left corner of the grid, and whenr5c5
is 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 #8005Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi,
DebugOff
has been disabled for the demonstration link on the farm, but the debugger should never appear at the data-collection link. See this threadJeremy
Jeremy
KeymasterHi,
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 #7993Jeremy
KeymasterHi,
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
wait
command: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: null
on 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 #7991Jeremy
KeymasterHi,
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
blank
function from your last message, right (egblank-1
)? Because the code of theblank
function I proposed would look something likeblank-12
,blank-36
andblank-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
Template
and 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
Jeremy
KeymasterIt 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
Jeremy
KeymasterHi,
Indeed, you won’t need to use the Var method now that you’ve fixed the EventTime issue in R
.log
is not a global PennController command: you either call it on a PennController element or on the closing parenthesis of anewTrial
command. In either case,.log
comes immediately after a closing parenthesis (ignoring any space or linebreak character separating the parentehsis and the period). You get an error because you wrote.log
after a comma, the way you would insert a reference to an element usingnewX
orgetX
This is how you would call
.log
on 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
Jeremy
KeymasterHi,
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
shuffle
command in those latter trials comes afterprint
on the Canvas element, which is not hidden either at the time whenshuffle
is 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
Jeremy
KeymasterHi,
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
.global
Var 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 calllog
on the closing parenthesis ofnewTrial()
rather than on the Var element itselfJeremy
-
AuthorPosts