aboutsummaryrefslogtreecommitdiffstats
path: root/bot
diff options
context:
space:
mode:
authorGravatar Rohan <[email protected]>2019-08-27 17:32:50 +0530
committerGravatar Rohan <[email protected]>2019-08-27 17:32:50 +0530
commitc558d88e74fa6e638dd38bc1232e2839ba72c8da (patch)
tree386920f795b7e7c56ae47f233d7b8a35bcf49273 /bot
parentMerge pull request #265 from vivax3794/losing_addintions (diff)
A fun trivia quiz that can be played on multiple channelsThe trivia quiz has the following features: quiz has the following features:
- There wont be any options to the questions asked, the users have to type the answer in the chat and the bot will pick up the right anwer - If 3 questions have not been answered in a row, the quiz will be brought to a halt assuming inactivity. - There are also hints in this game! A hint will be sent after 10s after the question has been sent.Each question has a maximum if 2 hints. - The bot checks for the closest answer rather than the exact one(Incase of typoes in the users answer
Diffstat (limited to 'bot')
-rw-r--r--bot/resources/evergreen/trivia_quiz.json34
-rw-r--r--bot/seasons/evergreen/TriviaQuiz.py263
2 files changed, 297 insertions, 0 deletions
diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json
new file mode 100644
index 00000000..f884e67a
--- /dev/null
+++ b/bot/resources/evergreen/trivia_quiz.json
@@ -0,0 +1,34 @@
+{
+ "retro": [
+ {
+ "id": 1,
+ "hints": ["It was not a mainline Mario Game, although the plumber was present.", "It was not a mainline Zelda Game, although Link was present."],
+ "question": "What was the best selling game on the Nintendo Gamecube?",
+ "answer": "Super Smash Bros"
+ },
+ {
+ "id": 2,
+ "hints": ["It was released before the 90s.", "It was released after 1980."],
+ "question": "What year was Tetris released?",
+ "answer": "1984"
+ },
+ {
+ "id": 3,
+ "hints": ["The occupation was in construction", "He appeared as this kind of worker in 1981's Donkey Kong"],
+ "question": "What was Mario's original occupation?",
+ "answer": "Carpenter"
+ },
+ {
+ "id": 4,
+ "hints": ["It was revelead in the Nintendo Character Guide in 1993.", "His last name has to do with eating Mario's enemies."],
+ "question": "What is Yoshi's(from Mario Bros) full name?",
+ "answer": "Yoshisaur Munchakoopas"
+ },
+ {
+ "id": 5,
+ "hints": ["The game was released in 1990.", "It was released on the SNES."],
+ "question": "What was the first game Yoshi appeared in?",
+ "answer": "Super Mario World"
+ }
+ ]
+}
diff --git a/bot/seasons/evergreen/TriviaQuiz.py b/bot/seasons/evergreen/TriviaQuiz.py
new file mode 100644
index 00000000..38f19922
--- /dev/null
+++ b/bot/seasons/evergreen/TriviaQuiz.py
@@ -0,0 +1,263 @@
+import asyncio
+import logging
+import random
+from dataclasses import dataclass
+from json import load
+from pathlib import Path
+
+import discord
+from discord.ext import commands
+from fuzzywuzzy import fuzz
+
+from bot.constants import Roles
+
+
+logger = logging.getLogger(__name__)
+
+
+annoyed_expressions = ["-_-", "-.-"]
+
+wrong_ans_responses = [
+ "No one gave the correct answer",
+ "Losers",
+ "You guys really need to learn"
+]
+
+
+@dataclass
+class GameData:
+ """A dataclass for game data."""
+
+ owner: int
+ players = []
+ points = []
+ done_questions = []
+ category: str
+ question: dict = None
+ hints = 0
+ unanswered_questions = 0
+
+
+class TriviaQuiz(commands.Cog):
+ """A cog for all quiz commands."""
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.questions = self.load_questions()
+ self.games = {} # channel as key and value as instinct of dataclass GameData
+ self.categories = {
+ "retro": "Questions related to retro gaming."
+ }
+ self.inactivity_limit = 3 # Number of questions unanswered in a row after which quiz stops.
+
+ @staticmethod
+ def load_questions():
+ """Load the questions from json file."""
+ p = Path("bot", "resources", "evergreen", "trivia_quiz.json ")
+ with p.open() as json_data:
+ questions = load(json_data)
+ return questions
+
+ @commands.group(name="tquiz", invoke_without_command=False)
+ async def tquiz(self, ctx):
+ """Trivia Quiz game for fun!"""
+ await ctx.send_help("tquiz")
+
+ @tquiz.command(name="start")
+ async def start(self, ctx, category=None):
+ """Start a quiz!
+
+ Questions for the quiz can be selected from the following categories:
+ - Retro : questions related to retro gaming.
+ """
+ await ctx.send("Quiz triggered! Gonna start in a couple of seconds...")
+ await asyncio.sleep(1)
+
+ # Checking if there is already a game running in that channel.
+ if ctx.channel.id in list(self.games.keys()):
+ return await ctx.send("Game already running in this channel!")
+
+ if category is None:
+ category = random.choice(list(self.categories.keys()))
+
+ else:
+ category = category.lower()
+ if category not in self.categories.keys():
+ embed = self.category_embed()
+ return await ctx.send(f"Category {category} does not exist!", embed=embed)
+
+ self.games[ctx.channel.id] = GameData(
+ owner=ctx.author.id,
+ category=category
+ )
+
+ await self.send_question(ctx.channel)
+
+ def category_embed(self):
+ """A function which returns an embed showing all avilable categories"""
+ embed = discord.Embed(colour=discord.Colour.blue())
+ embed.title = "The available question categories are:"
+ embed.description = ""
+ for cat, description in self.categories.items():
+ embed.description += f"**- {cat.capitalize()}**\n{description.capitalize()}\n"
+ embed.set_footer(text="If not category is chosen, then a random one will be selected.")
+ return embed
+
+ async def send_question(self, channel):
+ """This function is to be called whenever a question needs to be sent."""
+ await asyncio.sleep(2)
+ game = self.games[channel.id]
+ if game.unanswered_questions == self.inactivity_limit:
+ del self.games[channel.id]
+ return await channel.send("Game stopped due to inactivity.")
+
+ category = game.category
+ category_dict = self.questions[category]
+ question_dict = random.choice(category_dict)
+
+ same_question = True
+
+ # Making sure a question is not repeated.
+ while same_question is True:
+ question_dict = random.choice(category_dict)
+ if question_dict["id"] not in game.done_questions:
+ same_question = False
+ else:
+ pass
+
+ # Initial points for a question.
+ question_dict["points"] = 100
+ game.question = question_dict
+ game.hints = 0
+
+ embed = discord.Embed(colour=discord.Colour.dark_gold())
+
+ question = question_dict["question"]
+ question_id = question_dict["id"]
+
+ game.done_questions.append(question_id)
+ question_number = len(game.done_questions)
+
+ embed.title = f"#{question_number} Question"
+ embed.description = question
+ embed.set_footer(text="A hint will be provided after every 10s if no one gives the right answer.Max of 2 hints")
+
+ await channel.send(embed=embed)
+ await self.send_hint(channel, question_dict)
+
+ @commands.Cog.listener()
+ async def on_message(self, message):
+ """A function triggered when a message is sent."""
+ channel = message.channel
+ if channel.id not in list(self.games.keys()):
+ return
+ if message.author.bot:
+ return
+
+ game = self.games[message.channel.id]
+ question_data = game.question
+ answer = question_data["answer"].lower()
+ user_answer = message.content.lower()
+ ratio = fuzz.ratio(answer, user_answer)
+ if ratio > 84:
+ points = question_data["points"] - game.hints*25
+ if message.author in game.players:
+ author_index = game.players.index(message.author)
+ game.points[author_index] = game.points[author_index] + points
+ else:
+ game.players.append(message.author)
+ game.points.append(points)
+
+ await channel.send(f"{message.author.mention} got it right! Good job :tada:"
+ f"You got {points} points.")
+ await self.score_embed(channel)
+ await self.send_question(channel)
+ elif ratio in range(75, 84):
+ await channel.send(f"Your close to the answer {message.author.mention}")
+
+ async def send_hint(self, channel, question_dict):
+ """Function to be called whenever a hint has to be sent."""
+ await asyncio.sleep(10)
+ try:
+ game = self.games[channel.id]
+ except KeyError:
+ return
+
+ # Checking if the question is the same after 10 seconds.
+ if question_dict["id"] == game.question["id"]:
+
+ # If the max number of hints is already reached, then send the answer.
+ if 2 - game.hints == 0:
+ return await self.send_answer(channel)
+
+ hint_list = question_dict["hints"]
+ hint_index = game.hints
+ hint = hint_list[hint_index]
+ game.hints += 1
+ message = f"**Hint {game.hints}**: {hint}\n*Number of hints remaining: {2-game.hints}*"
+ await channel.send(message)
+ await self.send_hint(channel, question_dict)
+ else:
+ pass
+
+ async def send_answer(self, channel):
+ """A function to send the answer in the channel if no user have given the correct answer even after 2 hints."""
+ game = self.games[channel.id]
+ answer = game.question["answer"]
+ response = random.choice(wrong_ans_responses)
+ expression = random.choice(annoyed_expressions)
+ await channel.send(f"{response} {expression}, the correct answer is **{answer}**.")
+ self.games[channel.id].unanswered_questions += 1
+ await self.score_embed(channel)
+ await self.send_question(channel)
+
+ @tquiz.command(name="score")
+ async def send_score(self, ctx):
+ """Show scoreboard of the game running in this channel."""
+ await self.score_embed(ctx.channel)
+
+ async def score_embed(self, channel):
+ """Show score of each player in the quiz."""
+ if channel.id not in list(self.games.keys()):
+ return await channel.send("There are no games running in this channel!")
+ game = self.games[channel.id]
+ players = game.players
+ if len(players) == 0:
+ return
+ points = game.points
+ embed = discord.Embed(color=discord.Colour.dark_gold())
+ embed.title = "Scoreboard"
+ embed.description = ""
+ for player, score in zip(players, points):
+ embed.description = f"{player} - {score}\n"
+ await channel.send(embed=embed)
+
+ @tquiz.command(name="stop")
+ async def stop_quiz(self, ctx):
+ """Stop the quiz."""
+ if ctx.channel.id not in list(self.games.keys()):
+ return await ctx.send("No game running, nothing to stop here -.-")
+ game = self.games[ctx.channel.id]
+ owner = game.owner
+ mods = Roles.moderator
+ if ctx.author.id == owner or mods in [role.id for role in ctx.author.roles]:
+ await ctx.send("Game is not running anymore!")
+ await self.score_embed(ctx.channel)
+ if game.players:
+ highest_points = max(game.points)
+ author_index = game.points.index(highest_points)
+ winner = game.players[author_index]
+ await ctx.send(
+ f"Congratz {winner.mention} :tada: "
+ f"You have won this quiz game with a grand total of {highest_points} points!!"
+ )
+ await asyncio.sleep(2)
+ del self.games[ctx.channel.id]
+ else:
+ await ctx.send("You are not authorised to close this game!")
+
+
+def setup(bot):
+ """Loading the cog."""
+ bot.add_cog(TriviaQuiz(bot))
+ logger.debug("TriviaQuiz cog loaded!")