wisconsinDemo.m

A copy is also included in PsychBench in <PsychBench folder>/demos, so you can run it from there.

<cdcm>% WISCONSIN CARD SORTING EXPERIMENT DEMO<cdcm>
<cdcm>% Coding method<cdcm>
<cdcm>% --------------------------------------------------<cdcm>
 
 
<cdcm>% Wisconsin card sorting task using a deck of 64 card images. Each card shows a<cdcm>
<cdcm>% number (1-4) of one of 4 shapes (circle, cross, star, triangle) in one of 4<cdcm>
<cdcm>% colors (red, green, blue, yellow) (4x4x4 = 64).<cdcm>
<cdcm>%<cdcm>
<cdcm>% Each trial shows one "test" card, and four "reference" cards arranged<cdcm>
<cdcm>% horizontally below it (see property settings below for sizes and positions).<cdcm>
<cdcm>% The test card is different in each trial. The set of four reference cards is<cdcm>
<cdcm>% the same across trials. The reference cards are chosen randomly for the<cdcm>
<cdcm>% experiment such that all four possibilities for number, color, and shape are<cdcm>
<cdcm>% in the set (e.g. 1 red circle, 2 green crosses, 3 blue stars, 4 yellow triangles).<cdcm>
<cdcm>% The subject responds by mouse click on one of the reference cards with which<cdcm>
<cdcm>% one matches the test card. The correct match is by one of three rules: number,<cdcm>
<cdcm>% shape, color. The match rule changes every 10 trials. The subject sees<cdcm>
<cdcm>% correct/incorrect feedback after each choice, and they need to figure out the<cdcm>
<cdcm>% rule by trial and error each time it changes. We run 60 trials, one for each<cdcm>
<cdcm>% of the 60 cards not among the four reference cards (i.e. reference cards are<cdcm>
<cdcm>% not test cards), all in random order. This corresponds to 2 10-trial blocks<cdcm>
<cdcm>% for each of the 3 rules, also in random order.<cdcm>
<cdcm>%<cdcm>
<cdcm>% https://en.wikipedia.org/wiki/Wisconsin_Card_Sorting_Test<cdcm>
 
 
 
newExperiment
 
 
 
<cdcm>%Card file names, copied from <PsychBench folder>/materials/wisconsin/wisconsin_list_matlab.txt.<cdcm>
<cdcm>%Could also just load them into the workspace using MATLAB readlines().<cdcm>
fileNames = [
    <cdsm>"wisconsin_1 blue circle.png"<cdsm>
    <cdsm>"wisconsin_1 blue cross.png"<cdsm>
    <cdsm>"wisconsin_1 blue star.png"<cdsm>
    <cdsm>"wisconsin_1 blue triangle.png"<cdsm>
    <cdsm>"wisconsin_1 green circle.png"<cdsm>
    <cdsm>"wisconsin_1 green cross.png"<cdsm>
    <cdsm>"wisconsin_1 green star.png"<cdsm>
    <cdsm>"wisconsin_1 green triangle.png"<cdsm>
    <cdsm>"wisconsin_1 red circle.png"<cdsm>
    <cdsm>"wisconsin_1 red cross.png"<cdsm>
    <cdsm>"wisconsin_1 red star.png"<cdsm>
    <cdsm>"wisconsin_1 red triangle.png"<cdsm>
    <cdsm>"wisconsin_1 yellow circle.png"<cdsm>
    <cdsm>"wisconsin_1 yellow cross.png"<cdsm>
    <cdsm>"wisconsin_1 yellow star.png"<cdsm>
    <cdsm>"wisconsin_1 yellow triangle.png"<cdsm>
    <cdsm>"wisconsin_2 blue circle.png"<cdsm>
    <cdsm>"wisconsin_2 blue cross.png"<cdsm>
    <cdsm>"wisconsin_2 blue star.png"<cdsm>
    <cdsm>"wisconsin_2 blue triangle.png"<cdsm>
    <cdsm>"wisconsin_2 green circle.png"<cdsm>
    <cdsm>"wisconsin_2 green cross.png"<cdsm>
    <cdsm>"wisconsin_2 green star.png"<cdsm>
    <cdsm>"wisconsin_2 green triangle.png"<cdsm>
    <cdsm>"wisconsin_2 red circle.png"<cdsm>
    <cdsm>"wisconsin_2 red cross.png"<cdsm>
    <cdsm>"wisconsin_2 red star.png"<cdsm>
    <cdsm>"wisconsin_2 red triangle.png"<cdsm>
    <cdsm>"wisconsin_2 yellow circle.png"<cdsm>
    <cdsm>"wisconsin_2 yellow cross.png"<cdsm>
    <cdsm>"wisconsin_2 yellow star.png"<cdsm>
    <cdsm>"wisconsin_2 yellow triangle.png"<cdsm>
    <cdsm>"wisconsin_3 blue circle.png"<cdsm>
    <cdsm>"wisconsin_3 blue cross.png"<cdsm>
    <cdsm>"wisconsin_3 blue star.png"<cdsm>
    <cdsm>"wisconsin_3 blue triangle.png"<cdsm>
    <cdsm>"wisconsin_3 green circle.png"<cdsm>
    <cdsm>"wisconsin_3 green cross.png"<cdsm>
    <cdsm>"wisconsin_3 green star.png"<cdsm>
    <cdsm>"wisconsin_3 green triangle.png"<cdsm>
    <cdsm>"wisconsin_3 red circle.png"<cdsm>
    <cdsm>"wisconsin_3 red cross.png"<cdsm>
    <cdsm>"wisconsin_3 red star.png"<cdsm>
    <cdsm>"wisconsin_3 red triangle.png"<cdsm>
    <cdsm>"wisconsin_3 yellow circle.png"<cdsm>
    <cdsm>"wisconsin_3 yellow cross.png"<cdsm>
    <cdsm>"wisconsin_3 yellow star.png"<cdsm>
    <cdsm>"wisconsin_3 yellow triangle.png"<cdsm>
    <cdsm>"wisconsin_4 blue circle.png"<cdsm>
    <cdsm>"wisconsin_4 blue cross.png"<cdsm>
    <cdsm>"wisconsin_4 blue star.png"<cdsm>
    <cdsm>"wisconsin_4 blue triangle.png"<cdsm>
    <cdsm>"wisconsin_4 green circle.png"<cdsm>
    <cdsm>"wisconsin_4 green cross.png"<cdsm>
    <cdsm>"wisconsin_4 green star.png"<cdsm>
    <cdsm>"wisconsin_4 green triangle.png"<cdsm>
    <cdsm>"wisconsin_4 red circle.png"<cdsm>
    <cdsm>"wisconsin_4 red cross.png"<cdsm>
    <cdsm>"wisconsin_4 red star.png"<cdsm>
    <cdsm>"wisconsin_4 red triangle.png"<cdsm>
    <cdsm>"wisconsin_4 yellow circle.png"<cdsm>
    <cdsm>"wisconsin_4 yellow cross.png"<cdsm>
    <cdsm>"wisconsin_4 yellow star.png"<cdsm>
    <cdsm>"wisconsin_4 yellow triangle.png"<cdsm>
    ];
 
<cdcm>% Reshape into a 4x4x4 (number x color x shape) string array for easier indexing below<cdcm>
fileNames = reshape(fileNames, [4 4 4]);
fileNames = permute(fileNames, [3 2 1]);
 
<cdcm>% Randomly select 4 reference cards with all 4 values for each of number, color, shape present.<cdcm>
<cdcm>% 4x3 matrix with rows containing [number, color, shape] indexes to fileNames above.<cdcm>
<cdcm>% Uses randomOrder() in <PsychBench folder>/tools.<cdcm>
nn_refNums      = randomOrder(1:4);
nn_refColors    = randomOrder(1:4);
nn_refShapes    = randomOrder(1:4);
    nn_refCards = zeros(4, 3);
<cdkm>for<cdkm> n = 1:4
    nn_refCards(n,:) = [nn_refNums(n) nn_refColors(n) nn_refShapes(n)];
<cdkm>end<cdkm>
 
<cdcm>% 4x2 matrix with rows containing reference card positions: 4 deg down from center (+y = down), centered horizontally, spaced horizontally 5 deg apart<cdcm>
refCardPositions = [
    -7.5    4
    -2.5    4
     2.5    4
     7.5    4
     ];
 
 
 
<cdcm>% TRIAL TEMPLATE<cdcm>
<cdcm>% ==========<cdcm>
    <cdcm>% Make objects and set only properties that will be the same in all trials where we will use the template...<cdcm>
 
 
    <cdcm>% Test card<cdcm>
    <cdcm>% ---<cdcm>
        testCard = pictureObject;
 
        <cdcm>% Position centered horizontally, 4 deg up<cdcm>
        testCard.position = [0 -4];
 
        <cdcm>% 5 deg height<cdcm>
        testCard.height = 5;
 
        <cdcm>% Default start at trial start; End at response<cdcm>
        testCard.end.response = true;
    <cdcm>% ---<cdcm>
 
 
    <cdcm>%Reference cards<cdcm>
    <cdcm>% ---<cdcm>
        <cdcm>% 1x4 array of picture objects<cdcm>
        refCards = pictureObject(4);
    <cdkm>for<cdkm> n = 1:4
        <cdcm>% Get file name from file names array using number, color, shape indexes<cdcm>
        refCards(n).fileName = fileNames(nn_refCards(n,1),nn_refCards(n,2),nn_refCards(n,3));
 
        <cdcm>% Get position from positions matrix<cdcm>
        refCards(n).position = refCardPositions(n,:);
 
        refCards(n).height = 5;
 
        refCards(n).end.response = true;
    <cdkm>end<cdkm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Mouse click response<cdcm>
    <cdcm>% ---<cdcm>
    response = mouseClickObject;
 
    <cdcm>% Define 4 areas on screen that can be clicked.<cdcm>
    <cdcm>% Centers = reference card positions, sizes [width height] = reference card sizes -> effect of click on reference cards.<cdcm>
    <cdcm>% Possible response values = area #s = reference card #s.<cdcm>
    response.areaPositions = refCardPositions;
    response.areaSize = [3.55 5];
 
    <cdcm>% Score correct/incorrect<cdcm>
    response.scoreResponse = true;
 
    <cdcm>% Default start at trial start.<cdcm>
    <cdcm>% By default mouseClick elements end when they record a response -> don't need to set .end.<cdcm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Feedback<cdcm>
    <cdcm>% ---<cdcm>
    <cdcm>% Two text objects<cdcm>
    feedbacks = textObject(2);
 
    <cdcm>% Green "YES" starts at correct response, shows for 1 sec<cdcm>
    feedbacks(1).text = <cdsm>"YES"<cdsm>;
    feedbacks(1).color = [0 1 0];
    feedbacks(1).fontSize = 1;
    feedbacks(1).start.correctResponse = true;
    feedbacks(1).end.duration = 1;
 
    <cdcm>% Red "NO" starts at incorrect response, shows for 1 sec<cdcm>
    feedbacks(2).text = <cdsm>"NO"<cdsm>;
    feedbacks(2).color = [1 0 0];
    feedbacks(2).fontSize = 1;
    feedbacks(2).start.incorrectResponse = true;
    feedbacks(2).end.duration = 1;
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Trial object<cdcm>
    <cdcm>% ---<cdcm>
    trial = trialObject;
 
    <cdcm>% (All properties not left at default will be specific to trial -> nothing more to set in template here)<cdcm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Add template with name "trial"<cdcm>
    addTemplate(testCard, refCards, response, feedbacks, trial, <cdsm>"trial"<cdsm>);
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% TASK TRIALS<cdcm>
<cdcm>% ==========<cdcm>
 
<cdcm>% Generate 64x3 matrix with rows = all possible combinations of 1-4 in each column.<cdcm>
<cdcm>% This can be done with tool setprod() in <PsychBench folder>/tools.<cdcm>
<cdcm>% -> 64x3 matrix with rows containing [number, color, shape] indexes to fileNames above.<cdcm>
nn_cards_test = setprod(1:4, 1:4, 1:4);
<cdcm>% Use MATLAB ismember() to remove the 4 reference card rows<cdcm>
tff = ismember(nn_cards_test, nn_refCards, <cdsm>"rows"<cdsm>);
nn_cards_test(tff,:) = [];
<cdcm>% Randomize order of rows<cdcm>
nn_cards_test = randomOrder(nn_cards_test, 1);
 
<cdcm>% Define 3 match rule names<cdcm>
ruleNames = [<cdsm>"number"<cdsm> <cdsm>"color"<cdsm> <cdsm>"shape"<cdsm>];
 
<cdcm>% Generate 1x6 vector match rule indexes with each match rule appearing x2, all in random order.<cdcm>
    nn_rules_test = randomOrder(rep(1:3, 2));
<cdcm>% Constrain that match rule must change each time.<cdcm>
<cdcm>% There is probably an elegant way to do this but may as well do by brute force: just try random orders until get one that works...<cdcm>
<cdkm>while<cdkm> any(nn_rules_test(1:end-1) == nn_rules_test(2:end))
    disp(<cdsm>'.'<cdsm>)
    nn_rules_test = randomOrder(rep(1:3, 2));
<cdkm>end<cdkm>
 
<cdcm>% 60 trial definitions numbered 1-60: 6 rules x 10 trial definitions each, 60 test cards distributed across them<cdcm>
        i_testCard = 0;
<cdkm>for<cdkm> n_rule = nn_rules_test
    <cdkm>for<cdkm> n_rep = 1:10
        i_testCard = i_testCard+1;
 
        ruleName = ruleNames(n_rule);
        n_testCard = nn_cards_test(i_testCard,:);
 
 
        <cdcm>% Start with objects as set in template "trial" above<cdcm>
        [testCard, refCards, response, feedbacks, trial] = getTemplate(<cdsm>"trial"<cdsm>);
 
 
        <cdcm>% Test card<cdcm>
        <cdcm>% ---<cdcm>
            <cdcm>% File name<cdcm>
            testCard.fileName = fileNames(n_testCard(1),n_testCard(2),n_testCard(3));
 
            <cdcm>% Set some information to see in experiment results output<cdcm>
            testCard.info.num = n_testCard(1);
            testCard.info.n_color = n_testCard(2);
            testCard.info.n_shape = n_testCard(3);
            testCard.report = <cdsm>"fileName"<cdsm>;
        <cdcm>% ---<cdcm>
 
 
        <cdcm>%Reference cards<cdcm>
        <cdcm>% ---<cdcm>
        <cdkm>for<cdkm> n = 1:4
            refCards(n).info.n = n;
            refCards(n).info.num = nn_refCards(n,1);
            refCards(n).info.n_color = nn_refCards(n,2);
            refCards(n).info.n_shape = nn_refCards(n,3);
            refCards(n).report = <cdsm>"fileName"<cdsm>;
        <cdkm>end<cdkm>
        <cdcm>% ---<cdcm>
 
 
        <cdcm>% Mouse click response<cdcm>
        <cdcm>% ---<cdcm>
        <cdcm>% Get correct response value (reference card #) based on match rule for the trial<cdcm>
        <cdkm>if<cdkm>      n_rule == 1
            <cdcm>% Number of shapes -> reference card # for number = test card number<cdcm>
            response.correctResponse = find(nn_refCards(:,1) == n_testCard(1), 1);
        <cdkm>elseif<cdkm>  n_rule == 2
            <cdcm>% Color -> reference card # for color = test card color<cdcm>
            response.correctResponse = find(nn_refCards(:,2) == n_testCard(2), 1);
        <cdkm>else<cdkm>    <cdcm>% 3<cdcm>
            <cdcm>% Shape -> reference card # for shape = test card shape<cdcm>
            response.correctResponse = find(nn_refCards(:,3) == n_testCard(3), 1);
        <cdkm>end<cdkm>
 
        <cdcm>% See response, correct response, score, latency in results<cdcm>
        response.report = [<cdsm>"response"<cdsm> <cdsm>"correctResponse"<cdsm> <cdsm>"responseScore"<cdsm> <cdsm>"responseLatency"<cdsm>];
        <cdcm>% ---<cdcm>
 
 
        <cdcm>% Feedback<cdcm>
        <cdcm>% ---<cdcm>
        <cdcm>% (Nothing to add)<cdcm>
        <cdcm>% ---<cdcm>
 
 
        <cdcm>% Trial object<cdcm>
        <cdcm>% ---<cdcm>
        <cdcm>% Set some information to see in experiment results output at trial level, not specific to any one element object<cdcm>
        trial.info.n_rule = n_rule;
        trial.info.ruleName = ruleName;
        <cdcm>% ---<cdcm>
 
 
        <cdcm>% Add trial definition with default numbering (1, 2, 3, ...)<cdcm>
        addTrial(testCard, refCards, response, feedbacks, trial);
    <cdkm>end<cdkm>
<cdkm>end<cdkm>
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% TRAINING TRIALS<cdcm>
<cdcm>% ==========<cdcm>
<cdcm>% Same as task trial definitions except no experiment results output...<cdcm>
 
<cdcm>% Use 10 random test cards<cdcm>
nn_cards_test = setprod(1:4, 1:4, 1:4);
nn_cards_test = randomChoose(nn_cards_test, 10, 1);
 
<cdcm>% Use 1 random match rule<cdcm>
n_rule = randomChoose(1:3);
 
<cdcm>% 10 trial definitions numbered 101-110: 1 rule x 10 trial definitions, 10 test cards distributed across them<cdcm>
    i_testCard = 0;
<cdkm>for<cdkm> n_rep = 1:10
    i_testCard = i_testCard+1;
 
    n_testCard = nn_cards_test(i_testCard,:);
 
 
    [testCard, refCards, response, feedbacks, trial] = getTemplate(<cdsm>"trial"<cdsm>);
 
 
    <cdcm>% Test card<cdcm>
    <cdcm>% ---<cdcm>
    testCard.fileName = fileNames(n_testCard(1),n_testCard(2),n_testCard(3));
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Reference cards<cdcm>
    <cdcm>% ---<cdcm>
    <cdcm>% (Nothing to add)<cdcm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Mouse click response<cdcm>
    <cdcm>% ---<cdcm>
    <cdkm>if<cdkm>      n_rule == 1
        response.correctResponse = find(nn_refCards(:,1) == n_testCard(1), 1);
    <cdkm>elseif<cdkm>  n_rule == 2
        response.correctResponse = find(nn_refCards(:,2) == n_testCard(2), 1);
    <cdkm>else<cdkm>    <cdcm>% 3<cdcm>
        response.correctResponse = find(nn_refCards(:,3) == n_testCard(3), 1);
    <cdkm>end<cdkm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Feedback<cdcm>
    <cdcm>% ---<cdcm>
    <cdcm>% (Nothing to add)<cdcm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Trial object<cdcm>
    <cdcm>% ---<cdcm>
    <cdcm>% (Nothing to add)<cdcm>
    <cdcm>% ---<cdcm>
 
 
    <cdcm>% Add trial definition to group 100 (101, 102, 103, ...)<cdcm>
    addTrial(testCard, refCards, response, feedbacks, trial, 100);
<cdkm>end<cdkm>
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% INTRO, PAUSE TRIALS<cdcm>
<cdcm>% ==========<cdcm>
<cdcm>% Intro.<cdcm>
<cdcm>% dialogue object to show text until subject presses any key.<cdcm>
<cdcm>% Can just set text and leave all properties at default.<cdcm>
text = dialogueObject;
<cdcm>% Each string in the array is a new line<cdcm>
text.text = [
    <cdsm>"Use the mouse to match the card on the top with one of the four cards on the bottom. Each time we will tell you whether you were right or wrong. The correct match is based on ONE of the following rules:"<cdsm>
    <cdsm>""<cdsm>
    <cdsm>"NUMBER of shapes (1, 2, 3, 4)"<cdsm>
    <cdsm>"COLOR (blue, green, red, yellow)"<cdsm>
    <cdsm>"SHAPE (circle, cross, star, triangle)"<cdsm>
    <cdsm>""<cdsm>
    <cdsm>"We won't tell you which rule it is. The rule lasts for a few choices and you need to figure it out by trial and error across those choices. From time to time the rule will change and you need to figure it out again when that happens."<cdsm>
    <cdsm>"We'll start with a few choices to get the idea. Press any key to continue..."<cdsm>
    ];
 
<cdcm>% Add trial definition with name "intro"<cdcm>
addTrial(text, <cdsm>"intro"<cdsm>);
 
 
<cdcm>% Pause.<cdcm>
<cdcm>% Use same object from above, just change the text.<cdcm>
text.text = <cdsm>"Okay! Remember the rule will change from time to time and you need to figure out the new rule when that happens. Press any key to begin..."<cdsm>;
 
<cdcm>% Add trial definition with name "pause"<cdcm>
addTrial(text, <cdsm>"pause"<cdsm>);
<cdcm>% ==========<cdcm>
 
 
 
<cdcm>% Set trial list:<cdcm>
<cdcm>%<cdcm>
<cdcm>% - intro<cdcm>
<cdcm>% - 10 training trial definitions<cdcm>
<cdcm>% - pause<cdcm>
<cdcm>% - 60 task trial definitions<cdcm>
<cdcm>%<cdcm>
<cdcm>% We implemented order in the definitions above just because it seemed easier.<cdcm>
<cdcm>% No repetitions needed.<cdcm>
nn = {<cdsm>"intro"<cdsm> 101:110 <cdsm>"pause"<cdsm> 1:60};
setTrialList(nn)
 
 
 
<cdcm>% You can call "viewExperiment" and "viewExperiment -d" to visualize trials<cdcm>
 
 
 
[results, resultsMatrix] = runExperiment;