Jeremy

Forum Replies Created

Viewing 15 posts - 481 through 495 (of 1,522 total)
  • Author
    Posts
  • in reply to: Creating visual stimuli from a csv #8031
    Jeremy
    Keymaster

    Hi,

    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 using hidden and visible 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 content

    Jeremy

    in reply to: html layout/event times/selector vs. scale/ compatibility #8030
    Jeremy
    Keymaster

    Hi,

    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 otherwise

    If 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 in success will run when they type the correct answer, otherwise what you put in failure will run

    Could 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

    in reply to: Randomize DashedSentence #8029
    Jeremy
    Keymaster
    Jeremy
    Keymaster

    Hi,

    This is a known issue, we are working on fixing it. Apologies for the inconvenience

    Jeremy

    in reply to: Creating visual stimuli from a csv #8010
    Jeremy
    Keymaster

    Hi 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 when r5c5 is 0 you also print a white square at the exact same position, resulting in a visually blank grid

    Independently 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

    Jeremy
    Keymaster

    Hi,

    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

    in reply to: Pseudo-randomising stimulus presentation #8004
    Jeremy
    Keymaster

    Hi,

    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

    in reply to: DebugOff() not working #7999
    Jeremy
    Keymaster

    Hi,

    DebugOff has been disabled for the demonstration link on the farm, but the debugger should never appear at the data-collection link. See this thread

    Jeremy

    in reply to: Accuracy of the final trial w/o comprehension question #7996
    Jeremy
    Keymaster

    Hi,

    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.” Replace newVar("correct").global(), with newVar("correct").global().set("NA"), if you want trials without a question to report “NA” instead

    Jeremy

    in reply to: html layout/event times/selector vs. scale/ compatibility #7993
    Jeremy
    Keymaster

    Hi,

    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 of blank:

    blank = sentence => Object({continueMessage: null, html: 

    Jeremy

    in reply to: html layout/event times/selector vs. scale/ compatibility #7991
    Jeremy
    Keymaster

    Hi,

    Because now I get in the result something like:

    blank1 = sy
    blank2 = ient
    blank3 = ding

    This would be what you get from the code of the blank function from your last message, right (eg blank-1)? Because the code of the blank function I proposed would look something like blank-12, blank-36 and blank-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

    in reply to: Can we randomize time with newTimer() ? #7986
    Jeremy
    Keymaster

    It 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

    in reply to: Can we randomize time with newTimer() ? #7984
    Jeremy
    Keymaster

    Hi,

    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 a newTrial 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 using newX or getX

    This is how you would call .log on the closing parenthesis of newTrial:

    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

    in reply to: Selector shuffle making images flash #7981
    Jeremy
    Keymaster

    Hi,

    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 after print on the Canvas element, which is not hidden either at the time when shuffle is executed, so that’s consistent with the issue as described in the other thread

    Do 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() and getSelector("learning_selector").wait() if you don’t mind adding a very short delay inside your trials

    Jeremy

    in reply to: Can we randomize time with newTimer() ? #7979
    Jeremy
    Keymaster

    Hi,

    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 call log on the closing parenthesis of newTrial() rather than on the Var element itself

    Jeremy

Viewing 15 posts - 481 through 495 (of 1,522 total)