diff options
| author | 2021-09-07 12:53:23 -0400 | |
|---|---|---|
| committer | 2021-09-07 12:53:23 -0400 | |
| commit | 566ec414321420572bcb70a89f10e04150ceb143 (patch) | |
| tree | e677990cdfb10179a6359227d54b0a30df3bf708 | |
| parent | Update code to use 'mode' variable (diff) | |
| parent | Merge branch 'main' into color-677 (diff) | |
Merge branch 'color-677' of https://github.com/brad90four/sir-lancebot into color-677
Diffstat (limited to '')
| -rw-r--r-- | bot/exts/fun/trivia_quiz.py | 422 | ||||
| -rw-r--r-- | bot/resources/fun/trivia_quiz.json | 94 | 
2 files changed, 299 insertions, 217 deletions
| diff --git a/bot/exts/fun/trivia_quiz.py b/bot/exts/fun/trivia_quiz.py index cf9e6cd3..236586b0 100644 --- a/bot/exts/fun/trivia_quiz.py +++ b/bot/exts/fun/trivia_quiz.py @@ -3,12 +3,16 @@ import json  import logging  import operator  import random +import re +import string +from collections import defaultdict  from dataclasses import dataclass +from datetime import datetime, timedelta  from pathlib import Path  from typing import Callable, Optional  import discord -from discord.ext import commands +from discord.ext import commands, tasks  from rapidfuzz import fuzz  from bot.bot import Bot @@ -16,177 +20,194 @@ from bot.constants import Colours, NEGATIVE_REPLIES, Roles  logger = logging.getLogger(__name__) -DEFAULT_QUESTION_LIMIT = 6 +DEFAULT_QUESTION_LIMIT = 7  STANDARD_VARIATION_TOLERANCE = 88  DYNAMICALLY_GEN_VARIATION_TOLERANCE = 97 +MAX_ERROR_FETCH_TRIES = 3 +  WRONG_ANS_RESPONSE = [      "No one answered correctly!",      "Better luck next time...",  ] -N_PREFIX_STARTS_AT = 5 -N_PREFIXES = [ -    "penta", "hexa", "hepta", "octa", "nona", -    "deca", "hendeca", "dodeca", "trideca", "tetradeca", -] +RULES = ( +    "No cheating and have fun!", +    "Points for each question reduces by 25 after 10s or after a hint. Total time is 30s per question" +) -PLANETS = [ -    ("1st", "Mercury"), -    ("2nd", "Venus"), -    ("3rd", "Earth"), -    ("4th", "Mars"), -    ("5th", "Jupiter"), -    ("6th", "Saturn"), -    ("7th", "Uranus"), -    ("8th", "Neptune"), -] - -TAXONOMIC_HIERARCHY = [ -    "species", "genus", "family", "order", -    "class", "phylum", "kingdom", "domain", -] - -UNITS_TO_BASE_UNITS = { -    "hertz": ("(unit of frequency)", "s^-1"), -    "newton": ("(unit of force)", "m*kg*s^-2"), -    "pascal": ("(unit of pressure & stress)", "m^-1*kg*s^-2"), -    "joule": ("(unit of energy & quantity of heat)", "m^2*kg*s^-2"), -    "watt": ("(unit of power)", "m^2*kg*s^-3"), -    "coulomb": ("(unit of electric charge & quantity of electricity)", "s*A"), -    "volt": ("(unit of voltage & electromotive force)", "m^2*kg*s^-3*A^-1"), -    "farad": ("(unit of capacitance)", "m^-2*kg^-1*s^4*A^2"), -    "ohm": ("(unit of electric resistance)", "m^2*kg*s^-3*A^-2"), -    "weber": ("(unit of magnetic flux)", "m^2*kg*s^-2*A^-1"), -    "tesla": ("(unit of magnetic flux density)", "kg*s^-2*A^-1"), -} +WIKI_FEED_API_URL = "https://en.wikipedia.org/api/rest_v1/feed/featured/{date}" +TRIVIA_QUIZ_ICON = ( +    "https://raw.githubusercontent.com/python-discord/branding/main/icons/trivia_quiz/trivia-quiz-dist.png" +)  @dataclass(frozen=True)  class QuizEntry: -    """Dataclass for a quiz entry (a question and a string containing answers separated by commas).""" +    """Stores quiz entry (a question and a list of answers)."""      question: str -    answer: str - - -def linear_system(q_format: str, a_format: str) -> QuizEntry: -    """Generate a system of linear equations with two unknowns.""" -    x, y = random.randint(2, 5), random.randint(2, 5) -    answer = a_format.format(x, y) - -    coeffs = random.sample(range(1, 6), 4) - -    question = q_format.format( -        coeffs[0], -        coeffs[1], -        coeffs[0] * x + coeffs[1] * y, -        coeffs[2], -        coeffs[3], -        coeffs[2] * x + coeffs[3] * y, -    ) - -    return QuizEntry(question, answer) - - -def mod_arith(q_format: str, a_format: str) -> QuizEntry: -    """Generate a basic modular arithmetic question.""" -    quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) -    ans = random.randint(0, 9)  # max remainder is 9, since the minimum modulus is 10 -    a = quotient * m + ans - b - -    question = q_format.format(a, b, m) -    answer = a_format.format(ans) +    answers: list[str] +    var_tol: int + + +class DynamicQuestionGen: +    """Class that contains functions to generate math/science questions for TriviaQuiz Cog.""" + +    N_PREFIX_STARTS_AT = 5 +    N_PREFIXES = [ +        "penta", "hexa", "hepta", "octa", "nona", +        "deca", "hendeca", "dodeca", "trideca", "tetradeca", +    ] + +    PLANETS = [ +        ("1st", "Mercury"), +        ("2nd", "Venus"), +        ("3rd", "Earth"), +        ("4th", "Mars"), +        ("5th", "Jupiter"), +        ("6th", "Saturn"), +        ("7th", "Uranus"), +        ("8th", "Neptune"), +    ] + +    TAXONOMIC_HIERARCHY = [ +        "species", "genus", "family", "order", +        "class", "phylum", "kingdom", "domain", +    ] + +    UNITS_TO_BASE_UNITS = { +        "hertz": ("(unit of frequency)", "s^-1"), +        "newton": ("(unit of force)", "m*kg*s^-2"), +        "pascal": ("(unit of pressure & stress)", "m^-1*kg*s^-2"), +        "joule": ("(unit of energy & quantity of heat)", "m^2*kg*s^-2"), +        "watt": ("(unit of power)", "m^2*kg*s^-3"), +        "coulomb": ("(unit of electric charge & quantity of electricity)", "s*A"), +        "volt": ("(unit of voltage & electromotive force)", "m^2*kg*s^-3*A^-1"), +        "farad": ("(unit of capacitance)", "m^-2*kg^-1*s^4*A^2"), +        "ohm": ("(unit of electric resistance)", "m^2*kg*s^-3*A^-2"), +        "weber": ("(unit of magnetic flux)", "m^2*kg*s^-2*A^-1"), +        "tesla": ("(unit of magnetic flux density)", "kg*s^-2*A^-1"), +    } + +    @classmethod +    def linear_system(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a system of linear equations with two unknowns.""" +        x, y = random.randint(2, 5), random.randint(2, 5) +        answer = a_format.format(x, y) + +        coeffs = random.sample(range(1, 6), 4) + +        question = q_format.format( +            coeffs[0], +            coeffs[1], +            coeffs[0] * x + coeffs[1] * y, +            coeffs[2], +            coeffs[3], +            coeffs[2] * x + coeffs[3] * y, +        ) -    return QuizEntry(question, answer) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) +    @classmethod +    def mod_arith(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a basic modular arithmetic question.""" +        quotient, m, b = random.randint(30, 40), random.randint(10, 20), random.randint(200, 350) +        ans = random.randint(0, 9)  # max remainder is 9, since the minimum modulus is 10 +        a = quotient * m + ans - b -def ngonal_prism(q_format: str, a_format: str) -> QuizEntry: -    """Generate a question regarding vertices on n-gonal prisms.""" -    n = random.randint(0, len(N_PREFIXES) - 1) +        question = q_format.format(a, b, m) +        answer = a_format.format(ans) -    question = q_format.format(N_PREFIXES[n]) -    answer = a_format.format((n + N_PREFIX_STARTS_AT) * 2) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) -    return QuizEntry(question, answer) +    @classmethod +    def ngonal_prism(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a question regarding vertices on n-gonal prisms.""" +        n = random.randint(0, len(cls.N_PREFIXES) - 1) +        question = q_format.format(cls.N_PREFIXES[n]) +        answer = a_format.format((n + cls.N_PREFIX_STARTS_AT) * 2) -def imag_sqrt(q_format: str, a_format: str) -> QuizEntry: -    """Generate a negative square root question.""" -    ans_coeff = random.randint(3, 10) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) -    question = q_format.format(ans_coeff ** 2) -    answer = a_format.format(ans_coeff) +    @classmethod +    def imag_sqrt(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a negative square root question.""" +        ans_coeff = random.randint(3, 10) -    return QuizEntry(question, answer) +        question = q_format.format(ans_coeff ** 2) +        answer = a_format.format(ans_coeff) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) -def binary_calc(q_format: str, a_format: str) -> QuizEntry: -    """Generate a binary calculation question.""" -    a = random.randint(15, 20) -    b = random.randint(10, a) -    oper = random.choice( -        ( -            ("+", operator.add), -            ("-", operator.sub), -            ("*", operator.mul), +    @classmethod +    def binary_calc(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a binary calculation question.""" +        a = random.randint(15, 20) +        b = random.randint(10, a) +        oper = random.choice( +            ( +                ("+", operator.add), +                ("-", operator.sub), +                ("*", operator.mul), +            )          ) -    ) -    # if the operator is multiplication, lower the values of the two operands to make it easier -    if oper[0] == "*": -        a -= 5 -        b -= 5 +        # if the operator is multiplication, lower the values of the two operands to make it easier +        if oper[0] == "*": +            a -= 5 +            b -= 5 -    question = q_format.format(a, oper[0], b) -    answer = a_format.format(oper[1](a, b)) +        question = q_format.format(a, oper[0], b) +        answer = a_format.format(oper[1](a, b)) -    return QuizEntry(question, answer) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) +    @classmethod +    def solar_system(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a question on the planets of the Solar System.""" +        planet = random.choice(cls.PLANETS) -def solar_system(q_format: str, a_format: str) -> QuizEntry: -    """Generate a question on the planets of the Solar System.""" -    planet = random.choice(PLANETS) +        question = q_format.format(planet[0]) +        answer = a_format.format(planet[1]) -    question = q_format.format(planet[0]) -    answer = a_format.format(planet[1]) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) -    return QuizEntry(question, answer) +    @classmethod +    def taxonomic_rank(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a question on taxonomic classification.""" +        level = random.randint(0, len(cls.TAXONOMIC_HIERARCHY) - 2) +        question = q_format.format(cls.TAXONOMIC_HIERARCHY[level]) +        answer = a_format.format(cls.TAXONOMIC_HIERARCHY[level + 1]) -def taxonomic_rank(q_format: str, a_format: str) -> QuizEntry: -    """Generate a question on taxonomic classification.""" -    level = random.randint(0, len(TAXONOMIC_HIERARCHY) - 2) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE) -    question = q_format.format(TAXONOMIC_HIERARCHY[level]) -    answer = a_format.format(TAXONOMIC_HIERARCHY[level + 1]) +    @classmethod +    def base_units_convert(cls, q_format: str, a_format: str) -> QuizEntry: +        """Generate a SI base units conversion question.""" +        unit = random.choice(list(cls.UNITS_TO_BASE_UNITS)) -    return QuizEntry(question, answer) - - -def base_units_convert(q_format: str, a_format: str) -> QuizEntry: -    """Generate a SI base units conversion question.""" -    unit = random.choice(list(UNITS_TO_BASE_UNITS)) - -    question = q_format.format( -        unit + " " + UNITS_TO_BASE_UNITS[unit][0] -    ) -    answer = a_format.format( -        UNITS_TO_BASE_UNITS[unit][1] -    ) +        question = q_format.format( +            unit + " " + cls.UNITS_TO_BASE_UNITS[unit][0] +        ) +        answer = a_format.format( +            cls.UNITS_TO_BASE_UNITS[unit][1] +        ) -    return QuizEntry(question, answer) +        return QuizEntry(question, [answer], DYNAMICALLY_GEN_VARIATION_TOLERANCE)  DYNAMIC_QUESTIONS_FORMAT_FUNCS = { -    201: linear_system, -    202: mod_arith, -    203: ngonal_prism, -    204: imag_sqrt, -    205: binary_calc, -    301: solar_system, -    302: taxonomic_rank, -    303: base_units_convert, +    201: DynamicQuestionGen.linear_system, +    202: DynamicQuestionGen.mod_arith, +    203: DynamicQuestionGen.ngonal_prism, +    204: DynamicQuestionGen.imag_sqrt, +    205: DynamicQuestionGen.binary_calc, +    301: DynamicQuestionGen.solar_system, +    302: DynamicQuestionGen.taxonomic_rank, +    303: DynamicQuestionGen.base_units_convert,  } @@ -202,7 +223,7 @@ class TriviaQuiz(commands.Cog):          self.questions = self.load_questions()          self.question_limit = 0 -        self.player_scores = {}  # A variable to store all player's scores for a bot session. +        self.player_scores = defaultdict(int)  # A variable to store all player's scores for a bot session.          self.game_player_scores = {}  # A variable to store temporary game player's scores.          self.categories = { @@ -212,8 +233,72 @@ class TriviaQuiz(commands.Cog):              "science": "Put your understanding of science to the test!",              "cs": "A large variety of computer science questions.",              "python": "Trivia on our amazing language, Python!", +            "wikipedia": "Guess the title of random wikipedia passages.",          } +        self.get_wiki_questions.start() + +    def cog_unload(self) -> None: +        """Cancel `get_wiki_questions` task when Cog will unload.""" +        self.get_wiki_questions.cancel() + +    @tasks.loop(hours=24.0) +    async def get_wiki_questions(self) -> None: +        """Get yesterday's most read articles from wikipedia and format them like trivia questions.""" +        error_fetches = 0 +        wiki_questions = [] +        # trivia_quiz.json follows a pattern, every new category starts with the next century. +        start_id = 501 +        yesterday = datetime.strftime(datetime.now() - timedelta(1), '%Y/%m/%d') + +        while error_fetches < MAX_ERROR_FETCH_TRIES: +            async with self.bot.http_session.get(url=WIKI_FEED_API_URL.format(date=yesterday)) as r: +                if r.status != 200: +                    error_fetches += 1 +                    continue +                raw_json = await r.json() +                articles_raw = raw_json["mostread"]["articles"] + +                for article in articles_raw: +                    question = article.get("extract") +                    if not question: +                        continue + +                    # Normalize the wikipedia article title to remove all punctuations from it +                    for word in re.split(r"[\s-]", title := article["normalizedtitle"]): +                        cleaned_title = re.sub( +                            rf'\b{word.strip(string.punctuation)}\b', word, title, flags=re.IGNORECASE +                        ) + +                    # Since the extract contains the article name sometimes this would replace all the matching words +                    # in that article with *** of that length. +                    # NOTE: This removes the "answer" for 99% of the cases, but sometimes the wikipedia article is +                    # very different from the words in the extract, for example the title would be the nickname of a +                    # person (Bob Ross) whereas in the extract it would the full name (Robert Norman Ross) so it comes +                    # out as (Robert Norman ****) and (Robert Norman Ross) won't be a right answer :( +                    for word in re.split(r"[\s-]", cleaned_title): +                        word = word.strip(string.punctuation) +                        secret_word = r"\*" * len(word) +                        question = re.sub(rf'\b{word}\b', f"**{secret_word}**", question, flags=re.IGNORECASE) + +                    formatted_article_question = { +                        "id": start_id, +                        "question": f"Guess the title of the Wikipedia article.\n\n{question}", +                        "answer": cleaned_title, +                        "info": article["extract"] +                    } +                    start_id += 1 +                    wiki_questions.append(formatted_article_question) + +                # If everything has gone smoothly until now, we can break out of the while loop +                break + +        if error_fetches < MAX_ERROR_FETCH_TRIES: +            self.questions["wikipedia"] = wiki_questions.copy() +        else: +            del self.categories["wikipedia"] +            logger.warning(f"Not loading wikipedia guess questions, hit max error fetches: {MAX_ERROR_FETCH_TRIES}.") +      @staticmethod      def load_questions() -> dict:          """Load the questions from the JSON file.""" @@ -221,7 +306,7 @@ class TriviaQuiz(commands.Cog):          return json.loads(p.read_text(encoding="utf-8")) -    @commands.group(name="quiz", aliases=["trivia"], invoke_without_command=True) +    @commands.group(name="quiz", aliases=("trivia", "triviaquiz"), invoke_without_command=True)      async def quiz_game(self, ctx: commands.Context, category: Optional[str], questions: Optional[int]) -> None:          """          Start a quiz! @@ -233,6 +318,7 @@ class TriviaQuiz(commands.Cog):          - science: Put your understanding of science to the test!          - cs: A large variety of computer science questions.          - python: Trivia on our amazing language, Python! +        - wikipedia: Guess the title of random wikipedia passages.          (More to come!)          """ @@ -264,7 +350,7 @@ class TriviaQuiz(commands.Cog):          topic_length = len(topic)          if questions is None: -            self.question_limit = DEFAULT_QUESTION_LIMIT +            self.question_limit = min(DEFAULT_QUESTION_LIMIT, topic_length)          else:              if questions > topic_length:                  await ctx.send( @@ -279,13 +365,13 @@ class TriviaQuiz(commands.Cog):                  await ctx.send(                      embed=self.make_error_embed(                          "You must choose to complete at least one question. " -                        f"(or enter nothing for the default value of {DEFAULT_QUESTION_LIMIT + 1} questions)" +                        f"(or enter nothing for the default value of {DEFAULT_QUESTION_LIMIT} questions)"                      )                  )                  return              else: -                self.question_limit = questions - 1 +                self.question_limit = questions          # Start game if not running.          if not self.game_status[ctx.channel.id]: @@ -296,13 +382,13 @@ class TriviaQuiz(commands.Cog):              await ctx.send(embed=start_embed)  # send an embed with the rules              await asyncio.sleep(5) -        done_question = [] +        done_questions = []          hint_no = 0 -        answers = None +        quiz_entry = None          while self.game_status[ctx.channel.id]:              # Exit quiz if number of questions for a round are already sent. -            if len(done_question) > self.question_limit and hint_no == 0: +            if len(done_questions) == self.question_limit and hint_no == 0:                  await ctx.send("The round has ended.")                  await self.declare_winner(ctx.channel, self.game_player_scores[ctx.channel.id]) @@ -317,32 +403,27 @@ class TriviaQuiz(commands.Cog):                  # Select a random question which has not been used yet.                  while True:                      question_dict = random.choice(topic) -                    if question_dict["id"] not in done_question: -                        done_question.append(question_dict["id"]) +                    if question_dict["id"] not in done_questions: +                        done_questions.append(question_dict["id"])                          break                  if "dynamic_id" not in question_dict: -                    question = question_dict["question"] -                    answers = question_dict["answer"].split(", ") - -                    var_tol = STANDARD_VARIATION_TOLERANCE +                    quiz_entry = QuizEntry( +                        question_dict["question"], +                        quiz_answers if isinstance(quiz_answers := question_dict["answer"], list) else [quiz_answers], +                        STANDARD_VARIATION_TOLERANCE +                    )                  else:                      format_func = DYNAMIC_QUESTIONS_FORMAT_FUNCS[question_dict["dynamic_id"]] -                      quiz_entry = format_func(                          question_dict["question"],                          question_dict["answer"],                      ) -                    question, answers = quiz_entry.question, quiz_entry.answer -                    answers = [answers] - -                    var_tol = DYNAMICALLY_GEN_VARIATION_TOLERANCE -                  embed = discord.Embed(                      colour=Colours.gold, -                    title=f"Question #{len(done_question)}", -                    description=question, +                    title=f"Question #{len(done_questions)}", +                    description=quiz_entry.question,                  )                  if img_url := question_dict.get("img_url"): @@ -354,13 +435,13 @@ class TriviaQuiz(commands.Cog):                  def contains_correct_answer(m: discord.Message) -> bool:                      return m.channel == ctx.channel and any(                          fuzz.ratio(answer.lower(), m.content.lower()) > variation_tolerance -                        for answer in answers +                        for answer in quiz_entry.answers                      )                  return contains_correct_answer              try: -                msg = await self.bot.wait_for("message", check=check_func(var_tol), timeout=10) +                msg = await self.bot.wait_for("message", check=check_func(quiz_entry.var_tol), timeout=10)              except asyncio.TimeoutError:                  # In case of TimeoutError and the game has been stopped, then do nothing.                  if not self.game_status[ctx.channel.id]: @@ -388,10 +469,10 @@ class TriviaQuiz(commands.Cog):                      await self.send_answer(                          ctx.channel, -                        answers, +                        quiz_entry.answers,                          False,                          question_dict, -                        self.question_limit - len(done_question) + 1, +                        self.question_limit - len(done_questions),                      )                      await asyncio.sleep(1) @@ -421,10 +502,10 @@ class TriviaQuiz(commands.Cog):                  await self.send_answer(                      ctx.channel, -                    answers, +                    quiz_entry.answers,                      True,                      question_dict, -                    self.question_limit - len(done_question) + 1, +                    self.question_limit - len(done_questions),                  )                  await self.send_score(ctx.channel, self.game_player_scores[ctx.channel.id]) @@ -432,19 +513,18 @@ class TriviaQuiz(commands.Cog):      def make_start_embed(self, category: str) -> discord.Embed:          """Generate a starting/introduction embed for the quiz.""" +        rules = "\n".join([f"{index}: {rule}" for index, rule in enumerate(RULES, start=1)]) +          start_embed = discord.Embed( -            colour=Colours.blue, -            title="A quiz game is starting!", +            title="Quiz game Starting!!",              description=( -                f"This game consists of {self.question_limit + 1} questions.\n\n" -                "**Rules: **\n" -                "1. Only enclose your answer in backticks when the question tells you to.\n" -                "2. If the question specifies an answer format, follow it or else it won't be accepted.\n" -                "3. You have 30s per question. Points for each question reduces by 25 after 10s or after a hint.\n" -                "4. No cheating and have fun!\n\n" -                f"**Category**: {category}" +                f"Each game consists of {self.question_limit} questions.\n" +                f"**Rules :**\n{rules}" +                f"\n **Category** : {category}"              ), +            colour=Colours.blue          ) +        start_embed.set_thumbnail(url=TRIVIA_QUIZ_ICON)          return start_embed @@ -503,6 +583,7 @@ class TriviaQuiz(commands.Cog):              title="Score Board",              description="",          ) +        embed.set_thumbnail(url=TRIVIA_QUIZ_ICON)          sorted_dict = sorted(player_data.items(), key=operator.itemgetter(1), reverse=True)          for item in sorted_dict: @@ -534,8 +615,8 @@ class TriviaQuiz(commands.Cog):                  winners_mention = winner.mention              await channel.send( -                f"Congratulations {winners_mention} :tada: " -                f"You have won this quiz game with a grand total of {highest_points} points!" +                f"{winners_mention} Congratulations " +                f"on winning this quiz game with a grand total of {highest_points} points :tada:"              )      def category_embed(self) -> discord.Embed: @@ -578,7 +659,8 @@ class TriviaQuiz(commands.Cog):              description="",          ) -        if info is not None: +        # Don't check for info is not None, as we want to filter out empty strings. +        if info:              embed.description += f"**Information**\n{info}\n\n"          embed.description += ( diff --git a/bot/resources/fun/trivia_quiz.json b/bot/resources/fun/trivia_quiz.json index 8008838c..0b3e6802 100644 --- a/bot/resources/fun/trivia_quiz.json +++ b/bot/resources/fun/trivia_quiz.json @@ -52,7 +52,7 @@          "They generally have handdrawn nature images on them."        ],        "question": "What did Nintendo make before video games and toys?", -      "answer": "Hanafuda, Hanafuda cards" +      "answer": ["Hanafuda", "Hanafuda cards"]      },      {        "id": 7, @@ -292,22 +292,22 @@      {        "id": 201,        "question": "What is the highest power of a biquadratic polynomial?", -      "answer": "4, four" +      "answer": ["4", "four"]      },      {        "id": 202,        "question": "What is the formula for surface area of a sphere?", -      "answer": "4pir^2, 4πr^2" +      "answer": ["4pir^2", "4πr^2"]      },      {        "id": 203,        "question": "Which theorem states that hypotenuse^2 = base^2 + height^2?", -      "answer": "Pythagorean's, Pythagorean's theorem" +      "answer": ["Pythagorean's", "Pythagorean's theorem"]      },      {        "id": 204,        "question": "Which trigonometric function is defined as hypotenuse/opposite?", -      "answer": "cosecant, cosec, csc" +      "answer": ["cosecant", "cosec", "csc"]      },      {        "id": 205, @@ -317,7 +317,7 @@      {        "id": 206,        "question": "How many quadrants are there in a cartesian plane?", -      "answer": "4, four" +      "answer": ["4", "four"]      },      {        "id": 207, @@ -328,7 +328,7 @@        "id": 208,        "question": "What's the following formula that finds the area of a triangle called?",        "img_url": "https://wikimedia.org/api/rest_v1/media/math/render/png/d22b8566e8187542966e8d166e72e93746a1a6fc", -      "answer": "Heron's formula, Heron" +      "answer": ["Heron's formula", "Heron"]      },      {        "id": 209, @@ -372,12 +372,12 @@      {        "id": 216,        "question": "In set builder notation, what does {p/q | q ≠ 0, p & q ∈ Z} represent?", -      "answer": "Rationals, Rational Numbers" +      "answer": ["Rationals", "Rational Numbers"]      },      {        "id": 217,        "question": "What is the natural log of -1 (use i for imaginary number)?", -      "answer": "pi*i, pii, πi" +      "answer": ["pi*i", "pii", "πi"]      },      {        "id": 218, @@ -397,7 +397,7 @@      {        "id": 221,        "question": "Prime numbers only have __ factors.", -      "answer": "2, two" +      "answer": ["2", "two"]      },      {        "id": 222, @@ -408,7 +408,7 @@        "id": 223,        "question": "In statistics, what does this formula represent?",        "img_url": "https://www.statisticshowto.com/wp-content/uploads/2013/11/sample-standard-deviation.jpg", -      "answer": "sample standard deviation, standard deviation of a sample" +      "answer": ["sample standard deviation", "standard deviation of a sample"]      },      {        "id": 224, @@ -418,7 +418,7 @@      {        "id": 225,        "question": "A matrix multiplied by its inverse matrix equals...", -      "answer": "the identity matrix, identity matrix" +      "answer": ["the identity matrix", "identity matrix"]      },      {        "id": 226, @@ -429,19 +429,19 @@      {        "id": 227,        "question": "What is the only number in the entire number system which can be spelled with the same number of letters as itself?", -      "answer": "4, four" +      "answer": ["4", "four"]      },      {        "id": 228,        "question": "1/100th of a second is also termed as what?", -      "answer": "a jiffy, jiffy, centisecond" +      "answer": ["a jiffy", "jiffy", "centisecond"]      },      {        "id": 229,        "question": "What is this triangle called?",        "img_url": "https://cdn.askpython.com/wp-content/uploads/2020/07/Pascals-triangle.png", -      "answer": "Pascal's triangle, Pascal" +      "answer": ["Pascal's triangle", "Pascal"]      },      {        "id": 230, @@ -468,17 +468,17 @@      {        "id": 304,        "question": "What do you call an organism composed of only one cell?", -      "answer": "unicellular, single-celled" +      "answer": ["unicellular", "single-celled"]      },      {        "id": 305,        "question": "The Heisenberg's Uncertainty Principle states that the position and \\_\\_\\_\\_\\_\\_\\_\\_ of a quantum object can't be both exactly measured at the same time.", -      "answer": "velocity, momentum" +      "answer": ["velocity", "momentum"]      },      {        "id": 306,        "question": "A \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_ reaction is the one wherein an atom or a set of atoms is/are replaced by another atom or a set of atoms", -      "answer": "displacement, exchange" +      "answer": ["displacement", "exchange"]      },      {        "id": 307, @@ -512,7 +512,7 @@      {        "id": 312,        "question": "What is the intermolecular force caused by temporary and induced dipoles?", -      "answer": "LDF, London dispersion, London dispersion force" +      "answer": ["LDF", "London dispersion", "London dispersion force"]      },      {        "id": 313, @@ -522,7 +522,7 @@      {        "id": 314,        "question": "About how many neurons are in the human brain?\n(A. 1 billion, B. 10 billion, C. 100 billion, D. 300 billion)", -      "answer": "C, 100 billion, 100 bil" +      "answer": ["C", "100 billion", "100 bil"]      },      {        "id": 315, @@ -537,12 +537,12 @@      {        "id": 317,        "question": "Which vascular tissue transports water and minerals from the roots to the rest of a plant?", -      "answer": "the xylem, xylem" +      "answer": ["the xylem", "xylem"]      },      {        "id": 318,        "question": "Who discovered the theories of relativity?", -      "answer": "Albert Einstein, Einstein" +      "answer": ["Albert Einstein", "Einstein"]      },      {        "id": 319, @@ -557,7 +557,7 @@      {        "id": 321,        "question": "What range of frequency are the average human ears capable of hearing?\n(A. 10Hz-10kHz, B. 20Hz-20kHz, C. 20Hz-2000Hz, D. 10kHz-20kHz)", -      "answer": "B, 20Hz-20kHz" +      "answer": ["B", "20Hz-20kHz"]      },      {        "id": 322, @@ -572,7 +572,7 @@      {        "id": 324,        "question": "The type of rock that is formed by the accumulation or deposition of mineral or organic particles at the Earth's surface, followed by cementation, is called...", -      "answer": "sedimentary, sedimentary rock" +      "answer": ["sedimentary", "sedimentary rock"]      },      {        "id": 325, @@ -582,7 +582,7 @@      {        "id": 326,        "question": "What type of image is formed by a convex mirror?", -      "answer": "virtual image, virtual" +      "answer": ["virtual image", "virtual"]      },      {        "id": 327, @@ -592,17 +592,17 @@      {        "id": 328,        "question": "Which law states that the global entropy in a closed system can only increase?", -      "answer": "second law, second law of thermodynamics" +      "answer": ["second law", "second law of thermodynamics"]      },      {        "id": 329,        "question": "Which particle is emitted during the beta decay of a radioactive element?", -      "answer": "an electron, the electron, electron" +      "answer": ["an electron", "the electron", "electron"]      },      {        "id": 330,        "question": "When DNA is unzipped, two strands are formed. What are they called (separate both answers by the word \"and\")?", -      "answer": "leading and lagging, leading strand and lagging strand" +      "answer": ["leading and lagging", "leading strand and lagging strand"]      }    ],    "cs": [ @@ -619,7 +619,7 @@      {        "id": 403,        "question": "What does SASS stand for?", -      "answer": "Syntactically Awesome Stylesheets, Syntactically Awesome Style Sheets" +      "answer": ["Syntactically Awesome Stylesheets", "Syntactically Awesome Style Sheets"]      },      {        "id": 404, @@ -629,7 +629,7 @@      {        "id": 405,        "question": "What is computing capable of performing exaFLOPS called?", -      "answer": "exascale computing, exascale" +      "answer": ["exascale computing", "exascale"]      },      {        "id": 406, @@ -654,7 +654,7 @@      {        "id": 410,        "question": "A hypothetical point in time at which technological growth becomes uncontrollable and irreversible, resulting in unforeseeable changes to human civilization is termed as...?", -      "answer": "technological singularity, singularity" +      "answer": ["technological singularity", "singularity"]      },      {        "id": 411, @@ -664,12 +664,12 @@      {        "id": 412,        "question": "How many bits are in a TCP checksum header?", -      "answer": "16, sixteen" +      "answer": ["16", "sixteen"]      },      {        "id": 413,        "question": "What is the most popular protocol (as of 2021) that handles communication between email servers?", -      "answer": "SMTP, Simple Mail Transfer Protocol" +      "answer": ["SMTP", "Simple Mail Transfer Protocol"]      },      {        "id": 414, @@ -679,12 +679,12 @@      {        "id": 415,        "question": "Which DNS record contains mail servers of a given domain?", -      "answer": "MX, mail exchange" +      "answer": ["MX", "mail exchange"]      },      {        "id": 416,        "question": "Which newline sequence does HTTP use?", -      "answer": "carriage return line feed, CRLF, \\r\\n" +      "answer": ["carriage return line feed", "CRLF", "\\r\\n"]      },      {        "id": 417, @@ -694,7 +694,7 @@      {        "id": 418,        "question": "Name a universal logic gate.", -      "answer": "NAND, NOR" +      "answer": ["NAND", "NOR"]      },      {        "id": 419, @@ -729,7 +729,7 @@      {        "id": 425,        "question": "What does the \"a\" represent in a HSLA color value?", -      "answer": "transparency, translucency, alpha value, alpha channel, alpha" +      "answer": ["transparency", "translucency", "alpha value", "alpha channel", "alpha"]      },      {        "id": 426, @@ -749,7 +749,7 @@      {        "id": 429,        "question": "Which of these sorting algorithms is not stable?\n(Counting sort, quick sort, insertion sort, tim sort, bubble sort)", -      "answer": "quick, quick sort" +      "answer": ["quick", "quick sort"]      },      {        "id": 430, @@ -761,7 +761,7 @@      {        "id": 501,        "question": "Is everything an instance of the `object` class (y/n)?", -      "answer": "y, yes" +      "answer": ["y", "yes"]      },      {        "id": 502, @@ -781,7 +781,7 @@      {        "id": 505,        "question": "Can you pickle a running `list_iterator` (y/n)?", -      "answer": "y, yes" +      "answer": ["y", "yes"]      },      {        "id": 506, @@ -821,12 +821,12 @@      {        "id": 513,        "question": "Where does the name Python come from?", -      "answer": "Monty Python, Monty Python's Flying Circus" +      "answer": ["Monty Python", "Monty Python's Flying Circus"]      },      {        "id": 514,        "question": "How is infinity represented in Python?", -      "answer": "float(\"infinity\"), float('infinity'), float(\"inf\"), float('inf')" +      "answer": ["float(\"infinity\")", "float('infinity')", "float(\"inf\")", "float('inf')"]      },      {        "id": 515, @@ -846,12 +846,12 @@      {        "id": 518,        "question": "What decorator is used to allow a protocol to be checked at runtime?", -      "answer": "runtime_checkable, typing.runtime_checkable" +      "answer": ["runtime_checkable", "typing.runtime_checkable"]      },      {        "id": 519,        "question": "Does `numbers.Rational` include the builtin object float (y/n)", -      "answer": "n, no" +      "answer": ["n", "no"]      },      {        "id": 520, @@ -866,7 +866,7 @@      {        "id": 522,        "question": "What is the garbage collection strategy used by cpython to collect everything but reference cycles?", -      "answer": "reference counting, refcounting" +      "answer": ["reference counting", "refcounting"]      },      {        "id": 523, @@ -891,12 +891,12 @@      {        "id": 527,        "question": "Is the `__aiter__` method async (y/n)?", -      "answer": "n, no" +      "answer": ["n", "no"]      },      {        "id": 528,        "question": "How does one call a class who defines the behavior of their instance classes?", -      "answer": "a metaclass, metaclass" +      "answer": ["a metaclass", "metaclass"]      },      {        "id": 529, | 
