User:Rolig Loon/Quiz From Notecard

From Second Life Wiki
< User:Rolig Loon
Revision as of 08:41, 25 January 2015 by ObviousAltIsObvious Resident (talk | contribs) (<lsl> tag to <source>)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

A multiple-choice testing script that reads questions and answer choices from a notecard and presents them in dialog boxes.

Features

* Allows only one student at a time to take the quiz.
* Gives immediate feedback to the student when a dialog button is pushed.
* Does not allow student to retake the quiz.
* Stores quiz scores and gives instructor a report on chat command.
* Quiz can be timed.

Script

//A Dialog-driven quiz, using text from a notecard -- Rolig Loon -- October 2009
//
// Free for public use -- please don't do something crass like selling my script.
// Modify if you must, but please keep these header lines intact.  Be nice.
//
// Notecard format:
// A line beginning with a "Q" is part of a question
// A line beginning with an "A" is a string of comma-delimited zeros (wrong) and a one (right) to identify the answer
// A line beginning with a "#" is a comment
// Blank lines are ignored
//
// Dialog boxes are limited to 512 characters, so make each question (including choices) short enough to fit.
// There is no limit to the number of questions in a quiz, and you may have up to 12 answer choices per question
//
// =============== sample notecard ===========
//# Lines starting with Q appear verbatim in a dialog box
//# You may have as many Q lines as you want per question, but only one A line
//# Interpreted data begins immediately after the lead character in a line
//QWhat is the capital of Minnesota?
//Q1. St. Paul
//Q2. Minneapolis
//Q3. Iowa City
//Q4. Boston
//A1,0,0,0
//QHow many fingers are on my right hand?
//Q1. One
//Q2. Two
//Q3. Three
//Q4. Four
//Q5. Five
//A0,0,0,0,1
// ================= end of sample =================
//
// Instructor types "results" in channel 24 to get a report of all student scores

string gCard; //Notecard name
integer gLine; //Current line being read
key gQID; //Dataserver key
integer gtouch; //Activates/deactivates touch_start event 
list gAvList = []; //Cumulative list of people who have taken this quiz
string gAv; //Name of the current quiz-taker
key gAvKey; //Key of the current quiz-taker
integer CHAN; //Channel for dialog communication
integer Handle; //Listen handle for dialog
integer gScore; //This quiz-taker's score
string gQuestion; //Text for the current question
list gAnswers; //Answer key for the current question
list gAllScores = []; //Cumulative list of scores for quiz-takers
integer Timespan = 10;  // This is the maximum time alloted for the quiz, in minutes. Change here if needed. 

integer IsNameOnList(list namelist, string name)  //Verifies whether av has already taken the quiz
{
    integer i;
    integer len = llGetListLength(namelist);
    for (i=0; i<=len-1;++i)
        if(llList2String(namelist,i) == name)
        {
            return TRUE;
        }
    return FALSE;
}

init()  //Resets parameters for the next quiz-taker
{
    llSetTimerEvent(0);
    gtouch = 0;
    gAv = "";
    gAvKey = NULL_KEY;
    gLine = 0;
    gAllScores += gScore;
    gScore = 0;
    llListenControl(Handle,FALSE);
}

list order_buttons(list buttons)
{
return llList2List(buttons, -3, -1) + llList2List(buttons, -6, -4)
+ llList2List(buttons, -9, -7) + llList2List(buttons, -12, -10);
}

default
{
    state_entry()
    {
        gCard = llGetInventoryName(INVENTORY_NOTECARD,0);
        gAvList = [];
        CHAN = (integer)(llFrand(100000000))* (-1);
        Handle = llListen(CHAN,"","","");
        llListen(24,"",llGetOwner(),"");
        init();
    }

    touch_start(integer total_number)
    {
        if (gtouch == 0)  // Starting quiz with a new person
        {
            if(IsNameOnList(gAvList,llDetectedName(0)))
            {
                llInstantMessage(llDetectedKey(0),"Sorry, "+ llDetectedName(0) + ". You have already taken the quiz. You cannot take it twice.");
                return;
            }
            else
            {
                gAvList += llDetectedName(0); // Add av to the list of people who have attempted this quiz
            }
            gAv = llDetectedName(0);
            gAvKey = llDetectedKey(0);
            llInstantMessage(gAvKey,"Hello, "+ gAv+ ". You will have "+(string)Timespan+" minutes to finish this quiz. Respond to questions as they appear in blue dialog boxes on your screen.");
            llInstantMessage(gAvKey,"Touch this panel again to stop the quiz.");
            llSetTimerEvent(Timespan*60);
        }
        if (gAv != llDetectedName(0)) //Only accept touches from this av until the quiz is finished
        {
            llInstantMessage(llDetectedKey(0),"Someone else is taking the quiz now.  Please wait.");
            return;
        }
        if (gtouch >=1) // This is the emergency stop. Av wants to stop taking the quiz before the last question
        {
            llListenControl(Handle,TRUE);
            llDialog(gAvKey,"If you stop now, you may not restart later. \nDo you want to QUIT now?", ["YES", "NO"],CHAN);
            return;
        }
        // An av should only reach this point if it is the first touch
        gQID = llGetNotecardLine(gCard,gLine); //Read the first line of the notecard
        ++gtouch;
    }

    changed(integer change)
    {
        if(change & CHANGED_INVENTORY)
        {
            llResetScript();
        }
    }

timer()
{
    llInstantMessage(gAvKey,"Your time is up. Thank you for taking the quiz. Your score is "+ (string)gScore);
    init(); //Restart the quiz for the next person
}

dataserver(key query_id, string data)
{
    if(query_id == gQID) //If the data request came from this script
    {
        if(data != EOF) //If there is still data to be read from the notecard
        {
            if(llGetSubString(data,0,0) == "#"||llGetSubString(data,0,0) == "") //Ignore comment lines and blank lines
            {
                ++gLine;
                gQID = llGetNotecardLine(gCard,gLine);
            }
            else if (llGetSubString(data,0,0) == "Q") //Read the question and all answer choices
            {
                gQuestion += llGetSubString(data,1,-1) + "\n"; //Format each "Q" line as a new line in the dialog box
                ++gLine;
                gQID = llGetNotecardLine(gCard,gLine);
            }
            else if (llGetSubString(data,0,0) == "A") //Read the answer key
            {
                gAnswers = llParseString2List(llGetSubString(data,1,-1),[","],[]);
                integer len = llGetListLength(gAnswers);
                integer i;
                list buttons = [];
                for (i=1;i<=len;++i) //Create a numbered button for each choice
                {
                    buttons += [(string)i];
                }
                llListenControl(Handle,TRUE);
                llDialog(gAvKey,gQuestion,order_buttons(buttons),CHAN); //Display the question in a dialog box
            }
        }
        else // If there are no more lines on the notecard
        {
            llInstantMessage(gAvKey,"You have finished the quiz. Congratulations. Your score is "+(string)gScore);
            init(); //Restart the quiz for the next person
        }
    }
}

listen (integer channel, string name, key id, string message)
{
    if (channel == 24) //Teacher said something on channel 24
    {
        if (llToLower(message) == "results") //and the message was "results"
        {
            integer len = llGetListLength(gAvList);
            integer i;
            for (i=0;i<=len-1;++i)
            {
                llOwnerSay(llList2String(gAvList,i) +  "....  Score = " + llList2String(gAllScores,i+1));
            }
        }
    }
    else if (message == "YES") // Av has touched the panel and wants to quit
    {
        llInstantMessage(gAvKey,"You have left the quiz with a score of "+(string)gScore +". Goodbye!");
        init();
    }
    else if(message == "NO") // Av has touched the panel and does NOT want to quit
    {
        return;
    }
    else // A question has been displayed in a dialog box
    {
        integer pos = llListFindList(gAnswers,["1"]); //Search the answer key. The correct answer is pos+1
        if(message == (string)(pos+1))
        {
            ++gScore;
            llInstantMessage(gAvKey,"Correct! Your score is now "+ (string)gScore+ ". Next ...");
        }
        else if (pos != -1)
        {
            llInstantMessage(gAvKey,"Wrong. The correct answer was "+ (string)(pos+1) + ". Next ....");
        }
        else if (pos == -1) //The teacher screwed up and didn't code a correct answer
        {
            llInstantMessage(gAvKey,"Ooops! There is no right answer to this question. Let's move on....");
        }
        gQuestion = ""; //Erase the current question
        gAnswers = []; //And its answer key
        ++gLine;
        gQID = llGetNotecardLine(gCard,gLine); // Get the next question
    }
}
}