Displaying Quiz Results in Rust

Mục tiêu: A comprehensive function to display quiz results including final score and individual question outcomes with feedback.


Let’s create a comprehensive function to display quiz results in Rust. This function will show the final score, each question’s outcome, and provide clear feedback to the user.

// Define a struct to hold the results of each question
struct ResultLine {
    question: String,
    user_answer: String,
    is_correct: bool,
    correct_answer: String,
}

// Define a struct to hold all results data
struct Results {
    questions: Vec<ResultLine>,
    total_score: usize,
}

impl Results {
    // Function to display the quiz results
    fn display_results(&self) {
        println!("-------------------------");
        println!("         RESULTS          ");
        println!("-------------------------");

        // Display each question's result
        for (index, question) in &self.questions.iter().enumerate() {
            println!("Question {}: {}", index + 1, question.question);
            println!("Your answer: {}", question.user_answer);

            if question.is_correct {
                println!("✅ Correct!");
            } else {
                println!("❌ Incorrect. Correct answer: {}", question.correct_answer);
            }

            println!("-------------------------");
        }

        // Display final score
        println!("Total Score: {} points", self.total_score);
        println!("-------------------------");
    }
}

fn main() {
    // Example usage:
    let results = Results {
        questions: vec![
            ResultLine {
                question: "What is the capital of France?".to_string(),
                user_answer: "Paris".to_string(),
                is_correct: true,
                correct_answer: "Paris".to_string(),
            },
            ResultLine {
                question: "Which planet is closest to the Sun?".to_string(),
                user_answer: "Venus".to_string(),
                is_correct: false,
                correct_answer: "Mercury".to_string(),
            },
        ],
        total_score: 1,
    };

    results.display_results();
}

Explanation:

  1. Struct Definitions:
  2. ResultLine: Represents the result of a single question with properties for the question itself, the user’s answer, whether it was correct, and the correct answer.
  3. Results: Contains a collection of ResultLine instances and the total score.
  4. Display Functionality:
  5. The display_results method iterates through each question result and prints:
    • The question number
    • The user’s answer
    • Whether the answer was correct or not
    • The correct answer if it was wrong
  6. After all questions, it displays the total score
  7. Formatting:
  8. Uses println! with formatted strings for better readability
  9. Includes separators (dashes) between sections
  10. Uses emojis for visual feedback (✅ for correct, ❌ for incorrect)
  11. Example Usage:
  12. Creates a sample Results instance with two questions
  13. Calls display_results() to show the formatted output

Enhancements:

  • Add color output using ANSI escape codes for better visual distinction between correct and incorrect answers
  • Include a summary section showing the number of correct/incorrect answers
  • Add percentage score calculation
  • Implement different display formats for different question types

Next Steps:

  • Implement the function that calculates the score based on user responses
  • Create a function to format the results with different colors
  • Add error handling for empty results or invalid data

Further Reading:

Formatting Quiz Results with ANSI Colors and Alignment

Mục tiêu: Enhancing quiz results display using ANSI colors and proper text alignment in Rust


Let’s dive into formatting the quiz results for better readability. We’ll create a visually appealing display that clearly shows correct and incorrect answers, along with the final score.

We’ll use the ansi_term crate for adding colors to the terminal output and unicode-width for better text alignment. First, let’s add these dependencies to our Cargo.toml:

[dependencies]
ansi_term = "0.12"
unicode-width = "0.1.9"
textwrap = "0.15"

Now, let’s create a function to format and display the results. We’ll build upon the previous task’s display function but enhance it with better formatting.

use ansi_term::{
    colour::{Green, Red},
    Style,
};
use unicode_width::UnicodeWidthStr;

// Assume we have these structs defined from previous tasks
#[derive(Debug)]
struct Question {
    question: String,
    correct_answer: String,
    possible_answers: Vec<String>,
}

#[derive(Debug)]
struct QuizData {
    questions: Vec<Question>,
}

fn display_formatted_results(quiz_data: &QuizData, user_answers: Vec<String>) {
    println!("{}", Style::new("Welcome to your quiz results!").bold());
    println!("");  // Empty line for spacing

    let mut correct_count = 0;
    let mut formatted_output = String::new();

    // Header
    let header = "Question | Your Answer | Correct Answer | Status";
    let header_line = format!("{}\n{}",
        header,
        "-".repeat(header.width()));
    formatted_output.push_str(&header_line);
    println!("{}", header_line);

    for i in 0..quiz_data.questions.len() {
        let question = &quiz_data.questions[i];
        let your_answer = &user_answers[i];
        let correct = your_answer == question.correct_answer;

        if correct {
            correct_count += 1;
        }

        let status = if correct {
            Style::new("✓").with(Green)
        } else {
            Style::new("✗").with(Red)
        };

        let line = format!("{} | {} | {} | {}",
            i + 1,
            your_answer,
            question.correct_answer,
            status
        );

        formatted_output.push_str(&line);
        println!("{}", line);
    }

    // Footer with statistics
    let final_score = (correct_count as f64 / quiz_data.questions.len() as f64) * 100.0;
    let footer = format!("\nFinal Score: {:.2}% of {} questions correct",
        final_score,
        correct_count
    );

    formatted_output.push_str(&footer);
    println!("{}", footer);

    // Optional: Save results to a file
    // We'll implement this in a future task
}

// Example usage
fn main() {
    // Sample quiz data and user answers
    let quiz_data = QuizData {
        questions: vec![
            Question {
                question: "What is the capital of France?".to_string(),
                correct_answer: "Paris".to_string(),
                possible_answers: vec![
                    "Paris".to_string(),
                    "London".to_string(),
                    "Berlin".to_string(),
                    "Madrid".to_string()
                ],
            },
            // Add more questions as needed
        ],
    };

    let user_answers = vec!("Paris".to_string(), "London".to_string());

    display_formatted_results(&quiz_data, user_answers);
}

What’s Changed from Previous Task

  1. Added color formatting using ansi_term
  2. Added better text alignment using unicode-width
  3. Structured the output in a tabular format
  4. Added visual separators between sections
  5. Included a footer with final score and statistics

Key Features of This Implementation

  1. Color Coding: Correct answers are shown in green (✓), incorrect in red (✗)
  2. Table Format: Results are displayed in a structured table format
  3. Statistics: Shows overall score percentage
  4. Readability: Proper spacing and alignment make the results easy to read
  5. Visual Hierarchy: Clear separation between different sections of the output

Testing This Implementation

To test this implementation:

  1. Run cargo build
  2. Run cargo run
  3. The quiz results will be displayed in your terminal with formatted colors and structure

Next Steps

  1. Add high score system
  2. Implement different question types
  3. Add input validation
  4. Save results to a file for future reference

Further Reading

Enhancing Rust Quiz with Color Formatting

Mục tiêu: Enhance Rust Quiz results with color formatting using crossterm for better user experience.


Adding Color and Formatting to Quiz Results in Rust

To enhance the user experience of your Rust Quiz Game, we’ll add color and formatting to highlight correct and incorrect answers. This will make the results more visually appealing and easier to understand.

Step 1: Add Dependencies

First, we need to add the crossterm crate to handle terminal styling. Add this dependency to your Cargo.toml:

[dependencies]
crossterm = "0.23"

Step 2: Import Necessary Modules

At the top of your main file, import the required modules from crossterm:

use crossterm::{
    style::{Color, Print, ResetColor, SetForegroundColor},
    terminal::{Clear, ClearType},
    event::{read, Event, KeyCode},
    execute,
};

Step 3: Create a Function to Display Results

Here’s a function that displays results with color formatting:

fn display_results(questions: Vec<Question>, answers: Vec<String>) {
    // Clear the terminal screen
    execute!(std::io::stdout(), Clear(ClearType::All)).unwrap();

    println!("Quiz Results:");
    println!("------------");

    let mut score = 0;

    for i in 0..questions.len() {
        let correct = questions[i].answer == answers[i];

        if correct {
            score += 1;
            execute!(
                std::io::stdout(),
                SetForegroundColor(Color::Green),
                Print(format!("Question {}: Correct!\n", i + 1)),
                ResetColor
            ).unwrap();
        } else {
            execute!(
                std::io::stdout(),
                SetForegroundColor(Color::Red),
                Print(format!(
                    "Question {}: Incorrect. Correct answer was: {}\n",
                    i + 1,
                    questions[i].answer
                )),
                ResetColor
            ).unwrap();
        }
    }

    println!("\nFinal Score: {}/{}", score, questions.len());

    // Optional: Wait for user input before continuing
    println!("Press Enter to continue...");
    read().unwrap();
}

Step 4: Update Your Main Function

Modify your main function to use the new display results function:

fn main() -> std::io::Result<()> {
    let questions = load_questions(); // Assume this is your function to load questions
    let answers = get_user_answers();  // Assume this is your function to get user answers

    display_results(questions, answers);
    Ok(())
}

How It Works

  • Color Formatting: We use crossterm to set different colors for correct and incorrect answers. Green for correct answers and red for incorrect ones.
  • Score Calculation: The function keeps track of the score and displays it at the end.
  • User Feedback: It shows the correct answer if the user’s answer was wrong.
  • Terminal Clearing: The terminal screen is cleared before showing results for a clean display.

Testing

You can test this by running your program with different answer combinations. You should see:

  • Green text for correct answers
  • Red text for incorrect answers
  • The correct answer shown when wrong
  • Final score at the end

Next Steps

After implementing this, you might want to:

  1. Add more color formatting for different question types
  2. Implement a high score system
  3. Add more visual elements like progress bars

Further Reading

Testing Rust Quiz Game Result Display

Mục tiêu: Testing the result display functionality of a Rust quiz game with various datasets to ensure correct and robust output.


Testing the Result Display with Different Datasets

Testing is a crucial part of software development, and for your Rust Quiz Game, it’s especially important to ensure that the result display works correctly with various datasets. This ensures that your game handles different scenarios gracefully, such as all correct answers, all incorrect answers, and mixed results.

Step-by-Step Guide to Testing the Result Display

1. Create Test Cases

First, let’s create some test cases that simulate different scenarios:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_display_results_all_correct() {
        let results = vec![
            Result {
                question: "What is the capital of France?".to_string(),
                correct_answer: "Paris".to_string(),
                user_answer: "Paris".to_string(),
            },
            Result {
                question: "Which planet is closest to the Sun?".to_string(),
                correct_answer: "Mercury".to_string(),
                user_answer: "Mercury".to_string(),
            },
        ];

        display_results(&results);
        // You can manually verify the output
        // or use visual inspection
    }

    #[test]
    fn test_display_results_all_incorrect() {
        let results = vec![
            Result {
                question: "What is the capital of France?".to_string(),
                correct_answer: "Paris".to_string(),
                user_answer: "London".to_string(),
            },
            Result {
                question: "Which planet is closest to the Sun?".to_string(),
                correct_answer: "Mercury".to_string(),
                user_answer: "Venus".to_string(),
            },
        ];

        display_results(&results);
    }

    #[test]
    fn test_display_results_mixed() {
        let results = vec![
            Result {
                question: "What is the capital of France?".to_string(),
                correct_answer: "Paris".to_string(),
                user_answer: "Paris".to_string(),
            },
            Result {
                question: "Which planet is closest to the Sun?".to_string(),
                correct_answer: "Mercury".to_string(),
                user_answer: "Venus".to_string(),
            },
        ];

        display_results(&results);
    }
}

2. Modify the Display Function

Let’s enhance the display_results function to handle different cases:

fn display_results(results: &Vec<Result>) {
    println!("\n=== Quiz Results ===");

    for result in results {
        println!("Question: {}", result.question);
        println!("Correct Answer: {}", result.correct_answer);
        println!("Your Answer: {}", result.user_answer);

        if result.user_answer == result.correct_answer {
            println!("\x1B[32mCorrect!\x1B[0m");
        } else {
            println!("\x1B[31mIncorrect\x1B[0m");
        }

        println!("---");
    }

    let correct_count = results.iter()
        .filter(|r| r.user_answer == r.correct_answer)
        .count();

    println!("\nTotal Correct: {}/{}", correct_count, results.len());
}

3. Run the Tests

You can run the tests using:

cargo test

4. Visual Inspection

While the above tests will help you verify functionality, it’s also important to do visual inspection. Run the game with different datasets and observe how the results are displayed. Pay attention to:

  • Correct answers are displayed in green
  • Incorrect answers are displayed in red
  • The total score is calculated correctly
  • The formatting remains consistent

5. Edge Cases

Test these edge cases:

  • All answers correct
  • All answers incorrect
  • Empty results (though this should not happen in normal operation)
  • Single correct answer
  • Single incorrect answer

6. Color Formatting

The above code uses ANSI escape codes for color formatting:

  • \x1B[32m for green (correct answers)
  • \x1B[31m for red (incorrect answers)
  • \x1B[0m resets the color

This will make the results more visually appealing and easier to understand.

7. Further Enhancements

Once you have confirmed that the result display works correctly, you could consider these enhancements:

  1. Save the results to a file for later review
  2. Add a feature to retry incorrect questions
  3. Implement a high scores system
  4. Add more sophisticated formatting or even graphical output

What to Read More About

This testing will ensure that your result display function works correctly under various conditions, making your quiz game more robust and user-friendly.