User:Rolig Loon/Quiz From Notecard
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
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
<lsl> //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 }
} } </lsl>