aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar RohanJnr <[email protected]>2019-09-23 19:00:32 +0530
committerGravatar RohanJnr <[email protected]>2019-09-23 19:00:32 +0530
commit207efa437e3b585250f7b36e66ab262a0acc3217 (patch)
tree341e9ebb0733fc73ef210abda2379a97d2ef371b
parentusing self.categories insted of list(self.categories.keys()) (diff)
Modified the code drastically for the quiz game:
- Using a while loop to run a game rather than bunch of the functions calling each other. - using wait_for() to send hints, answers and to move to next questions.
-rw-r--r--bot/resources/evergreen/trivia_quiz.json3
-rw-r--r--bot/seasons/evergreen/travia_quiz.py225
-rw-r--r--bot/seasons/evergreen/trivia_quiz.py266
3 files changed, 227 insertions, 267 deletions
diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json
index f884e67a..62856863 100644
--- a/bot/resources/evergreen/trivia_quiz.json
+++ b/bot/resources/evergreen/trivia_quiz.json
@@ -30,5 +30,6 @@
"question": "What was the first game Yoshi appeared in?",
"answer": "Super Mario World"
}
- ]
+ ],
+ "general":[]
}
diff --git a/bot/seasons/evergreen/travia_quiz.py b/bot/seasons/evergreen/travia_quiz.py
new file mode 100644
index 00000000..84a16654
--- /dev/null
+++ b/bot/seasons/evergreen/travia_quiz.py
@@ -0,0 +1,225 @@
+import asyncio
+import json
+import logging
+import random
+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"
+]
+
+game_rules = [
+ "No cheating!",
+ "Idk what rules to put in here haha."
+
+]
+
+
+class TriviaQuiz(commands.Cog):
+ """A cog for all quiz commands."""
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.questions = self.load_questions()
+ self.game_status = {}
+ self.game_owners = {}
+ self.question_limit = 3
+ self.categories = {
+ "general": "Test your general knwoledge",
+ "retro": "Questions related to retro gaming."
+ }
+
+ @staticmethod
+ def load_questions() -> dict:
+ """Load the questions from json file."""
+ p = Path("bot", "resources", "evergreen", "trivia_quiz.json")
+ with p.open() as json_data:
+ questions = json.load(json_data)
+ return questions
+
+ @commands.command(name="quiz")
+ async def start(self, ctx, option: str, category: str = "retro") -> None:
+ """
+ Start/Stop a quiz!
+
+ arguments:
+ option:
+ - start : to start a quiz in a channel
+ - stop : stop the quiz running in that channel.
+
+ Questions for the quiz can be selected from the following categories:
+ - general : Test your general knowledge. (default)
+ - Retro : questions related to retro gaming.
+ """
+ category = category.lower()
+ player_data = {} # a dict to store players and their points.
+
+ if ctx.channel.id not in self.game_status:
+ self.game_status[ctx.channel.id] = None
+
+ if option == "start":
+ if self.game_status[ctx.channel.id] is True:
+ return await ctx.send("Game already running.")
+ else:
+ self.game_owners[ctx.channel.id] = ctx.author
+ self.game_status[ctx.channel.id] = True
+ start_embed = discord.Embed(colour=discord.Colour.red())
+ start_embed.title = "Quiz game Starting!!"
+ start_embed.description = "Each game consists of 5 questions.\n"
+ start_embed.description += "**Rules :**\n"
+ for rule in game_rules:
+ start_embed.description += f"- {rule}\n"
+ start_embed.set_footer(text="2 hints per question sent after every 10s")
+ await ctx.send(embed=start_embed) # send an embed with the rules
+
+ elif option == "stop":
+ if self.game_status[ctx.channel.id] is False:
+ return await ctx.send("No game running, nothing to stop here.")
+ else:
+ if (
+ ctx.author == self.game_owners[ctx.channel.id] or
+ Roles.moderator in [role.id for role in ctx.author.roles]
+ ):
+ await self.declare_winner(ctx.channel, player_data)
+ self.game_status[ctx.channel.id] = False
+ else:
+ await ctx.send(f"{ctx.author.mention}, you are not authorised to stop this game :ghost: !")
+ else:
+ self.game_status[ctx.channel.id] = False
+ return await ctx.send("start or stop only")
+
+ unanswerd = 0
+ done_question = []
+ hint_no = 0
+ answer = None
+ hints = None
+ while self.game_status[ctx.channel.id] is True:
+ if len(done_question) > self.question_limit and hint_no == 0:
+ await ctx.send("The round ends here.")
+ await self.declare_winner(ctx.channel, player_data)
+ break
+ if unanswerd > 3:
+ await ctx.send("Game stopped due to inactivity.")
+ await self.declare_winner(ctx.channel, player_data)
+ break
+ if category not in self.categories:
+ embed = self.category_embed
+ await ctx.send(embed=embed)
+ break
+ topic = self.questions[category]
+ if hint_no == 0:
+ while True:
+ question_dict = random.choice(topic)
+ if question_dict["id"] not in done_question:
+ done_question.append(question_dict["id"])
+ break
+ q = question_dict["question"]
+ answer = question_dict["answer"]
+ hints = question_dict["hints"]
+
+ embed = discord.Embed(colour=discord.Colour.gold())
+ embed.title = f"Question #{len(done_question)}"
+ embed.description = q
+ await ctx.send(embed=embed)
+
+ def check(m):
+ ratio = fuzz.ratio(answer.lower(), m.content)
+ return ratio > 80 and m.channel == ctx.channel
+ try:
+ msg = await self.bot.wait_for('message', check=check, timeout=10)
+ except Exception as e:
+ if self.game_status[ctx.channel.id] is False:
+ break
+ if isinstance(e, asyncio.TimeoutError):
+ if hint_no < 2:
+ await ctx.send(f"**Hint #{hint_no+1}\n**{hints[hint_no]}")
+ hint_no += 1
+ else:
+ response = random.choice(wrong_ans_responses)
+ expression = random.choice(annoyed_expressions)
+ await ctx.send(f"{response} {expression}, the correct answer is **{answer}**.")
+ hint_no = 0
+ unanswerd += 1
+ await self.send_score(ctx.channel, player_data)
+
+ else:
+ points = 100 - 25*hint_no
+ if msg.author in player_data:
+ player_data[msg.author] += points
+ else:
+ player_data[msg.author] = points
+ hint_no = 0
+ unanswerd = 0
+ await ctx.send(f"{msg.author.mention} got the correct answer :tada: {points} points for ya.")
+ await ctx.send(f"Correct answer is **{answer}**")
+ await self.send_score(ctx.channel, player_data)
+
+ @staticmethod
+ async def send_score(channel, player_data):
+ """A function which sends the score."""
+ embed = discord.Embed(colour=discord.Colour.blue())
+ embed.title = "Score Board"
+ embed.description = ""
+ for k, v in player_data.items():
+ embed.description += f"{k} : {v}\n"
+ await channel.send(embed=embed)
+
+ @staticmethod
+ async def declare_winner(channel, player_data):
+ """A function declare the winner of the quiz."""
+
+ if player_data:
+ highest_points = max(list(player_data.values()))
+ no_of_winners = list(player_data.values()).count(highest_points)
+
+ # Check if more than 1 player has highest points.
+ if no_of_winners > 1:
+ winners = []
+ points_copy = list(player_data.values()).copy()
+ for _ in range(no_of_winners):
+ index = points_copy.index(highest_points)
+ winners.append(list(player_data.keys())[index])
+ points_copy[index] = 0
+ winners_mention = None
+ for winner in winners:
+ winners_mention += f"{winner.mention} "
+
+ else:
+ author_index = list(player_data.values()).index(highest_points)
+ winner = list(player_data.keys())[author_index]
+ winners_mention = winner.mention
+ await channel.send(
+ f"Congratz {winners_mention} :tada: "
+ f"You have won this quiz game with a grand total of {highest_points} points!!"
+ )
+
+ @property
+ def category_embed(self) -> discord.Embed:
+ """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
+
+
+def setup(bot):
+ """Loading the cog."""
+ bot.add_cog(TriviaQuiz(bot))
+ logger.debug("TriviaQuiz cog loaded!")
diff --git a/bot/seasons/evergreen/trivia_quiz.py b/bot/seasons/evergreen/trivia_quiz.py
deleted file mode 100644
index 433a021d..00000000
--- a/bot/seasons/evergreen/trivia_quiz.py
+++ /dev/null
@@ -1,266 +0,0 @@
-import asyncio
-import json
-import logging
-import random
-from dataclasses import dataclass
-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.running_games = {} # channel as key and value as instance 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 = json.load(json_data)
- return questions
-
- @commands.group(name="tquiz", invoke_without_command=True)
- 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 self.running_games:
- return await ctx.send("Game already running in this channel!")
-
- if category is None:
- category = random.choice(self.categories)
-
- else:
- category = category.lower()
- if category not in self.categories:
- embed = self.category_embed()
- return await ctx.send(f"Category {category} does not exist!", embed=embed)
-
- self.running_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.running_games[channel.id]
- if game.unanswered_questions == self.inactivity_limit:
- del self.running_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
-
- # 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 self.running_games:
- return
- if message.author.bot:
- return
-
- game = self.running_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.send_score(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.running_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)
-
- 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.running_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.running_games[channel.id].unanswered_questions += 1
- await self.send_score(channel)
- await self.send_question(channel)
-
- @tquiz.command(name="score")
- async def show_score(self, ctx):
- """Show scoreboard of the game running in this channel."""
- await self.send_score(ctx.channel)
-
- async def send_score(self, channel):
- """Show score of each player in the quiz."""
- if channel.id not in self.running_games:
- return await channel.send("There are no games running in this channel!")
- game = self.running_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 self.running_games:
- return await ctx.send("No game running, nothing to stop here -.-")
- game = self.running_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.send_score(ctx.channel)
- if game.players:
- highest_points = max(game.points)
-
- # Check if more than 1 player has highest points.
-
- if game.points.count(highest_points) > 1:
- pass
- else:
- 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.running_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!")