Home > Software engineering >  JavaScript: How to move through my question array
JavaScript: How to move through my question array

Time:03-07

I am really stuck! As a newbie, I am trying to build a guessing game for Flags of the World. I have an array of objects which I want to shuffle. I then want to select the correct country that matches the image and add that to three answer buttons. Currently I am doing this by creating variables using a fixed index from the shuffled array for the currentFlagIndex (correct) and wrongFlagIndex (wrong) and anotherWrongIndex (also wrong). I then want to check for a 'correct' or 'wrong' answer and then for the user to click 'next' which would load the next 'correct' country and image and two 'incorrect' countries from my array of objects. I can make this all work once, as per the code below, but I am completely stuck on how to move to the 'next' question. I am running this all against a 60 second timer. I have the timer counting down, but have not attempted to put a check in to ensure this is not at zero (I will do that once I know how to move my questions on).

Flags Array extract:

const flags = [
{ 
    image: 'assets/images/ad.webp',
    country: 'Andorra',
},
{ 
    image: 'assets/images/ae.webp',
    country: 'United Arab Emirates',
},
{ 
    image: 'assets/images/af.webp',
    country: 'Afghanistan',
},

Game JavaScript:

const startButton = document.getElementById('start-button');
const nextButton = document.getElementById('next-button');
const answerButtons = document.getElementById('answer-buttons');

nextButton.addEventListener('click', buildNextQuestionArray);
/**
 * Set 60 second countdown timer. Code modified from Grepper: https://www.codegrepper.com/code-examples/javascript/add countdown timer to javascript quiz
 */
let count = 60;
let interval = setInterval(function () {
    document.getElementById('timer').innerHTML = count;
    count--;
    if (count === 0) {
        clearInterval(interval);
        document.getElementById('timer').innerHTML = 'GAME OVER'; // this is where I can add what to do once the timer ends - take to the GAME OVER score html page
    }
}, 1000);

/**
 * Function to randomly sort array modified from: https://www.codegrepper.com/code-examples/javascript/how to randomly sort an array javascript 
 */
function createNewFlags(array) {
    var currentIndex = array.length,
        temporaryValue, randomIndex;
    while (0 !== currentIndex) {
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }
    return array;
}

let newFlags = createNewFlags(flags);
console.log(newFlags[0].country);

let currentFlagIndex = 0;
console.log(newFlags[currentFlagIndex]);

let wrongFlagIndex = 12;
console.log(newFlags[wrongFlagIndex]);

let anotherWrongFlagIndex = 21;
console.log(newFlags[anotherWrongFlagIndex]);

/** 
 * Create an array of the correct answer and two wrong answers
 **/
function buildFullAnswerArray() {
    let fullAnswerArray = []; {
        fullAnswerArray.push(newFlags[currentFlagIndex].country);
        fullAnswerArray.push(newFlags[wrongFlagIndex].country);
        fullAnswerArray.push(newFlags[anotherWrongFlagIndex].country);
    }
    return fullAnswerArray
}

let allAnswers = buildFullAnswerArray();
console.log(allAnswers);

/** 
 * Shuffle the allAnswers array so that the order of the countries in the answers will be randomised
 **/
function createShuffledAnswers(array) {
    var currentIndex = array.length,
        temporaryValue, randomIndex;
    while (0 !== currentIndex) {
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }
    return array;
}

let finalAnswers = createShuffledAnswers(allAnswers);
console.log(finalAnswers);


    document.getElementById('flag').src = newFlags[currentFlagIndex].image;

    let answer1 = document.getElementById('answer-1');
    let answer2 = document.getElementById('answer-2');
    let answer3 = document.getElementById('answer-3');

    answer1.innerText = finalAnswers[0];
    answer2.innerText = finalAnswers[1];
    answer3.innerText = finalAnswers[2];

    answer1.addEventListener('click', checkAnswer);
    answer2.addEventListener('click', checkAnswer);
    answer3.addEventListener('click', checkAnswer);


/** 
 * Check button onclick whether correct answer or not - event listener
 * If correct - return 'CORRECT!' and change the body color to green
 * If incorrect - return 'WRONG!' and change the body color to red
 **/

function checkAnswer() {
    if (this.textContent === newFlags[currentFlagIndex].country) {
        let correct = true
        let correctAnswer = `CORRECT!`
        document.getElementById('result').innerHTML = correctAnswer;
        setStatusClass(document.body, correct);
        increaseScore();
    } else {
        let wrong = false
        let wrongAnswer = `WRONG!`
        document.getElementById('result').innerHTML = wrongAnswer;
        setStatusClass(document.body, wrong);
    }
    nextButton.classList.remove('hide');
    answerButtons.classList.add('hide');
}

/**
 * Gets the current score from the DOM and increments it by 1
 */
function increaseScore() {
    let currentScore = parseInt(document.getElementById('correct').innerText);
    document.getElementById('correct').innerText =   currentScore;
}
/**
 * Adds a class to the body depending on whether the answer is correct or wrong. Allows the body color to be changed depending on correct or wrong answers.
 * Adapted from Web Dev Simplified YouTube Video: https://www.youtube.com/watch?v=riDzcEQbX6k
 */
function setStatusClass(element, correct) {
    clearStatusClass(element)
    if (correct) {
        element.classList.add('correct')
    } else {
        element.classList.add('wrong')
    }
}

/**
 * Resets class status on body. Used when setting the nextQuestion()
 * Adapted from Web Dev Simplified YouTube Video: https://www.youtube.com/watch?v=riDzcEQbX6k
 */
function clearStatusClass(element) {
    element.classList.remove('correct')
    element.classList.remove('wrong')
}

function resetState() {
    clearStatusClass(document.body);
    nextButton.classList.add('hide');
}

// function buildNextQuestionArray() {
//     currentFlagIndex =   currentFlagIndex;
//     wrongFlagIndex =   wrongFlagIndex;
//     anotherWrongFlagIndex =   anotherWrongFlagIndex;
//     let nextQuestionArray = []; {
//         nextQuestionArray.push(newFlags[currentFlagIndex].country);
//         nextQuestionArray.push(newFlags[wrongFlagIndex].country);
//         nextQuestionArray.push(newFlags[anotherWrongFlagIndex].country);
//     }
//     return nextQuestionArray
// }

// let nextAnswers = buildNextQuestionArray();
// console.log(nextAnswers);

I have commented out the above code as it will generate a new array of three but it also causes the current correct answer to return wrong, so must be changing the variables.

I think I have a problem where I am inserting the answer text and image outside of a function, but I have tried many things here, which all return errors.

Game HTML:

 <!-- GAME AREA -->
    <div >
        <div id="timer"></div>
        <div id="flag-container" ><img src="" id="flag"></div>
        <div id="answer-buttons" >
            <button  id="answer-1">Country 1</button>
            <button  id="answer-2">Country 2</button>
            <button  id="answer-3">Country 3</button>
        </div>
        <div id="result" ></div>
        <!-- <div ><button  id="start-button">Start >></button></div> -->
        <div ><button  id="next-button">Next flag >></button></div>
        <div >Score: <span id="correct" >0</span></div>
    </div>

    <!-- SCRIPTS -->
    <script src="assets/js/flags.js"></script>
    <script src="assets/js/game.js"></script>

I have a solution where I can add two incorrect answers to my flags array, but that seems like a long-hand (there are 216 flags!) way to 'solve' this problem. I have all of the parts, I just need someone to help me move through the array to the next flag please!

The latest version of the full code is also deployed here: Git Hub

I am sure there is a lot wrong with my code, but any straightforward help to move me on would be much appreciated!

CodePudding user response:

So you want to display the next question. If you think about it this statement can be broken down into few part.

  1. Update the <image> src so that the next flag is displayed
  2. Update the options being displayed.
  3. Update the checkAnswer function to have the correct value in the variable currentFlagIndex.

well you have already figured out the logic for this. Look at these lines from your code.

    document.getElementById('flag').src = newFlags[currentFlagIndex].image;

    let answer1 = document.getElementById('answer-1');
    let answer2 = document.getElementById('answer-2');
    let answer3 = document.getElementById('answer-3');

    answer1.innerText = finalAnswers[0];
    answer2.innerText = finalAnswers[1];
    answer3.innerText = finalAnswers[2];

    answer1.addEventListener('click', checkAnswer);
    answer2.addEventListener('click', checkAnswer);
    answer3.addEventListener('click', checkAnswer);

They give you exactly what you want. Now the next step would be to copy these lines into the buildNextQuestionArray (I know this function is supposed to just return the next set of options but you execute this function when next button is clicked so I figured to put these here)

This gets you over your immediate hurdle. But still the function buildNextQuestionArray is incomplete and you have work more on this. I hope able to make a lovely game in the end.

CodePudding user response:

So you are trying to find a way to:

  • Generate the wrong answer flags(instead of hard coding it)
  • Regenerating a new game
  • Restart the X timer

Good question there are multiple ways to complete this.

One route would be to move/swap(swap items for faster Big O if you are worried about speed) the selected flags to the end of the array. (For most of the logic you will be reusing the random generated logic)

Flags c=correct s=selected

[s,x,x,c,x,s,x,...] => [x,x,x,x,...,s,c,s]

Now when generating the next correct flag for a new game you would reuse the random generated logic and reduce the length of the flags array by one (so you don't choose the same flag as the previous game but I'll leave that to you).

I have a snippet of logic here, you just need to restart the timer: https://jsfiddle.net/pmt3Ls2q/11/

function generateChoosenFlags(flagSize = flags.length) {
  // [wrong, wrong, correctFlag]?
    let choosenFlags = [];
  
  let randomFlagIndex = getRandomFlagIndex();
  let randomFlag = getFlag(randomFlagIndex);
  swapFlags(randomFlagIndex, flagSize - 1)
  choosenFlags.push(randomFlag);

  randomFlagIndex = getRandomFlagIndex(flags.length - 1);
  randomFlag = getFlag(randomFlagIndex);
  swapFlags(randomFlagIndex, flagSize - 2)
  choosenFlags.push(randomFlag);
  
  randomFlagIndex = getRandomFlagIndex(flags.length - 2);
  randomFlag = getFlag(randomFlagIndex);
  swapFlags(randomFlagIndex, flagSize - 3)
  choosenFlags.push(randomFlag); 

    return choosenFlags;
}

function getFlag(index) {
    return flags[index];
}

function getRandomFlagIndex(flagSize) {
    if(!flagSize){
    flagSize = flags.length;
  }
  
    return Math.floor(Math.random() * flagSize);
}

function swapFlags(indexFrom, indexTo) {
    const temp = flags[indexFrom];

    flags[indexFrom] = flags[indexTo];
  flags[indexTo] = temp;
}

function generateNewGame() {

 // If we wanted to prevent generating the same answer then we would change the size
 // to something like so:  generateChoosenFlags(flags.length - 1); But you will need to update the logic for that...
 const generatedFlags =  generateChoosenFlags();
 populateQuestion(generatedFlags);
 
 return generatedFlags;
}

function populateQuestion(generatedFlags) {
    let choosenFlagIndex = getRandomFlagIndex(generatedFlags.length);
    let choosenFlag = generatedFlags[choosenFlagIndex];
  choosenAnswer = choosenFlag;
  
  document.getElementById('flag').src = choosenFlag.image;
  let answerObjects = [
    document.getElementById('answer-1'), 
    document.getElementById('answer-2'), 
    document.getElementById('answer-3')
  ];
  
  for(var i = 0; i < generatedFlags.length; i  ) {
    answerObjects[i].innerHTML = generatedFlags[i].country;
  }  
  
    console.log(choosenAnswer);
}

function checkAnswer(id) {
    const isCorrect = document.getElementById(id).innerHTML == choosenAnswer.country;
// Restart timer
}
  • Related