How do I randomize quiz questions in actionscript3? - arrays

I have a quiz game in flash; basically you need to type the answer to answer 4 questions. In the end, it will show you the score and your answers vs. The correct answers.
I need help on how to randomize the questions (without repeating the question)
In the end the correct answer needs to match the order in which the player answered the question.
I attached the pictures below.
The code: Frame 1
stop();
var nQNumber:Number = 0;
var aQuestions:Array = new Array();
var aCorrectAnswers:Array = new Array("Jupiter", "Mars", "war", "Titan");
var aUserAnswers:Array = new Array();
aQuestions[0] = "What is the biggest planet in our solar system?";
aQuestions[1] = "Which planet in our solar system is the 4th planet from the
sun?";
aQuestions[2] = "Mars is named after the Roman god of ___.";
aQuestions[3] = "What is the name of Saturn's largest moon?";
questions_txt.text = aQuestions[nQNumber];
submit_btn.addEventListener(MouseEvent.CLICK, quiz);
function quiz(e:MouseEvent):void{
aUserAnswers.push(answers_txt.text);
answers_txt.text = "";
nQNumber++;
if(nQNumber < aQuestions.length){
questions_txt.text = aQuestions[nQNumber]}
else{
nextFrame()}
}
Frame 2
var nScore:Number = 0;
for(var i:Number = 0; i < aQuestions.length; i++){
this["userAnswer" + i + "_txt"].text = aUserAnswers[i];
this["correctAnswer" + i + "_txt"].text = aCorrectAnswers[i];
if(aUserAnswers[i].toUpperCase() == aCorrectAnswers[i].toUpperCase()){
nScore++}
if(i == aQuestions.length - 1){
score_txt.text = nScore.toString()}}

A common way to handle this, would be to create an array to hold your questions that need to be asked, randomize the array, then remove the corresponding question from the array at the time it is asked. Then when that array is empty, move on to your recap screen.
Here is one of many ways you could accomplish this:
First, let's simplify this by using objects instead of a whole bunch of arrays. Your object will have properties for all the relevant information
//create an array that will hold all your questions
var questions:Array = [];
//add a new question object to the array, repeat for all questions
questions.push({
question: "What is the biggest planet in our solar system?",
correctAnswer: "Jupiter"
userAnswer: null,
correct: false
});
Next, let's randomize that array:
//sort the array with the sort function below
questions.sort(randomizeArray);
//this sorts in a random way
function randomizeArray(a,b):int {
return(Math.random() > 0.5) ? 1: -1;
}
Now, let's copy the array to keep track of which questions need to be asked still
var askQuestions:Array = questions.concat(); //concat with no parameters returns a new shallow copy of the array
var curQuestion; //create a var to hold the current question
Now, create a function to ask the next question:
function askNextQuestion():void {
//check if there are any more questions to ask
if(askQuestions.length > 0){
//get the next question object
curQuestion = askQuestions.shift(); //shift removes the first item of an array, and returns that item
questions_txt.text = curQuestion.question;
answers_txt.text = "";
}else{
//all questions have been asked, show your recap screen
finish();
}
}
You'll need a function to run when you click the answer button:
function submitAnswer(e:Event = null):void {
//if there is a current question
if(curQuestion){
curQuestion.userAnswer = answers_txt.text;
curQuestion.correct = curQuestion.correctAnswer.toUpperCase() == answers_txt.text.toUpperCase();
}
//ask the next question
askNextQuestion();
}
And a function that runs when all questions have been asked:
function finish():void {
var score:int = 0;
//go through the array and count how many are correct and recap
for(var i:int=0; i<questions.length;i++){
if(questions[i].correct) score++;
trace("Question " + (i+1) + ":",questions[i].question); //arrays are 0 based, so we add 1 to the index (i) so that it says "Question 1:" for the first question instead of "Question 0:"
trace("You answered:",questions[i].userAnswer);
trace("Correct Answer:", questions[i].correctAnswer);
trace(questions[i].correct ? "You were correct" : "You were wrong","\n"); //this is shorthand if statement, \n is a line break
}
score_txt.text = score + " out of " + questions.length;
}
And of course, to get things started you just do: askNextQuestion()

Are you looking for the nifty feature called Math.random()? Multiply the result of Math.random() by your desired random generation range, round it, and select a random question in your array using the number.

Related

How to delete a word from an array if you got it 3 times in a row?

I'm doing my quiz app. I got a structured array that contains words, their translations, definitions, etc. After every answer, my app generates a random question. So, my question is how to delete a word from my array if user got it right for 3 times in a row.
How do I attach a counter to correctly answered word (that will be counting right answers for every single word in the array)?
I tried to triple my array and then delete this word, but it's a lot of coding and repetitive lines.
You can use this class to get the next question and update the array after a question has been answered correctly:
class QuestionProvider {
struct Question {
let question: String
let answer: String
var rightAnswers: Int
init(question: String, answer: String) {
self.question = question
self.answer = answer
self.rightAnswers = 0
}
}
private var questions = [Question]()
func add(question: Question) {
questions.append(question)
}
func getNextQuestion() -> Question {
let index = Int(arc4random_uniform(UInt32(questions.count)))
return questions[index]
}
func answeredCorrectly(question: Question) {
guard let index = questions.index(where: {$0.question == question.question}) else { return }
var qa = questions[index]
qa.rightAnswers += 1
if qa.rightAnswers >= 3 {
questions.remove(at: index)
} else {
questions[index] = qa
}
}
}

Retrieving datas within an Array

This is probably easy but I just cannot get the answer. Here is a simple Array:
I want the information to be distributed from an Input textBox to different dynamic textBox after a click. I am OK with buttons.
var ERLQ1:Array = ["ERLQ1", "N09°02.61 / E100°49.11", "ErawanLq"];
InputText = "ERLQ1";
//I want to display:
Txt1 = "ERLQ1" //Being first part of the array as main reference.
Txt2 = "N09°02.61 / E100°49.11" // Should be: String(ERLQ1[1])
Txt3 = "ErawanLq" // Should be: String(ERLQ1[2])
First time I write in a forum like this. Please forgive if not perfect. Thanks in advance.
Andre
If I understand correctly, an array of objects would work well. Since you have a set number of textfields I assume that you also have a set number of details that you want to display in them. If that is the case, this solution should work fine.
arr:Array = [{_name:"ERLQ1",ans1:"N09°02.61 / E100°49.11",ans2:"ErawanLq"},
{_name:"ERLQ2",ans1:"question 2 answer 1",ans2:"ques2ans1"}];
So, I don't really "get" your application, but if it were some sort of a quiz, you'd have a new array element for each question, and that element has a name, and two answers. Easy to modify it to grab answers from an answer pool. Now to find the element in the array that has ._name == "ERLQ1" you will need to loop through all the elements and return the one that has the ._name property that matches your search. Here is an example function:
private function matchName(arr:Array, term:String):int{
for (var i:int = 0; i < arr.length; i++){
if (arr[i]._name == term){
return i;
}
}
return -1;
}
This function will return the array index number of the matching term. If no match exists, it returns -1. So you could use it like this (pseudocode):
// on submit search{
// find the index number in the array of the element that matches the search term
var ind:int = matchName(arr, searchTerm);
// assign the textfield texts to the corresponding associated values
textBox1:text = arr[ind]._name;
textBox2:text = arr[ind].ans1;
textBox3:text = arr[ind].ans2;
}
I perhaps misunderstood (because of my English), but :
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
var ERLQ1:Array = ["ERLQ1", "N09°02.61 / E100°49.11", "ErawanLq"];
var Txt1 : TextField = new TextField();
Txt1.autoSize=TextFieldAutoSize.CENTER;
Txt1.type = TextFieldType.INPUT;
Txt1.border = true;
var Txt2 : TextField = new TextField();
Txt2.autoSize=TextFieldAutoSize.CENTER;
Txt2.type = TextFieldType.INPUT;
Txt2.border = true;
var Txt3 : TextField = new TextField();
Txt3.autoSize=TextFieldAutoSize.CENTER;
Txt3.type = TextFieldType.INPUT;
Txt3.border = true;
addChild(Txt1);
addChild(Txt2);
addChild(Txt3);
Txt1.x = 20, y =40;
Txt2.x = 180, y =40;
Txt1.x = 300, y =40;
Txt1.text = ERLQ1[0]; // is now : first part of the array as main reference (String(ERLQ1[0]).
Txt2.text = ERLQ1[1]; // is now : String(ERLQ1[1]);
Txt3.text = ERLQ1[2]; // is now : String(ERLQ1[2]);
This will display 3 TextFiels as input text like this :
If I misunderstood your question, please tell me more about what You expect!
Best regards.
Nicolas

How to find all sequences of three in an array of values

first question ever here...
I am coding a simple 3-card poker hand evaluator and am having problems finding/extracting multiple "straights" (sequential series of values) from an array of values.
I need to extract and return EVERY straight the array possibly has. Here's an example:
(assume array is first sorted numerically incrementing)
myArray = [1h,2h,3c,3h,4c]
Possible three-value sequences are:
[1h,2h,3c]
[1h,2h,3h]
[2h,3c,4c]
[2h,3h,4c]
Here is my original code to find sequences of 3, where the array contains card objects with .value and .suit. For simplicity in this question I just put "2h" etc here:
private var _pokerHand = [1h,2h,3c,3h,4c];
private function getAllStraights(): Array
{
var foundStraights:Array = new Array();
for (var i: int = 0; i < (_handLength - 2); i++)
{
if ((_pokerHand[i].value - _pokerHand[i + 1].value) == 1 && (_pokerHand[i + 1].value - _pokerHand[i + 2].value) == 1)
{
trace("found a straight!");
foundStraights.push(new Array(_pokerHand[i], _pokerHand[i + 1], _pokerHand[i + 2]));
}
}
return foundStraights;
}
but it of course fails when there are value duplicates (like the 3's above). I cannot discard duplicates because they could be of different suits. I need every possible straight as in the example above. This allows me to run the straights through a "Flush" function to find "straight flush".
What array iteration technique am I missing?
This is an interesting problem. Given the popularity of poker games (and Flash) I'm sure this has been solved many times before, but I couldn't find an example online. Here's how I would approach it:
Look at it like a path finding problem.
Begin with every card in the hand as the start of a possible path (straight).
While there are possible straights:
Remove one from the list.
Find all the next valid steps, (could be none, or up to 4 following cards with the same value), and for each next valid step:
If it reaches the goal (completes a straight) add it to a list of found straights.
Otherwise add the possible straight with the next step back to the stack.
This seems to do what you want (Card object has .value as int):
private function getAllStraights(cards:Vector.<Card>, straightLength:uint = 3):Vector.<Vector.<Card>> {
var foundStraights:Vector.<Vector.<Card>> = new <Vector.<Card>>[];
var possibleStraights:Vector.<Vector.<Card>> = new <Vector.<Card>>[];
for each (var startingCard:Card in cards) {
possibleStraights.push(new <Card>[startingCard]);
}
while (possibleStraights.length) {
var possibleStraight:Vector.<Card> = possibleStraights.shift();
var lastCard:Card = possibleStraight[possibleStraight.length - 1];
var possibleNextCards:Vector.<Card> = new <Card>[];
for (var i:int = cards.indexOf(lastCard) + 1; i < cards.length; i++) {
var nextCard:Card = cards[i];
if (nextCard.value == lastCard.value)
continue;
if (nextCard.value == lastCard.value + 1)
possibleNextCards.push(nextCard);
else
break;
}
for each (var possibleNextCard:Card in possibleNextCards) {
var possibleNextStraight:Vector.<Card> = possibleStraight.slice().concat(new <Card>[possibleNextCard]);
if (possibleNextStraight.length == straightLength)
foundStraights.push(possibleNextStraight);
else
possibleStraights.push(possibleNextStraight);
}
}
return foundStraights;
}
Given [1♥,2♥,3♣,3♥,4♣] you get: [1♥,2♥,3♣], [1♥,2♥,3♥], [2♥,3♣,4♣], [2♥,3♥,4♣]
It gets really interesting when you have a lot of duplicates, like [1♥,1♣,1♦,1♠,2♥,2♣,3♦,3♠,4♣,4♦,4♥]. This gives you:
[1♥,2♥,3♦], [1♥,2♥,3♠], [1♥,2♣,3♦], [1♥,2♣,3♠], [1♣,2♥,3♦], [1♣,2♥,3♠], [1♣,2♣,3♦], [1♣,2♣,3♠], [1♦,2♥,3♦], [1♦,2♥,3♠], [1♦,2♣,3♦], [1♦,2♣,3♠], [1♠,2♥,3♦], [1♠,2♥,3♠], [1♠,2♣,3♦], [1♠,2♣,3♠], [2♥,3♦,4♣], [2♥,3♦,4♦], [2♥,3♦,4♥], [2♥,3♠,4♣], [2♥,3♠,4♦], [2♥,3♠,4♥], [2♣,3♦,4♣], [2♣,3♦,4♦], [2♣,3♦,4♥], [2♣,3♠,4♣], [2♣,3♠,4♦], [2♣,3♠,4♥]
I haven't checked this thoroughly but it looks right at a glance.

Checking arrays in AS3

I'm collecting rows of answers from a database which are made in to arrays. Something like:
for (var i:int = 0; i < event.result.length; i++) {
var data = event.result[i];
var answer:Array = new Array(data["question_id"], data["focus_id"], data["attempts"], data["category"], data["answer"], data["correct"], data["score"]);
trace("answer: " + answer);
restoreAnswer(answer, i);
}
Now, if I trace answer, I typically get something like:
answer: 5,0,2,IK,1.a,3.1,0
answer: 5,0,1,IK,2.a,3.1,0
answer: 4,1,1,AK,3,3,2
From this we see that focus_id 0 (second array item) in question_id 5 (first array item) has been attempted twice (third array item), and I only want to use the last attempt in my restoreAnswer function.
My problem is that first attempt answers override the second attempts since the first are parsed last it seems. How do I go about only calling my restoreAnswer only when appropriate?
The options are:
1 attempts, correct score (2 points)
2 attempts, correct score (1 points)
1 attempt, wrong score (0 points)
2 attemps, wrong score (0 points)
There can be multiple focus_id in each question_id.
Thank you very much! :)
I would consider having the DB query return only the most recent attempt, or if that doesn't work efficiently, return the data in attempt order. You may score question 5 twice, but at least it'll score correctly on the last pass.
You can also filter or sort the data you get back from the server.
Michael Brewer-Davis suggested using the database query to resolve this; normally speaking, this would be the right solution.
If you wait until you get it back from the web method call or whatever in AS3, then consider creating an additional Vector variable:
var vAttempts:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(this.m_iNumQuestions);
You mentioned that it seems that everything is sorted so that earlier attempts come last. First you want to make sure that's true. If so, then before you do any call to restoreAnswer(), you'll want to check vAttempts to make sure that you have not already called restoreAnswer() for that question_id and focus_id:
if (!vAttempts[data["question_id"]])
{
vAttempts[data["question_id"]] = new Vector.<int>(); // ensuring a second dimension
}
if (vAttempts[data["question_id"]].indexOf(data["focus_id"]) == -1)
{
restoreAnswer(answer, i);
vAttempts[data["question_id"]].push(data["focus_id"]);
}
So optimizing this just a little bit, what you'll have is as follows:
private final function resultHandler(event:ResultEvent):void {
var vAttempts:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(this.m_iNumQuestions);
var result:Object = event.result;
var iLength:int = result.length;
for (var i:int = 0; i < iLength; i++) {
var data = result[i];
var iQuestionID:int = data["question_id"];
var iFocusID:int = data["focus_id"];
var answer:Array = [iQuestionID, iFocusID, data["attempts"],
data["category"], data["answer"], data["correct"], data["score"]];
trace("answer: " + answer);
var vFocusIDs:Vector.<int> = vAttempts[iQuestionID];
if (!vFocusIDs) {
vAttempts[iQuestionID] = new <int>[iFocusID];
restoreAnswer(answer, i);
} else if (vFocusIDs.indexOf(iFocusID) == -1) {
restoreAnswer(answer, i);
vFocusIDs.push(iFocusID);
}
}
}
Note: In AS3, Arrays can skip over certain indexes, but Vectors can't. So if your program doesn't already have some sort of foreknowledge as to the number of questions, you'll need to change vAttempts from a Vector to an Array. Also account for whether question IDs are 0-indexed (as this question assumes) or 1-indexed.

hitTestObject not hitTesting with all MovieClips

Hey guys having a little trouble this might be easier than i am making it out to be.
But the problem that i am having is when i hittest my mcPoints with my mcPlayer it is only interacting with 4 out of 5 of the movie clips that are added to the stage by a for loop.
I have been struggling with this for the past two days and cant seem to pin point the problem, everything seems set up perfectly but maybe you can help.
These are my Variables:
public var mcPoints:smallGainPoints;
private var nPoints:Number = 5;
private var aPointsArray:Array;
Here is how i set up the 5 mcPoints Movie Clips to be added to stage:
private function addPointsToStage():void
{
var startPoint:Point = new Point((stage.stageWidth / 2) - 100, stage.stageHeight / 2);
var spacing:Number = 50;
for (var i = 0; i < nPoints; i++)
{
trace(aPointsArray.length);
mcPoints = new smallGainPoints();
aPointsArray.push(mcPoints);
stage.addChild(mcPoints);
mcPoints.x = startPoint.x + (spacing * i);
mcPoints.y = startPoint.y;
}
}
So that adds the 5 points movie Clips to the stage which are aligned horizontally.
And finally here is the loop that listens for the HitTestObject to Initiate:
private function checkPlayerHitPoints():void
{
for (var i:int = 0; i < aPointsArray.length; i++)
{
//get current point in i loop
var currentPoints:smallGainPoints = aPointsArray[i];
//test if player is hitting current point
if(player.hitTestObject(currentPoints))
{
//Add points sound effects
var pointsSound:Sound = new pointsPickUpSound();
pointsSound.play();
//remove point on stage
currentPoints.destroyPoints()
//remove points from array
aPointsArray.splice(i, 1);
trace(aPointsArray.length);
//Add plus 5 text to current points position
mcPlus5 = new plusFiveText();
stage.addChild(mcPlus5);
mcPlus5.x = (currentPoints.x);
mcPlus5.y = (currentPoints.y);
//Update high score text
nScore += 5;
updateHighScore();
}
}
}
So i added traces both for when the movie clips are added and when they are hit here are the values i get:
0
1
2
3
4
Hit: 4
Hit: 3
Hit: 2
Hit: 1
Also i call the addPointsToStage(); in my constructor for more information.
So from the values im getting it seems that the last value "0" isn't being interacted with, any ideas why? Please anything would be of use. Thanks so much!
i'm not exactly sure what your code is suppose to be doing. But when you remove element from array in loop you are lose one item.
You array is
[1][2][3][4][5]
When i=1 you remove element and get
[1][3][4][5]
next interation i=2 which means you never test agains value 3.
You should decrement i whenever you remove element from an array.

Resources