Original Plan:

  1. Start Game
    • User inputs their name
    • Selects difficulty level (easy, medium, hard).
  2. Question Display
    • A question with multiple-choice options is fetched from your database.
  3. User Selection
    • User selects an answer.
    • Instant feedback (correct or incorrect) is displayed.
  4. Score Update
    • The score is updated dynamically based on the user’s selection.
  5. End of Game
    • After a set number of questions, the game ends.
    • Final score is displayed.
  6. Leaderboard
    • The leaderboard shows past users and their scores.

New Plan:

Instead of making the questions myself, I fetched science questions from open trivia DB by creating an api link. I then store it in a variable, like this:

TRIVIA_API_URL = "https://opentdb.com/api.php?amount=1&category=17"

I then use this for my api code and getting a batch of questions in order to reduce lagging and loading.

@questions_api.route('/get_questions', methods=['GET'])
def get_questions():
    """Fetches a batch of trivia questions for preloading."""
    difficulty = request.args.get('difficulty', 'medium')
    api_url = f"{TRIVIA_API_URL}&difficulty={difficulty}"

    try:
        response = requests.get(api_url)
        response.raise_for_status()
        data = response.json()

        if "results" not in data or not data["results"]:
            return jsonify({"error": "No questions found"}), 404

        questions = []
        for q in data["results"]:
            options = q["incorrect_answers"] + [q["correct_answer"]]
            random.shuffle(options)
            questions.append({
                "question": q["question"],
                "options": options,
                "correct_answer": q["correct_answer"]
            })

        return jsonify(questions)

    except requests.exceptions.RequestException as e:
        return jsonify({"error": f"Failed to fetch questions: {str(e)}"}), 500

This is what I get when I test it on postman: trivia

There is also an api for calculating the total score of the user that gets saved into a json file:

# creating file
SCOREBOARD_FILE = "scoreboard.json"

# Initialize scoreboard file if it doesn't exist
if not os.path.exists(SCOREBOARD_FILE):
    with open(SCOREBOARD_FILE, "w") as f:
        json.dump([], f)

# api for saving user score
@scoreboard_api.route('/submit_scores', methods=['POST'])
def submit_score():
    """Saves user score."""
    data = request.json
    username = data.get("username")
    score = data.get("score")

    if not username or score is None:
        return jsonify({"error": "Missing username or score"}), 400

    with open(SCOREBOARD_FILE, "r") as f:
        scores = json.load(f)

    scores.append({"username": username, "score": score})
    scores = sorted(scores, key=lambda x: x["score"], reverse=True)[:10]  # Keep top 10

    with open(SCOREBOARD_FILE, "w") as f:
        json.dump(scores, f)

    return jsonify({"message": "Score saved successfully!"})

Postman test:

scores

There is also an api for displaying the leaderboard in order to be dynamic:

@scoreboard_api.route('/get_scores', methods=['GET'])
def get_scores():
    """Retrieves the top 10 scores."""
    with open(SCOREBOARD_FILE, "r") as f:
        scores = json.load(f)
    return jsonify(scores)

Postman test: scoreboard

On the frontend side, I made sure to import pythonURI and FetchOptions to make it easier for deployment:

import { pythonURI, fetchOptions } from '/genescope/assets/js/api/config.js';

Next steps (4/7):

  • Get login to work so user doesn’t have to input name
  • turn leaderboard into a dynamic table and not a json file

6/2 Update:

The leaderboard has become a SQL table and is being displayed on the frontend, with search and filtering features. There are three modes: easy, medium, and hard. The easy mode questions are made from open trivia database, and the medium and hard questions are formed form information from the NCBI genetics database. For users that are new to DNA and genetics, flashcards are available for them to learn basic terms before starting.