Search code examples
javascriptlogicmultiplication

Game logic error , users choses correct choices but donst get point


This Game is a multiplication game where player gets 4 options to choose from for example 4*5 and user has to choose between 20 and 3 other wrong answers but once user selects the right option it doesn't increment the score although correct answer === users choice, i am new to JavaScript so please give full explanation to why the logic isn't working . This is the HTML

"use strict";

const resetBtn = document.querySelector(".reset");
const Navscore = document.querySelector(".ScoreNumber");
const scoreToReset = document.querySelector(".score");
const question = document.querySelector(".question");
const highScoreNumber = document.querySelector(".highScoreNumber");
const choices1 = document.getElementById("1");
const choices2 = document.getElementById("2");
const choices3 = document.getElementById("3");
const choices4 = document.getElementById("4");
const answer1 = document.querySelector(".answer1");
const answer2 = document.querySelector(".answer2");
const answer3 = document.querySelector(".answer3");
const answer4 = document.querySelector(".answer4");
const choices = document.querySelector(".choices");
let score = 0;
let Highscore = 0;
const numberForNumberGen = function() {
  return Math.trunc(Math.random() * 12 + 1);
};
const choicesAssinger = function(usersChoice) {
  // document.getElementById(correct).innerHTML = questionGen();
  console.log(`user choice ${usersChoice}`);
  const answerText = questionGen();
  const correct = numberGen(4);
  console.log(`correct choice ${correct}`);
  if (correct === 1) {
    answer1.innerHTML = answerText;
    answer2.innerHTML = answerText + numberForNumberGen();
    answer3.innerHTML = answerText - numberForNumberGen();
    answer4.innerHTML =
      answerText + numberForNumberGen() - numberForNumberGen();
  } else if (correct === 2) {
    answer2.innerHTML = answerText;
    answer1.innerHTML = answerText - numberForNumberGen();
    answer4.innerHTML = answerText - numberForNumberGen();
    answer3.innerHTML =
      answerText + numberForNumberGen() - numberForNumberGen();
  } else if (correct === 3) {
    answer3.innerHTML = answerText;
    answer4.innerHTML = answerText + numberForNumberGen();
    answer2.innerHTML = answerText - numberForNumberGen();
    answer1.innerHTML =
      answerText + numberForNumberGen() - numberForNumberGen();
  } else if (correct === 4) {
    answer4.innerHTML = answerText;
    answer3.innerHTML = answerText + numberForNumberGen();
    answer1.innerHTML = answerText - numberForNumberGen();
    answer2.innerHTML =
      answerText + numberForNumberGen() - numberForNumberGen();
  }
  // return correct;
  console.error(correct);
  console.error(usersChoice);

  console.log(correct == usersChoice);
  if (correct == usersChoice) {
    console.log("correct");
    document.querySelector("body").style.background = "green";
    score++;
    questionGen();
    choicesAssinger();
    Navscore.innerHTML = score;
    if (score > Highscore) {
      Highscore = score;
      highScoreNumber.innerHTML = Highscore;
    }
  } else {
    if (score !== 0) {
      document.querySelector("body").style.background = " #925e36";
      Navscore.innerHTML = "Please click Reset";
      choices1.removeEventListener("click", handler);
      choices2.removeEventListener("click", handler);
      choices3.removeEventListener("click", handler);
      choices4.removeEventListener("click", handler);
    }
  }
  // return rightChoiceNumber;
};
const start = () => {
  // choicesAssinger();
  choices1.addEventListener("click", handler);
  choices2.addEventListener("click", handler);
  choices3.addEventListener("click", handler);
  choices4.addEventListener("click", handler);
};
const numberGen = function(n) {
  const number = Math.trunc(Math.random() * n + 1);

  return number;
};

const questionGen = function() {
  const num1 = numberGen(numberForNumberGen());
  const num2 = numberGen(numberForNumberGen());
  const answer = num1 * num2;
  console.log(answer);
  const questionWriting = `${num1} x ${num2}`;
  question.innerHTML = questionWriting;
  return answer;
};

function handler(event) {
  const usersChoice = event.target.id;

  choicesAssinger(usersChoice);
}

resetBtn.addEventListener("click", () => {
  start();
  document.querySelector("body").style.background = " #925e36";
  score = 0;
  Navscore.innerHTML = 0;
  questionGen();
});
questionGen();
// choicesAssinger();
start();
<nav>
  <div class="reset utilites">Reset</div>
  <div class="score utilites">
    Score: <span class="ScoreNumber"> 0</span>
  </div>
  <div class="highScore utilites">
    High Score: <span class="highScoreNumber"> 0</span>
  </div>
</nav>
<section>
  <div class="choices" id="1">
    🚪
    <div class="answer answer1">answer</div>
  </div>
  <div class="choices" id="2">
    🚪
    <div class="answer answer2">answer</div>
  </div>
  <div class="choices" id="3">
    🚪
    <div class="answer answer3">answer</div>
  </div>
  <div class="choices" id="4">
    🚪
    <div class="answer answer4">answer</div>
  </div>
</section>
<div class="question">---</div>


Solution

  • I was having difficulty understanding what the specific issue is with the code you provided because I did see my score change to 1 when I got a correct answer. However, upon getting a correct answer, the background of the HTML body turned to brown (hex: #925e36) whereas I think you intend for it to turn green.

    The truth is, the body actually does turn green, but it quickly (imperceptibly to the eye because it is so fast) turns brown. This is because within the same code block that sets the body to green, you are calling choicesAssinger(); with no argument. This resets the options and, because there is no argument, sets the usersChoice to undefined - meaning the user will never have the correct option and so the body will switch to brown because score !== 0.

    I am not sure that there is a small change that can correct your logic. Overall, I find the code very hard to read and reason about because it is all so deeply connected (coupled).

    I really want to help you to simplify this solution, but I don't think I can do so without significantly re-writing the code.

    I would approach this by:

    • Encapsulating the state (the values that change) into a single object.
    • Having a single render function that will render the HTML according to the state object.
    • Have event handlers whose job is to update the state according to the user's action and then call the render function.

    Following these guides, I have re-written your code. I don't think I will be able to explain everything I would like to, but I will try my best to explain my decisions in comments:

    // utility function to shuffle the options
    // copied from: https://stackoverflow.com/a/33760558/3397771
    function shuffle(array) {
      let currentIndex = array.length,  randomIndex;
    
      // While there remain elements to shuffle.
      while (currentIndex != 0) {
    
        // Pick a remaining element.
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex--;
    
        // And swap it with the current element.
        [array[currentIndex], array[randomIndex]] = [
          array[randomIndex], array[currentIndex]];
      }
    
      return array;
    }
    
    // same number generator as in original post, just renamed
    const generateNumber = function() {
      return Math.trunc(Math.random() * 12 + 1);
    };
    
    // function to generate a new set of options
    // it ensures that the correct option is in the set
    // it also ensure no duplicate options
    const generateOptions = (state) => {
        const product = state.numbersToMultiply[0] * state.numbersToMultiply[1];
      const options = (new Array(state.numOptions - 1).fill(null)).reduce((acc) => {
          let nextTry = Math.round(Math.random() * 100);
    
          // ensure no duplicates in options
          while (acc.includes(nextTry)) {
            nextTry = Math.round(Math.random() * 100);
          }
    
          acc.push(nextTry);
    
          return acc;
        }, [product]); // start with the correct answer
    
        return shuffle(options);
    }
    
    // state object contains all of the values that change
    // over the life of the app
    // arguably `numOptions` could go in a config since it does not change
    const state = {
      highScore: 0,
      isShowOptions: false,
      numbersToMultiply: [generateNumber(), generateNumber()],
      numOptions: 4,
      options: [],
      selectedOption: null,
      score: 0
    };
    
    // set the options for the first question
    state.options = generateOptions(state);
    
    // DOM elements that we will interact with or set the contents of
    const choices = document.getElementById("Choices");
    const highScore = document.getElementById("HighScore");
    const next = document.getElementById("Next");
    const question = document.getElementById("Question");
    const reset = document.getElementById("Reset");
    const score = document.getElementById('Score');
    
    // single render function
    // it will get all of the values it needs from the state object
    const render = (state) => {
      const product = state.numbersToMultiply[0] * state.numbersToMultiply[1];
      const optionElements = state.options.map(option => {
        let className = '';
        
        // determine the class for this option
        // the definition for these classes must be defined in CSS
        if (state.isShowOptions) {
          if (state.selectedOption === option && state.selectedOption === product) {
            className = 'correct';
          } else if (state.selectedOption === option) {
            className = 'incorrect';
          } else if (product === option) {
            className = 'actual';
          }
        }
        
        // map options to <div> elements to be injected into DOM
        return `<div class="${className}" data-option="${option}">🚪 ${state.isShowOptions ? option : ''}</div>`;
      });
    
      choices.innerHTML = optionElements.join('');
      highScore.textContent = String(state.highScore);
      question.innerHTML = `${state.numbersToMultiply[0]} x ${state.numbersToMultiply[1]}`;
      score.textContent = String(state.score);
    };
    
    choices.addEventListener('click', (event) => {
      // if we are showing the options,
      // we don't want the state to change when options are clicked
      // so we return early
      if (state.isShowOptions) { return; }
    
      const selectedOption = Number(event.target.dataset.option);
      const correctOption = state.numbersToMultiply[0] * state.numbersToMultiply[1];
      const isCorrect = correctOption === selectedOption;
    
      state.isShowOptions = true;
      state.selectedOption = selectedOption;
      state.score += isCorrect ? 1 : 0;
      state.highScore = Math.max(state.score, state.highScore);
      
      render(state);
    });
    
    next.addEventListener('click', () => {
      state.isShowOptions = false;
      state.numbersToMultiply = [generateNumber(), generateNumber()];
      state.options = generateOptions(state);
      
      render(state);
    });
    
    reset.addEventListener('click', () => {
      state.highScore = state.score;
      state.isShowOptions = false;
      state.numbersToMultiply = [generateNumber(), generateNumber()];
      state.options = generateOptions(state);
      state.score = 0;
      
      render(state);
    })
    
    // on app start, render the first question
    render(state);
    

    I have created a fiddle for reference.