Skip to content

JavaScript: How to move through my question array

I am really stuck! As a newbie, I am trying to build a guessing game for Flags of the World. Here is what I’m trying to do:

  • I have an array of objects which I want to shuffle
  • I then want to select the correct country that matches the image
  • Add countries to three answer buttons below the image
  • I want to check for a correct/wrong answer
  • Add support for the user to click next which would load the next ‘correct’ country and image and two ‘incorrect’ countries from my array of objects.

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 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:

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 class="container">
        <div id="timer"></div>
        <div id="flag-container" class="flag"><img src="" id="flag"></div>
        <div id="answer-buttons" class="answer-box">
            <button class="btn" id="answer-1">Country 1</button>
            <button class="btn" id="answer-2">Country 2</button>
            <button class="btn" id="answer-3">Country 3</button>
        </div>
        <div id="result" class="result"></div>
        <!-- <div class="answer-box"><button class="start-btn" id="start-button">Start >></button></div> -->
        <div class="answer-box"><button class="next-btn hide" id="next-button">Next flag >></button></div>
        <div class="score">Score: <span id="correct" class="score">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!

Answer

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
}