aboutsummaryrefslogtreecommitdiffstats
path: root/bot/exts/events/trivianight/_game.py
diff options
context:
space:
mode:
Diffstat (limited to 'bot/exts/events/trivianight/_game.py')
-rw-r--r--bot/exts/events/trivianight/_game.py60
1 files changed, 57 insertions, 3 deletions
diff --git a/bot/exts/events/trivianight/_game.py b/bot/exts/events/trivianight/_game.py
index 086d0de6..aac745a7 100644
--- a/bot/exts/events/trivianight/_game.py
+++ b/bot/exts/events/trivianight/_game.py
@@ -1,4 +1,5 @@
import time
+from random import randrange
from string import ascii_uppercase
from typing import Iterable, Optional, TypedDict
@@ -25,6 +26,14 @@ UserGuess = tuple[
]
+class QuestionClosed(RuntimeError):
+ """Exception raised when the question is not open for guesses anymore."""
+
+
+class AlreadyUpdated(RuntimeError):
+ """Exception raised when the user has already updated their guess once."""
+
+
class Question:
"""Interface for one question in a trivia night game."""
@@ -76,10 +85,10 @@ class Question:
def _update_guess(self, user: int, answer: str) -> UserGuess:
"""Update an already existing guess."""
if self._started is None:
- raise RuntimeError("Question is not open for answers.")
+ raise QuestionClosed("Question is not open for answers.")
if self._guesses[user][1] is False:
- raise RuntimeError(f"User({user}) has already updated their guess once.")
+ raise AlreadyUpdated(f"User({user}) has already updated their guess once.")
self._guesses[user] = (answer, False, time.perf_counter() - self._started)
return self._guesses[user]
@@ -90,7 +99,7 @@ class Question:
return self._update_guess(user, answer)
if self._started is None:
- raise RuntimeError("Question is not open for answers.")
+ raise QuestionClosed("Question is not open for answers.")
self._guesses[user] = (answer, True, time.perf_counter() - self._started)
return self._guesses[user]
@@ -103,3 +112,48 @@ class Question:
self._guesses = {}
return guesses
+
+
+class TriviaNightGame:
+ """Interface for managing a game of trivia night."""
+
+ def __init__(self, data: list[QuestionData]) -> None:
+ self._questions = [Question(q) for q in data]
+ self.current_question: Optional[Question] = None
+
+ def __iter__(self) -> Iterable[Question]:
+ return iter(self._questions)
+
+ def next_question(self, number: str = None) -> Question:
+ """
+ Consume one random question from the trivia night game.
+
+ One question is randomly picked from the list of questions which is then removed and returned.
+ """
+ if self.current_question is not None:
+ raise RuntimeError("Cannot call next_question() when there is a current question.")
+
+ if number is not None:
+ try:
+ question = [q for q in self._questions if q.number == number][0]
+ except IndexError:
+ raise ValueError(f"Question number {number} does not exist.")
+ else:
+ question = self._questions.pop(randrange(len(self._questions)))
+
+ self.current_question = question
+ self.current_question.start()
+ return question
+
+ def end_question(self) -> None:
+ """
+ End the current question.
+
+ This method should be called when the question has been answered, it must be called before
+ attempting to call `next_question()` again.
+ """
+ if self.current_question is None:
+ raise RuntimeError("Cannot call end_question() when there is no current question.")
+
+ self.current_question.stop()
+ self.current_question = None