From be455959960fb369d0500b812ca70d2c4232b265 Mon Sep 17 00:00:00 2001
From: Stevan Beljic <stevan.beljic1@ucalgary.ca>
Date: Mon, 12 Feb 2024 21:56:02 +0000
Subject: [PATCH] Update 7 files

- /src/quizmenu.html
- /src/startpage.html
- /src/scripts/startpage.js
- /src/scripts/quizmenu.js
- /src/stylesheets/quizmenu.css
- /src/stylesheets/startpage.css
- /index.html
---
 src/scripts/quizmenu.js  | 163 +++++++++++++++++++++++++++------------
 src/scripts/startpage.js |   4 +
 2 files changed, 117 insertions(+), 50 deletions(-)

diff --git a/src/scripts/quizmenu.js b/src/scripts/quizmenu.js
index 185bdc7..86b7e0d 100644
--- a/src/scripts/quizmenu.js
+++ b/src/scripts/quizmenu.js
@@ -1,5 +1,16 @@
+//Stevan's Trivia Game - hosted on https://seng513a2.pages.dev
+//Stevan Beljic - SENG 513 Assignment 2
+
+/* 
+*   Quiz class
+*   Used for executing the game
+*/
+
+const MAX_QUESTIONS = 10;
+
 class Quiz {
 
+    //class fields
     score = 0;
     attempts = 0;
     quizQuestions;
@@ -8,47 +19,52 @@ class Quiz {
     num_answered = 0;
     difficulties = ['easy', 'medium', 'hard'];
 
+    //method used to format questions properly into Question objects
     formatQuestion(obj) {
         return new Question(obj.question, obj.correct_answer, obj.incorrect_answers[0], obj.incorrect_answers[1], obj.incorrect_answers[2], obj.difficulty);
     }
 
+    //increments attempts properly, ensuring the index does not go over the length of the array
     incrementAttempts(){
         this.attempts++;
-        if(this.attempts > this.quizQuestions.length){
-            this.attempts = 1;
+        if(this.attempts >= this.quizQuestions.length){
+            this.attempts = 0;
         }
     }
 
+    /*
+    *   Question generator method
+    *   Pulls a question and checks if its of the desired difficulty, if not, it will pull a new question. It will do this
+    *   until a valid question is found, and it will then yield it.
+    */
     *questionGenerator(){
-
         while(true){
             let tempQuestion = this.quizQuestions[this.attempts];
             if(this.gotCorrect === true){
-                //console.log("diff index: "+this.difficulties[this.diffIndex]);
                 if(tempQuestion.difficulty !== this.difficulties[this.diffIndex]){
                     this.incrementAttempts();
                     continue;
                 }
-                this.incrementAttempts();
                 yield tempQuestion;
+                this.incrementAttempts();
             } else {
-                //console.log("diff index: "+this.difficulties[this.diffIndex]);
                 if(tempQuestion.difficulty !== this.difficulties[this.diffIndex]){
                     this.incrementAttempts();
                     continue;
                 }
-                
-                this.incrementAttempts();
                 yield tempQuestion;
+                this.incrementAttempts();
             }
-            this.attempts++;
-            yield tempQuestion;
         } 
     }
 
+    /*
+    *   Method to fetch the quiz questions from the Open Trivia DB
+    */
     async fetchQuiz() {
 
-        await fetch('https://opentdb.com/api.php?amount=40&type=multiple')
+        //currently fetching geography questions
+        await fetch('https://opentdb.com/api.php?amount=50&category=22&type=multiple')
             .then(response => response.json())
             .then(data => {
                 const questions = [];
@@ -58,11 +74,17 @@ class Quiz {
                 
                 this.quizQuestions = questions;
             })
-            .catch(error => console.error('Error:', error));
+            .catch(error => {
+                throw "Error connecting to database";
+            }); //notify user of failure to obtain quiz
     }
     
 }
 
+/*
+*   Question class
+*   Contains question info, such as text, correct answer, incorrect answers, and difficulty
+*/
 class Question{
 
     questionText = "";
@@ -95,6 +117,10 @@ class Question{
 
 }
 
+/*
+*   User class
+*   Can be expanded to contain further user info. Currently only keeps track of the user's name.
+*/
 class User {
 
     username = "";
@@ -108,6 +134,11 @@ class User {
     }
 }
 
+/*
+*   Function: init
+*   Called onload of body element. Sets scorecard and options to invisible as to only display initial welcome message. Reads the users
+*   name from local storage and displays it in a welcome message.
+*/
 function init(){
     const tmp = localStorage.getItem('User');
     const user = JSON.parse(tmp);
@@ -121,21 +152,23 @@ function init(){
     optionsDiv.style.display = "none";
 }
 
-function printName(){
-    let username = localStorage.getItem('username');
-    document.getElementById('namep').innerHTML = username+' is here';
-}
-
+/*
+*   function: displayQuestion
+*   Must be called using the .call() function
+*   Displays the question text and its answers randomly across the four buttons 
+*/
 function displayQuestion(){
     document.getElementById('question').innerHTML = this.questionText;
     const occupiedSpaces = [];
+    //Uncomment this to see the difficulty of each question displayed
+    //console.log("Question difficulty: "+this.difficulty);
     
     let placement = Math.floor((Math.random() * 4) + 1);
     document.getElementById('option'+placement).innerHTML = this.correctOption;
     occupiedSpaces.push(placement);
 
     for(let x = 1; x <= 3; x++){
-        while (occupiedSpaces.includes(placement)){
+        while (occupiedSpaces.includes(placement)){ //generates a new number between 1 and 4 until a non-selected placement has been generated
             placement = Math.floor((Math.random() * 4) + 1);
         }
         document.getElementById('option'+placement).innerHTML = this['incorrect'+x];
@@ -143,20 +176,35 @@ function displayQuestion(){
     }
 }
 
+/*
+*   function: updateScore
+*   Updates the scorecard element.
+*/
 function updateScore(score){
     document.getElementById('scorecard').innerHTML = 'Score: '+score;
 }
 
+/*
+*   function: gameOver
+*   Ends the game by hiding all clickable buttons and informing the user what their final score was.
+*/
 function gameOver(score){
     const optionsDiv = document.getElementById('optionsDiv');
     optionsDiv.style.display = "none";
 
     const scorecard = document.getElementById('scorecard');
-    scorecard.innerHTML = 'Your final score was '+score+'<br>Thanks for playing!';
+    scorecard.innerHTML = 'Your final score was '+score+'<br>Thanks for playing, '+JSON.parse(localStorage.getItem('User')).username+'!';
     scorecard.style.fontSize = '30px';
     scorecard.style.textAlign = 'center';
 }
 
+/*
+*   function: highlightCorrect
+*   Parameters: correctButton, selection
+*   Takes the button containing the correct answer and the button the user selected.
+*   Appropriately highlights the user's selection, displaying green if they were correct, yellow on the user's selecton if it was incorrect,
+*   and grey for all other buttons.
+*/
 function highlightCorrect(correctButton, selection) {
     const buttons = document.getElementsByClassName('option');
 
@@ -178,6 +226,12 @@ function highlightCorrect(correctButton, selection) {
     }, 1000);
 }
 
+/*
+*   function: start
+*   Called upon clicking of start button.
+*   Initializes the game by creating a new quiz object. Initializes event listeners for four button options 
+*   and hides initial welcome message.
+*/
 async function start(){
     const game = new Quiz();
 
@@ -188,15 +242,23 @@ async function start(){
     scoreCard.style.display = "block";
 
     const optionsDiv = document.getElementById('optionsDiv');
-    optionsDiv.style.display = "grid";
-    document.getElementById("question").style.gridColumn = "1/3";
-    optionsDiv.style.gridTemplateColumns = "repeat(2, 1fr)";
-
-    await game.fetchQuiz();
-
-    const wrap = document.getElementById('buttonwrap');
+    optionsDiv.style.display = "initial";
+
+    //try catch around fetch quiz. If unable to fetch quiz, display the error to the user and terminate.
+    try{
+        await game.fetchQuiz();
+    } catch (error){
+        document.getElementById('scorecard').innerHTML = error;
+        const optionsDiv = document.getElementById('optionsDiv');
+        optionsDiv.style.display = "none";
+        console.log("Error connecting to database.");
+        return;
+    }
 
     //https://stackoverflow.com/questions/49680484/how-to-add-one-event-listener-for-all-buttons
+    //used source to create an event listener for all buttons within the wrap container
+    const wrap = document.getElementById('buttonwrap');
+    const questionGenerator = game.questionGenerator();
     wrap.addEventListener('click', (event) => {
         const isButton = event.target.nodeName === 'BUTTON';
         if (!isButton){
@@ -205,44 +267,45 @@ async function start(){
 
         game.num_answered++;
         const selected = event.target.innerHTML;
-        const correctSelection = game.quizQuestions[game.attempts-1].correctOption;
+        const correctSelection = game.quizQuestions[game.attempts].correctOption;
 
-        if (selected === correctSelection){
-            game.score+=10;
-            updateScore(game.score);
+        if (selected === correctSelection){ //right answer selected
+            updateScore((game.score+=10));
             highlightCorrect(correctSelection, selected);
-            game.diffIndex = Math.min(2, game.diffIndex+1);
+            game.diffIndex = Math.min(2, game.diffIndex+1); //increase difficulty
             game.gotCorrect = true;
-
-            if(game.num_answered > 9) {
-                gameOver(game.score);
-            }
-
+            
+            let questionGenerated = questionGenerator.next();
+            const question = questionGenerated.value;
+            //set a timeout, allowing the highlight to be displayed before showing the next question
             setTimeout(() => {
-                let questionGenerated = game.questionGenerator().next();
-                const question = questionGenerated.value;
+                if(game.num_answered >= MAX_QUESTIONS) {//end the game if 10 questions have been answered
+                    gameOver(game.score);
+                }
+                //set target of "this" to be question when calling displayQuestion
                 displayQuestion.call(question);
             }, 1000);
-        } else {
+
+        } else { //wrong answer selected
             highlightCorrect(correctSelection, selected);
-            game.diffIndex = Math.max(0, game.diffIndex-1);
+            game.diffIndex = Math.max(0, game.diffIndex-1); //decrease difficulty
             game.gotCorrect = false;
-
-            if(game.num_answered > 9) {
-                gameOver(game.score);
-            }
-
+            
+            let questionGenerated = questionGenerator.next();
+            const question = questionGenerated.value;
             setTimeout(() => {
-                let questionGenerated = game.questionGenerator().next();
-                const question = questionGenerated.value;
+                if(game.num_answered >= MAX_QUESTIONS) { //end the game if 10 questions have been answered
+                    gameOver(game.score);
+                }
+                //set target of "this" to be question when calling displayQuestion
                 displayQuestion.call(question);
             }, 1000);
         }
     })
-    
-    const questionGenerator = game.questionGenerator();
+
+    //display initial question
     let questionGenerated = questionGenerator.next();
     const question = questionGenerated.value;
-    displayQuestion.call(question);
+    displayQuestion.call(question); //set target of "this" to be question when calling displayQuestion
 
 }
\ No newline at end of file
diff --git a/src/scripts/startpage.js b/src/scripts/startpage.js
index 32e00d6..5f32f77 100644
--- a/src/scripts/startpage.js
+++ b/src/scripts/startpage.js
@@ -1,3 +1,7 @@
+/*
+*   function readName
+*   Reads the users name and saves it to local storage
+*/
 function readName(){
     let tempname = document.getElementById('name').value;
 
-- 
GitLab