From df3cbe2e8c1692309f7a1a8bd07aae32659aee44 Mon Sep 17 00:00:00 2001 From: AMGI Date: Mon, 12 May 2025 11:32:52 +0200 Subject: [PATCH 1/4] creates question index --- src/question.js | 78 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/src/question.js b/src/question.js index 68f6631a..aa2ec13c 100644 --- a/src/question.js +++ b/src/question.js @@ -1,7 +1,75 @@ class Question { - // YOUR CODE HERE: - // - // 1. constructor (text, choices, answer, difficulty) + constructor(text, choices, answer, difficulty) { + this.text = text; + this.choices = choices; + this.answer = answer; + this.difficulty = difficulty; + + // Validate inputs (moved inside constructor) + if (typeof this.text !== 'string') { + throw new Error('Question text must be a string'); + } + + if (!Array.isArray(this.choices) || this.choices.length === 0) { + throw new Error('Choices must be a non-empty array'); + } + + if (typeof this.answer !== 'string' || !this.choices.includes(this.answer)) { + throw new Error('Answer must be a string and one of the provided choices'); + } + + if (typeof this.difficulty !== 'number' || this.difficulty < 1 || this.difficulty > 3) { + throw new Error('Difficulty must be a number between 1 and 3'); + } + } + + shuffleChoices() { + // Create a copy to avoid modifying the original + const result = new Array(this.choices.length); + // Keep track of available positions + const availableIndices = Array.from({ length: this.choices.length }, (_, i) => i); + + // Process each element of the original array + this.choices.forEach(item => { + // Generate a random index from the available positions + const randomIndex = Math.floor(Math.random() * availableIndices.length); + // Get the actual position from our available indices + const newPosition = availableIndices[randomIndex]; + // Remove this index so it won't be reused + availableIndices.splice(randomIndex, 1); + // Place the item in its new position + result[newPosition] = item; + }); + + // Update the choices property with shuffled array + this.choices = result; + + // No return needed as we're modifying the object's property + } + } +class Question + +// should receive 4 arguments in the constructor (text, choices, answer, difficulty). +// should have 4 properties: text, choices, answer, difficulty. +// should receive text (string) as its 1st argument and assign it to text property. +// should receive choices (array of strings) as its 2nd argument and assign it to choices property. +// should receive answer (string) as its 3rd argument and assign it to answer property. +// should receive difficulty (number) as its 3rd argument and assign it to difficulty property. + +// Note: The difficulty will be a number between 1 and 3, with 1 being the easiest and 3 being the hardest. + + + +// shuffleChoices() method + +// Shuffles the elements stored in the choices array of the Question. + +// should be defined. + +// should be a function. + +// should receive no arguments. + +// should shuffle the elements stored in the choices array property. + - // 2. shuffleChoices() -} \ No newline at end of file From cbfe64fc149d47081b785dd95c0fb262d45a6a77 Mon Sep 17 00:00:00 2001 From: AMGI Date: Mon, 12 May 2025 11:53:53 +0200 Subject: [PATCH 2/4] completes quiz object --- src/question.js | 92 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 11 deletions(-) diff --git a/src/question.js b/src/question.js index aa2ec13c..3145eae3 100644 --- a/src/question.js +++ b/src/question.js @@ -47,22 +47,85 @@ class Question { // No return needed as we're modifying the object's property } } -class Question +class Question { + constructor(questions, timeLimit, timeRemaining) { + this.questions = questions; + this.timeLimit = timeLimit; + this.timeRemaining = timeRemaining; -// should receive 4 arguments in the constructor (text, choices, answer, difficulty). -// should have 4 properties: text, choices, answer, difficulty. -// should receive text (string) as its 1st argument and assign it to text property. -// should receive choices (array of strings) as its 2nd argument and assign it to choices property. -// should receive answer (string) as its 3rd argument and assign it to answer property. -// should receive difficulty (number) as its 3rd argument and assign it to difficulty property. + // Initialize additional properties with default values + this.correctAnswers = 0; + this.currentQuestionIndex = 0; + + // Validate inputs (moved inside constructor) + if (typeof this.text !== 'string') { + throw new Error('Question text must be a string'); + } + + // Check if questions object is empty + if (Object.keys(this.questions).length === 0) { + throw new Error('Questions object cannot be empty'); + } + + // Check if each question has required properties + for (const questionId in this.questions) { + const question = this.questions[questionId]; + + // Check if each question has all required properties + if (!question.hasOwnProperty('text') || + !question.hasOwnProperty('choices') || + !question.hasOwnProperty('answer') || + !question.hasOwnProperty('difficulty')) { + throw new Error(`Question ${questionId} is missing required properties`); + } + + if (typeof this.timeLimit !== 'number' || this.timeLimit <= 0) { + throw new Error('Time limit must be a positive number'); + } + + if (typeof this.timeRemaining !== 'number' || this.timeRemaining < 0) { + throw new Error('Time remaining must be a non-negative number'); + } + + // getQuestion() method -// Note: The difficulty will be a number between 1 and 3, with 1 being the easiest and 3 being the hardest. +// Returns the question from the questions array at the position of currentQuestionIndex. +// should be defined. +// should be a function. +// should receive no arguments. +// should return the item from the questions array at the position of currentQuestionIndex. +// moveToNextQuestion() method -// shuffleChoices() method +// When called, increments the currentQuestionIndex by 1. -// Shuffles the elements stored in the choices array of the Question. +// should be defined. +// should be a function. +// should receive no arguments. +// should increment the currentQuestionIndex by 1. + +// shuffleQuestions() method + +// Shuffles the elements stored in the questions array of the Quiz. + +// should be defined. +// should be a function. +// should receive no arguments. +// should shuffle the items in the questions array. + +// checkAnswer(answer) method + +// Checks if the passed answer is correct for the current question and increments correctAnswers by 1 if the answer is correct. + +// should be defined. +// should be a function. +// should receive 1 argument (answer - string). +// should increase correctAnswers by 1 when called with a correct answer for the current question + +// hasEnded() method + +// Returns true if the quiz has ended (the last question has been answered), and false otherwise. // should be defined. @@ -70,6 +133,13 @@ class Question // should receive no arguments. -// should shuffle the elements stored in the choices array property. +// should return false when currentQuestionIndex is less than the questions array length + +// should return true when currentQuestionIndex is equal to the questions array length + + + } + +} From b00b3502435ea02403ae8934903208fd87d68722 Mon Sep 17 00:00:00 2001 From: AMGI Date: Mon, 12 May 2025 12:54:52 +0200 Subject: [PATCH 3/4] pushes up quiz --- eslint.config.mjs | 6 ++ package.json | 28 +++++++++ src/question.js | 96 ------------------------------ src/quiz.js | 148 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 172 insertions(+), 106 deletions(-) create mode 100644 eslint.config.mjs create mode 100644 package.json diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..2cf0ebb7 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,6 @@ +import config from 'eslint-config-xo'; +import {defineConfig} from 'eslint/config'; + +export default defineConfig([ + config, +]); diff --git a/package.json b/package.json new file mode 100644 index 00000000..545ea965 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "javascript-quiz-project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/TheZuckaNator/javascript-quiz-project.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/TheZuckaNator/javascript-quiz-project/issues" + }, + "homepage": "https://github.com/TheZuckaNator/javascript-quiz-project#readme", + "devDependencies": { + "@eslint/js": "^9.26.0", + "eslint": "^9.26.0", + "eslint-config-xo": "^0.47.0", + "globals": "^16.1.0" + } +} diff --git a/src/question.js b/src/question.js index 3145eae3..e27ec4cc 100644 --- a/src/question.js +++ b/src/question.js @@ -47,99 +47,3 @@ class Question { // No return needed as we're modifying the object's property } } -class Question { - constructor(questions, timeLimit, timeRemaining) { - this.questions = questions; - this.timeLimit = timeLimit; - this.timeRemaining = timeRemaining; - - // Initialize additional properties with default values - this.correctAnswers = 0; - this.currentQuestionIndex = 0; - - // Validate inputs (moved inside constructor) - if (typeof this.text !== 'string') { - throw new Error('Question text must be a string'); - } - - // Check if questions object is empty - if (Object.keys(this.questions).length === 0) { - throw new Error('Questions object cannot be empty'); - } - - // Check if each question has required properties - for (const questionId in this.questions) { - const question = this.questions[questionId]; - - // Check if each question has all required properties - if (!question.hasOwnProperty('text') || - !question.hasOwnProperty('choices') || - !question.hasOwnProperty('answer') || - !question.hasOwnProperty('difficulty')) { - throw new Error(`Question ${questionId} is missing required properties`); - } - - if (typeof this.timeLimit !== 'number' || this.timeLimit <= 0) { - throw new Error('Time limit must be a positive number'); - } - - if (typeof this.timeRemaining !== 'number' || this.timeRemaining < 0) { - throw new Error('Time remaining must be a non-negative number'); - } - - // getQuestion() method - -// Returns the question from the questions array at the position of currentQuestionIndex. - -// should be defined. -// should be a function. -// should receive no arguments. -// should return the item from the questions array at the position of currentQuestionIndex. - -// moveToNextQuestion() method - -// When called, increments the currentQuestionIndex by 1. - -// should be defined. -// should be a function. -// should receive no arguments. -// should increment the currentQuestionIndex by 1. - -// shuffleQuestions() method - -// Shuffles the elements stored in the questions array of the Quiz. - -// should be defined. -// should be a function. -// should receive no arguments. -// should shuffle the items in the questions array. - -// checkAnswer(answer) method - -// Checks if the passed answer is correct for the current question and increments correctAnswers by 1 if the answer is correct. - -// should be defined. -// should be a function. -// should receive 1 argument (answer - string). -// should increase correctAnswers by 1 when called with a correct answer for the current question - -// hasEnded() method - -// Returns true if the quiz has ended (the last question has been answered), and false otherwise. - -// should be defined. - -// should be a function. - -// should receive no arguments. - -// should return false when currentQuestionIndex is less than the questions array length - -// should return true when currentQuestionIndex is equal to the questions array length - - - } - -} - - diff --git a/src/quiz.js b/src/quiz.js index d94cfd14..a1a0f4c9 100644 --- a/src/quiz.js +++ b/src/quiz.js @@ -1,15 +1,143 @@ -class Quiz { - // YOUR CODE HERE: - // - // 1. constructor (questions, timeLimit, timeRemaining) - // 2. getQuestion() +class Quiz{ + constructor(questions, timeLimit, timeRemaining) { + this.questions = questions; + this.timeLimit = timeLimit; + this.timeRemaining = timeRemaining; + + // Initialize additional properties with default values + this.correctAnswers = 0; + this.currentQuestionIndex = 0; + + // Validate inputs (moved inside constructor) + if (typeof this.text !== 'string') { + throw new Error('Question text must be a string'); + } + + // Check if questions object is empty + if (Object.keys(this.questions).length === 0) { + throw new Error('Questions object cannot be empty'); + } + + // Check if each question has required properties + for (const questionId in this.questions) { + const question = this.questions[questionId]; + + // Check if each question has all required properties + if (!question.hasOwnProperty('text') || + !question.hasOwnProperty('choices') || + !question.hasOwnProperty('answer') || + !question.hasOwnProperty('difficulty')) { + throw new Error(`Question ${questionId} is missing required properties`); + } + } + + // time and timelimit checks + if (typeof this.timeLimit !== 'number' || this.timeLimit <= 0) { + throw new Error('Time limit must be a positive number'); + } + + if (typeof this.timeRemaining !== 'number' || this.timeRemaining < 0) { + throw new Error('Time remaining must be a non-negative number'); + } + + } + + // starts methos + getQuestion() { + const keys = Object.keys(this.questions); + if (this.currentQuestionIndex >= 0 && this.currentQuestionIndex < keys.length) { + const currentKey = keys[this.currentQuestionIndex]; + return this.questions[currentKey]; + } + } - // 3. moveToNextQuestion() + moveToNextQuestion() { + this.currentQuestionIndex += 1; + } + + shuffleQuestions() { + const keys = Object.keys(this.questions); + const shuffledKeys = []; + const availableIndices = Array.from({ length: keys.length }, (_, i) => i); + + // Shuffle the keys + keys.forEach(key => { + const randomIndex = Math.floor(Math.random() * availableIndices.length); + const newPosition = availableIndices[randomIndex]; + availableIndices.splice(randomIndex, 1); + shuffledKeys[newPosition] = key; + }); + + // Create a new questions object with shuffled keys + const shuffledQuestions = {}; + shuffledKeys.forEach(key => { + shuffledQuestions[key] = this.questions[key]; + }); + + this.questions = shuffledQuestions; + } + + checkAnswer(answer) { + const currentQuestion = this.getQuestion(); + if (currentQuestion && currentQuestion.answer === currentQuestion.choices[i]) { + this.correctAnswers += 1; + return true; + } + return false; + } + + hasEnded() { + return this.currentQuestionIndex >= Object.keys(this.questions).length; + } + + // end class + } + +// moveToNextQuestion() method + +// When called, increments the currentQuestionIndex by 1. + +// should be defined. +// should be a function. +// should receive no arguments. +// should increment the currentQuestionIndex by 1. + +// shuffleQuestions() method + +// Shuffles the elements stored in the questions array of the Quiz. + +// should be defined. +// should be a function. +// should receive no arguments. +// should shuffle the items in the questions array. + +// checkAnswer(answer) method + +// Checks if the passed answer is correct for the current question and increments correctAnswers by 1 if the answer is correct. + +// should be defined. +// should be a function. +// should receive 1 argument (answer - string). +// should increase correctAnswers by 1 when called with a correct answer for the current question + +// hasEnded() method + +// Returns true if the quiz has ended (the last question has been answered), and false otherwise. + +// should be defined. + +// should be a function. + +// should receive no arguments. + +// should return false when currentQuestionIndex is less than the questions array length + +// should return true when currentQuestionIndex is equal to the questions array length + - // 4. shuffleQuestions() + } + +} - // 5. checkAnswer(answer) - // 6. hasEnded() -} \ No newline at end of file From d82d68e49afa1d185a31c7b1fcade9f89ea7ccee Mon Sep 17 00:00:00 2001 From: AMGI Date: Tue, 13 May 2025 13:06:45 +0200 Subject: [PATCH 4/4] pushes new code --- src/question.js | 63 +++++---------- src/quiz.js | 201 ++++++++++++++++-------------------------------- 2 files changed, 86 insertions(+), 178 deletions(-) diff --git a/src/question.js b/src/question.js index e27ec4cc..720ac724 100644 --- a/src/question.js +++ b/src/question.js @@ -1,49 +1,22 @@ class Question { - constructor(text, choices, answer, difficulty) { - this.text = text; - this.choices = choices; - this.answer = answer; - this.difficulty = difficulty; - - // Validate inputs (moved inside constructor) - if (typeof this.text !== 'string') { - throw new Error('Question text must be a string'); - } - - if (!Array.isArray(this.choices) || this.choices.length === 0) { - throw new Error('Choices must be a non-empty array'); - } - - if (typeof this.answer !== 'string' || !this.choices.includes(this.answer)) { - throw new Error('Answer must be a string and one of the provided choices'); - } - - if (typeof this.difficulty !== 'number' || this.difficulty < 1 || this.difficulty > 3) { - throw new Error('Difficulty must be a number between 1 and 3'); - } - } + constructor(text, choices, answer, difficulty) { + this.text = text; + this.choices = choices; + this.answer = answer; + this.difficulty = difficulty; + } + + shuffleChoices() { + // Fisher-Yates shuffle algorithm + const shuffled = [...this.choices]; // Create a copy of the array - shuffleChoices() { - // Create a copy to avoid modifying the original - const result = new Array(this.choices.length); - // Keep track of available positions - const availableIndices = Array.from({ length: this.choices.length }, (_, i) => i); - - // Process each element of the original array - this.choices.forEach(item => { - // Generate a random index from the available positions - const randomIndex = Math.floor(Math.random() * availableIndices.length); - // Get the actual position from our available indices - const newPosition = availableIndices[randomIndex]; - // Remove this index so it won't be reused - availableIndices.splice(randomIndex, 1); - // Place the item in its new position - result[newPosition] = item; - }); - - // Update the choices property with shuffled array - this.choices = result; - - // No return needed as we're modifying the object's property + for (let i = shuffled.length - 1; i > 0; i--) { + // Pick a random index from 0 to i + const j = Math.floor(Math.random() * (i + 1)); + // Swap elements at indices i and j + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } + + this.choices = shuffled; } +} \ No newline at end of file diff --git a/src/quiz.js b/src/quiz.js index a1a0f4c9..c90e2a7a 100644 --- a/src/quiz.js +++ b/src/quiz.js @@ -1,143 +1,78 @@ - -class Quiz{ +class Quiz { constructor(questions, timeLimit, timeRemaining) { - this.questions = questions; - this.timeLimit = timeLimit; - this.timeRemaining = timeRemaining; - - // Initialize additional properties with default values - this.correctAnswers = 0; - this.currentQuestionIndex = 0; - - // Validate inputs (moved inside constructor) - if (typeof this.text !== 'string') { - throw new Error('Question text must be a string'); - } - - // Check if questions object is empty - if (Object.keys(this.questions).length === 0) { - throw new Error('Questions object cannot be empty'); - } - - // Check if each question has required properties - for (const questionId in this.questions) { - const question = this.questions[questionId]; - - // Check if each question has all required properties - if (!question.hasOwnProperty('text') || - !question.hasOwnProperty('choices') || - !question.hasOwnProperty('answer') || - !question.hasOwnProperty('difficulty')) { - throw new Error(`Question ${questionId} is missing required properties`); - } - } - - // time and timelimit checks - if (typeof this.timeLimit !== 'number' || this.timeLimit <= 0) { - throw new Error('Time limit must be a positive number'); - } - - if (typeof this.timeRemaining !== 'number' || this.timeRemaining < 0) { - throw new Error('Time remaining must be a non-negative number'); - } - + this.questions = questions; + this.timeLimit = timeLimit; + this.timeRemaining = timeRemaining; + + // Initialize additional properties with default values + this.correctAnswers = 0; + this.currentQuestionIndex = 0; } - - // starts methos + getQuestion() { - const keys = Object.keys(this.questions); - if (this.currentQuestionIndex >= 0 && this.currentQuestionIndex < keys.length) { - const currentKey = keys[this.currentQuestionIndex]; - return this.questions[currentKey]; - } - } + // Return the current question based on the currentQuestionIndex + return this.questions[this.currentQuestionIndex]; + } moveToNextQuestion() { - this.currentQuestionIndex += 1; - } + // Increment the currentQuestionIndex by 1 + this.currentQuestionIndex += 1; + } shuffleQuestions() { - const keys = Object.keys(this.questions); - const shuffledKeys = []; - const availableIndices = Array.from({ length: keys.length }, (_, i) => i); - - // Shuffle the keys - keys.forEach(key => { - const randomIndex = Math.floor(Math.random() * availableIndices.length); - const newPosition = availableIndices[randomIndex]; - availableIndices.splice(randomIndex, 1); - shuffledKeys[newPosition] = key; - }); - - // Create a new questions object with shuffled keys - const shuffledQuestions = {}; - shuffledKeys.forEach(key => { - shuffledQuestions[key] = this.questions[key]; - }); - - this.questions = shuffledQuestions; - } - - checkAnswer(answer) { - const currentQuestion = this.getQuestion(); - if (currentQuestion && currentQuestion.answer === currentQuestion.choices[i]) { - this.correctAnswers += 1; - return true; - } - return false; - } - - hasEnded() { - return this.currentQuestionIndex >= Object.keys(this.questions).length; - } - - // end class + // Create a copy of the questions array to avoid modifying the original + const shuffled = [...this.questions]; + + // Fisher-Yates shuffle algorithm + for (let i = shuffled.length - 1; i > 0; i--) { + // Pick a random index from 0 to i + const j = Math.floor(Math.random() * (i + 1)); + // Swap elements at indices i and j + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + + // Update the questions property with the shuffled array + this.questions = shuffled; + } + + checkAnswer(answer) { + const currentQuestion = this.getQuestion(); + + // Check if the provided answer matches the correct answer for the current question + if (currentQuestion && answer === currentQuestion.answer) { + this.correctAnswers += 1; + return true; + } + return false; + } + + hasEnded() { + // Check if we've reached the end of the questions + return this.currentQuestionIndex >= this.questions.length; + } + + filterQuestionsByDifficulty(difficulty) { + // Check if difficulty is valid (between 1 and 3) + if (typeof difficulty !== 'number' || difficulty < 1 || difficulty > 3) { + return; // Do nothing if difficulty is invalid + } + + // Filter questions by the specified difficulty + this.questions = this.questions.filter(question => question.difficulty === difficulty); } - -// moveToNextQuestion() method - -// When called, increments the currentQuestionIndex by 1. - -// should be defined. -// should be a function. -// should receive no arguments. -// should increment the currentQuestionIndex by 1. - -// shuffleQuestions() method - -// Shuffles the elements stored in the questions array of the Quiz. - -// should be defined. -// should be a function. -// should receive no arguments. -// should shuffle the items in the questions array. - -// checkAnswer(answer) method - -// Checks if the passed answer is correct for the current question and increments correctAnswers by 1 if the answer is correct. - -// should be defined. -// should be a function. -// should receive 1 argument (answer - string). -// should increase correctAnswers by 1 when called with a correct answer for the current question - -// hasEnded() method - -// Returns true if the quiz has ended (the last question has been answered), and false otherwise. - -// should be defined. - -// should be a function. - -// should receive no arguments. - -// should return false when currentQuestionIndex is less than the questions array length - -// should return true when currentQuestionIndex is equal to the questions array length - - + + averageDifficulty() { + // If there are no questions, return 0 to avoid division by zero + if (this.questions.length === 0) { + return 0; } -} - - + // Use reduce() to sum all difficulty values + const totalDifficulty = this.questions.reduce((sum, question) => { + return sum + question.difficulty; + }, 0); + + // Calculate and return the average + return totalDifficulty / this.questions.length; + } + } \ No newline at end of file