Merge pull request #8 from SowinskiBraeden/nico_l11

Nico l11
This commit is contained in:
SowinskiBraeden authored and GitHub committed 2025-12-01 13:26:40 -08:00
commit 0dd1771f83
4 files changed
+433 -28

No files matched your search

+43
View File
@@ -55,3 +55,46 @@ Who won the 2022 World Championship?|Max Verstappen
Which driver has the most career race starts?|Fernando Alonso Which driver has the most career race starts?|Fernando Alonso
What does VSC stand for?|Virtual Safety Car What does VSC stand for?|Virtual Safety Car
Who is the most successful British F1 driver in terms of wins?|Lewis Hamilton Who is the most successful British F1 driver in terms of wins?|Lewis Hamilton
What planet is closest to the sun?|Mercury
What is the longest bone in the human body?|Femur
Which ocean lies on the east coast of the USA?|Atlantic
Which organ helps you breathe?|Lungs
Who invented the telephone?|Alexander Graham Bell
What is the capital of Spain?|Madrid
What number comes after 999?|1000
Which fruit is yellow and curved?|Banana
What is a baby dog called?|Puppy
What is the largest country in the world by area?|Russia
What is the main language spoken in China?|Mandarin
What instrument has six strings?|Guitar
What is the hottest planet in the solar system?|Venus
What natural satellite orbits Earth?|Moon
What is the capital of Australia?|Canberra
What gas do humans need to breathe?|Oxygen
How many minutes are in an hour?|60
What do cows drink?|Water
Which continent is Egypt part of?|Africa
What is 10 × 10?|100
Who wrote “Harry Potter”?|J.K. Rowling
What food do pandas primarily eat?|Bamboo
What is the capital of Germany?|Berlin
How many colors are in a rainbow?|7
What is the largest bird?|Ostrich
What machine measures temperature?|Thermometer
What is the capital of Russia?|Moscow
Which animal is known for laughing?|Hyena
What planet is known for its big red spot?|Jupiter
What is the main ingredient in sushi?|Rice
Who painted The Starry Night?|Vincent van Gogh
Which country invented pizza?|Italy
What is the capital of Mexico?|Mexico City
How many letters are in the English alphabet?|26
What is the biggest internal organ?|Liver
What animal is the symbol of Canada?|Beaver
What is the process of water turning into vapor?|Evaporation
What is the capital of South Korea?|Seoul
Which bird cannot fly?|Penguin
What is the name of Earths largest continent?|Asia
How many sides does a square have?|4
Who was the first president of the United States?|George Washington
What part of the plant is typically green?|Leaf
+49 -3
View File
@@ -1,5 +1,19 @@
package ca.bcit.comp2522.lab11; package ca.bcit.comp2522.lab11;
/**
* Represents a single quiz question containing a question prompt
* and one or more valid answers. A user-provided answer is considered
* correct if it matches any of the stored answers, ignoring case.
*
* This class performs validation to ensure question text and answer
* strings are never null or blank.
*
* @author Braeden Sowinski
* @author Nico Agostini
* @author Trishaan Shetty
* @author Calvin Arifianto
* @version 1.0.0
*/
public class Question public class Question
{ {
private static final int MIN_ANSWERS = 1; private static final int MIN_ANSWERS = 1;
@@ -7,6 +21,12 @@ public class Question
private final String question; private final String question;
private final String[] answers; private final String[] answers;
/**
* Validates that a string is not null or blank.
*
* @param str the string to validate
* @throws IllegalArgumentException if the string is null or blank
*/
private static void validateString(final String str) private static void validateString(final String str)
{ {
if (str == null || str.isBlank()) if (str == null || str.isBlank())
@@ -15,9 +35,18 @@ public class Question
} }
} }
/**
* Constructs a Question with a prompt and accepted answers.
*
* @param question the question text
* @param answers the list of valid answers
* @throws IllegalArgumentException if question is invalid,
* or if answers is empty,
* or if any individual answer is invalid
*/
public Question( public Question(
final String question, final String question,
final String[] answers final String[] answers
) { ) {
validateString(question); validateString(question);
@@ -35,21 +64,38 @@ public class Question
this.answers = answers; this.answers = answers;
} }
/**
* Returns the question text.
*
* @return the question string
*/
public String getQuestion() public String getQuestion()
{ {
return this.question; return this.question;
} }
/**
* Returns the list of accepted answers.
*
* @return an array of valid answers
*/
public String[] getAnswers() public String[] getAnswers()
{ {
return this.answers; return this.answers;
} }
/**
* Checks whether a user's answer is correct.
* Matching is case-insensitive.
*
* @param userAnswer the answer provided by the user
* @return true if it matches one of the valid answers; false otherwise
*/
public boolean validateAnswer(final String userAnswer) public boolean validateAnswer(final String userAnswer)
{ {
for (final String answer : this.answers) for (final String answer : this.answers)
{ {
if (answer.equals(userAnswer)) if (answer.equalsIgnoreCase(userAnswer))
{ {
return true; return true;
} }
+278 -25
View File
@@ -1,83 +1,336 @@
package ca.bcit.comp2522.lab11; package ca.bcit.comp2522.lab11;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application; import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
import javafx.util.Duration;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
/**
* A timed quiz application that loads questions from an external file
* and presents them to the user in randomized order. The user has a
* limited amount of time to answer a fixed number of questions.
* Final results are shown when time expires or all questions are answered.
*
* @author Braeden Sowinski
* @author Nico Agostini
* @author Trishaan Shetty
* @author Calvin Arifianto
* @version 1.0.0
*/
public class Quiz extends Application public class Quiz extends Application
{ {
private static final int WIDTH = 800; private static final int WIDTH = 800;
private static final int HEIGHT = 600; private static final int HEIGHT = 600;
private static final int QUESTION_SPLIT = 0; private static final int QUESTION_SPLIT = 0;
private static final int ANSWER_SPLIT = 1; private static final int COUNTER_STARTER = 0;
private static final String DELIMITER = "\\|"; private static final int ANSWER_SPLIT = 1;
private static final int NUM_QUESTIONS = 10; private static final int ONE_SECOND = 1;
private static final int MIN_LENGTH = 2;
private static final String DELIMITER = "\\|";
private static final int NUM_QUESTIONS = 10;
private static final int START_TIME_SECS = 90;
private static final Path QUIZ_FILE_PATH = Paths.get("comp2522", "data", "quiz.txt");
private final List<Question> questions = new ArrayList<>(); private final List<Question> questions = new ArrayList<>();
private List<Question> quizQuestions = new ArrayList<>();
private Question current; private Question current;
private void readQuestions(final String file) private int currentQuestionIndex;
private int correctCount;
private int wrongCount;
private int remainingSeconds;
private Timeline timeline;
private VBox root;
private Label questionLabel;
private Label feedbackLabel;
private Label statusLabel;
private TextField answerField;
private Button submitButton;
private Button startButton;
private Button playAgainButton;
/**
* Reads and loads quiz questions from a file.
*
* @param path the path to the quiz data file
*/
private void readQuestions(final Path path)
{ {
final BufferedReader reader; if (!questions.isEmpty())
try
{ {
reader = new BufferedReader(new FileReader(file)); return;
}
try (BufferedReader reader =
new BufferedReader(new FileReader(path.toAbsolutePath().toFile())))
{
String line; String line;
while ((line = reader.readLine()) != null) while ((line = reader.readLine()) != null)
{ {
final String question; final String[] parts = line.split(DELIMITER);
final String[] answers; if (parts.length < MIN_LENGTH)
{
continue;
}
question = line.split(DELIMITER)[QUESTION_SPLIT]; final String questionText = parts[QUESTION_SPLIT];
answers = line.split(DELIMITER)[ANSWER_SPLIT].split(","); final String[] answers = parts[ANSWER_SPLIT].split(",");
questions.add(new Question( questions.add(new Question(
question, questionText,
answers answers
)); ));
} }
} }
catch (final FileNotFoundException e) catch (final FileNotFoundException e)
{ {
System.out.println("Unable to open file: " + file); System.out.println("Unable to open file: " + path);
} }
catch (final IOException e) catch (final IOException e)
{ {
System.out.println("Error reading file: " + file); System.out.println("Error reading file: " + path);
} }
} }
/**
* Initializes the UI and starts the JavaFX application.
*
* @param stage the primary stage
*/
@Override @Override
public void start(final Stage stage) public void start(final Stage stage)
{ {
final Scene scene;
final VBox root;
root = new VBox(); root = new VBox();
root.setPrefSize(WIDTH, HEIGHT); root.setPrefSize(WIDTH, HEIGHT);
scene = new Scene(root);
this.current = this.questions.get(new Random().nextInt(this.questions.size())); questionLabel = new Label("Click \"Start quiz\" to begin.");
feedbackLabel = new Label();
statusLabel = new Label();
answerField = new TextField();
answerField.setPromptText("Type your answer here");
submitButton = new Button("Submit");
startButton = new Button("Start quiz");
playAgainButton = new Button("Play again");
readQuestions("./data/quiz.txt"); answerField.setDisable(true);
submitButton.setDisable(true);
playAgainButton.setDisable(true);
playAgainButton.setVisible(false);
root.getChildren().addAll(
questionLabel,
answerField,
submitButton,
feedbackLabel,
statusLabel,
startButton,
playAgainButton
);
Scene scene = new Scene(root);
scene.getStylesheets().add(
getClass().getResource("style.css").toExternalForm()
);
readQuestions(QUIZ_FILE_PATH);
if (questions.isEmpty())
{
questionLabel.setText("No questions loaded. Check quiz.txt path/contents.");
startButton.setDisable(true);
stage.setScene(scene);
stage.show();
return;
}
final Runnable submitAction = () -> {
if (current == null)
{
return;
}
final String userAnswer;
userAnswer = answerField.getText();
if (userAnswer == null || userAnswer.isBlank())
{
feedbackLabel.setText("Please enter an answer.");
return;
}
final String cleaned = userAnswer.trim();
if (current.validateAnswer(cleaned))
{
correctCount++;
}
else {
wrongCount++;
}
currentQuestionIndex++;
showNextQuestion();
};
submitButton.setOnAction(event -> submitAction.run());
answerField.setOnAction(event -> submitAction.run());
startButton.setOnAction(event -> startGame());
playAgainButton.setOnAction(event -> startGame());
stage.setScene(scene); stage.setScene(scene);
stage.setTitle("Quiz");
stage.show(); stage.show();
} }
/**
* Starts a new quiz round, resets counters, shuffles questions,
* and initializes the timer.
*/
private void startGame()
{
correctCount = COUNTER_STARTER;
wrongCount = COUNTER_STARTER;
remainingSeconds = START_TIME_SECS;
currentQuestionIndex = COUNTER_STARTER;
feedbackLabel.setText("");
questionLabel.setText("");
quizQuestions = new ArrayList<>(questions);
Collections.shuffle(quizQuestions);
if (quizQuestions.size() > NUM_QUESTIONS)
{
quizQuestions = new ArrayList<>(quizQuestions.subList(COUNTER_STARTER, NUM_QUESTIONS));
}
if (timeline != null)
{
timeline.stop();
}
timeline = new Timeline(
new KeyFrame(Duration.seconds(ONE_SECOND), event -> {
remainingSeconds--;
updateStatusLabel();
if (remainingSeconds <= COUNTER_STARTER)
{
endGame("Time's up!");
}
})
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
answerField.setDisable(false);
submitButton.setDisable(false);
startButton.setDisable(true);
playAgainButton.setDisable(true);
playAgainButton.setVisible(false);
showNextQuestion();
}
/**
* Displays the next question or ends the quiz if none remain.
*/
private void showNextQuestion()
{
if (currentQuestionIndex >= quizQuestions.size())
{
endGame("You answered all questions.");
return;
}
current = quizQuestions.get(currentQuestionIndex);
questionLabel.setText(current.getQuestion());
answerField.clear();
feedbackLabel.setText("");
updateStatusLabel();
}
/**
* Updates the status label to show progress and remaining time.
*/
private void updateStatusLabel()
{
if (quizQuestions.isEmpty())
{
statusLabel.setText("");
return;
}
final int displayIndex =
Math.min(currentQuestionIndex + ONE_SECOND, quizQuestions.size());
statusLabel.setText(
displayIndex + "/" + quizQuestions.size()
+ " ::: " + remainingSeconds + "s remaining"
);
}
/**
* Ends the game, stops the timer, and displays the final score.
*
* @param reason explanation for ending (e.g., time expired)
*/
private void endGame(final String reason)
{
if (timeline != null)
{
timeline.stop();
}
submitButton.setDisable(true);
answerField.setDisable(true);
current = null;
final String finalScore =
reason + "\n\nFinal score:"
+ "\nCorrect: " + correctCount
+ "\nWrong: " + wrongCount;
feedbackLabel.setText(finalScore);
questionLabel.setText("Game over.");
statusLabel.setText("");
playAgainButton.setVisible(true);
playAgainButton.setDisable(false);
startButton.setVisible(false);
}
/**
* Standard Java entry point. Launches the JavaFX app.
*
* @param args CLI arguments
*/
public static void main(final String[] args) public static void main(final String[] args)
{ {
launch(args); launch(args);
+63
View File
@@ -0,0 +1,63 @@
/* Overall background */
.root {
-fx-background-color: linear-gradient(to bottom, #fafafa, #eaeaea);
}
/* VBox container (add style class "vbox" in code if you want this) */
.vbox {
-fx-spacing: 10;
-fx-alignment: center;
-fx-padding: 20;
-fx-background-color: #f5f5f5;
-fx-border-color: #dddddd;
-fx-border-width: 1;
-fx-border-radius: 8;
-fx-background-radius: 8;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.15), 10, 0.2, 0, 2);
}
/* Labels */
.label {
-fx-font-size: 16px;
-fx-text-fill: #444444;
-fx-padding: 10;
}
/* Text fields */
.text-field {
-fx-font-size: 14px;
-fx-text-fill: #222222;
-fx-prompt-text-fill: gray;
-fx-background-color: #ffffff;
-fx-border-color: #2196F3;
-fx-border-radius: 5;
-fx-background-radius: 5;
-fx-padding: 5 8;
}
/* Optional: highlight on focus */
.text-field:focused {
-fx-border-color: #1976D2;
-fx-effect: dropshadow(gaussian, rgba(25, 118, 210, 0.4), 8, 0.3, 0, 0);
}
/* Buttons */
.button {
-fx-font-size: 14px;
-fx-background-color: linear-gradient(to bottom, #4CAF50, #388E3C);
-fx-text-fill: white;
-fx-border-radius: 5;
-fx-background-radius: 5;
-fx-padding: 5 14;
-fx-cursor: hand;
}
/* Hover and pressed effects for buttons */
.button:hover {
-fx-background-color: linear-gradient(to bottom, #66BB6A, #43A047);
}
.button:pressed {
-fx-background-color: linear-gradient(to bottom, #388E3C, #2E7D32);
-fx-translate-y: 1;
}