diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/client.py b/client.py index cd19349..aa1d945 100644 --- a/client.py +++ b/client.py @@ -3,10 +3,14 @@ # Apr 2025 from __future__ import annotations +from typing import TYPE_CHECKING import socket import select import network_utilities +if TYPE_CHECKING: + from ui import UI + class Game: @@ -41,12 +45,16 @@ class Game: packet_id = network_utilities.unpack_varint(self.__server) if packet_id == 1: name = network_utilities.unpack_string(self.__server) - self.__player.player_joined(name) + admin = bool(network_utilities.unpack_varint(self.__server)) + self.__player.player_joined(name, admin) elif packet_id == 2: text = network_utilities.unpack_string(self.__server) self.__player.new_verse(text) elif packet_id == 3: self.__player.guess_incorrect() + elif packet_id == 7: + url = network_utilities.unpack_string(self.__server) + self.__player.guess_partial_correct(url) elif packet_id == 4: self.__player.guess_correct() elif packet_id == 5: @@ -66,12 +74,16 @@ class Player: __verse: str __score: int __game: Game | None + __ui: UI + __admin: bool - def __init__(self, name: str): + def __init__(self, name: str, ui: UI): self.__name = name self.__verse = "" self.__score = 0 self.__game = None + self.__ui = ui + self.__admin = False @property def name(self) -> str: return self.__name @@ -82,6 +94,9 @@ class Player: @property def score(self) -> int: return self.__score + @property + def admin(self) -> bool: return self.__admin + def join_game(self, host: str = 'localhost', port: int = 7788): conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((host, port)) @@ -106,29 +121,36 @@ class Player: if self.__game is not None: self.__game.end_game() - def player_joined(self, name: str): + def player_joined(self, name: str, admin: bool): if self.__game is not None: - pass + if name == self.name: self.__admin = admin or self.__admin + self.__ui.player_joined(name, admin) def new_verse(self, text: str): if self.__game is not None: - pass + self.__verse = text + self.__ui.new_verse(text) def guess_incorrect(self): if self.__game is not None: - pass + self.__ui.guess_incorrect() + + def guess_partial_correct(self, url): + if self.__game is not None: + self.__ui.guess_partial_correct(url) def guess_correct(self): if self.__game is not None: - pass + self.__ui.guess_correct() def verse_guessed(self, points: int, url: str, player: str): if self.__game is not None: - pass + self.__score += points + self.__ui.verse_guessed(points, url, player) def game_over(self, players: list[str], scores: list[int]): if self.__game is not None: - pass + self.__ui.game_over(players, scores) def update(self): if self.__game is not None: diff --git a/library.py b/library.py index 38daec0..ded1329 100644 --- a/library.py +++ b/library.py @@ -9,6 +9,7 @@ import socket import select import network_utilities from server import Game +from time import sleep class Library: @@ -18,8 +19,9 @@ class Library: __host: str __port: int __socket: socket.socket + __bible_only: bool - def __init__(self, host: str = '', port: int = 7788): + def __init__(self, host: str = '', port: int = 7788, *, bible_only: bool = False): with open("data/scripture-frequencies.json", encoding='utf-8') as file: self.__verses = json.load(file) self.__games = [] @@ -28,8 +30,11 @@ class Library: self.__port = port self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.__bible_only = bible_only + def serve_forever(self): try: + print(f"Starting server at {self.__host}:{self.__port}") with self.__socket as s: s.bind((self.__host, self.__port)) s.listen(1) @@ -40,11 +45,13 @@ class Library: if not game.finished: game.update() else: self.__games.remove(game) if ready_to_read: - conn, _ = s.accept() + conn, addr = s.accept() conn.setblocking(False) if network_utilities.unpack_varint(conn) == 1: name = network_utilities.unpack_string(conn) + print(f"<< (1) join_game({name}, {addr})") self.join_game(name, conn) + sleep(0.1) except KeyboardInterrupt: print("KeyboardInterrupt\nExiting...") return @@ -61,6 +68,7 @@ class Library: def get_verse(self, difficulty: int, game: Game): url = self.__select_verse(difficulty) + print(f"Verse Selected: {url}") text = self.__get_verse_text(url) game.new_verse(url, text) @@ -103,9 +111,17 @@ class Library: difficulty_verses = [] for key, value in self.__verses.items(): + if self.__bible_only and not (key.startswith('/ot') or key.startswith('/nt')): + continue for i, diff in enumerate(value): if real_difficulty_lower <= diff <= real_difficulty_upper: difficulty_verses.append(f"{key}/{i+1}") + if not difficulty_verses: difficulty_verses.append('/pgp/js-h/1/17') + return difficulty_verses + +if __name__ == '__main__': + lib = Library(bible_only=False) + lib.serve_forever() diff --git a/main.py b/main.py new file mode 100644 index 0000000..c28dba5 --- /dev/null +++ b/main.py @@ -0,0 +1,60 @@ + +import random + +def name_gen(): + adjectives = [ + "able", "active", "adaptable", "adventurous", "agreeable", "alert", + "amazing", "amiable", "ample", "artistic", "attractive", "balanced", + "beautiful", "blissful", "bold", "brave", "bright", "brilliant", + "bubbly", "calm", "capable", "careful", "charming", "cheerful", "clean", + "clear", "clever", "colorful", "comfortable", "compassionate", + "confident", "considerate", "cool", "cooperative", "courageous", + "creative", "cultured", "cute", "daring", "decent", "delightful", + "detailed", "determined", "dignified", "disciplined", "dynamic", + "eager", "easygoing", "elegant", "energetic", "engaging", + "enthusiastic", "excellent", "exciting", "expressive", "fair", + "faithful", "fancy", "fascinating", "flexible", "focused", "friendly", + "fun", "funny", "generous", "gentle", "genuine", "gifted", "glad", + "gleaming", "good", "graceful", "gracious", "great", "handsome", + "happy", "harmonious", "helpful", "honest", "hopeful", "humble", + "imaginative", "impressive", "independent", "innocent", "inspiring", + "intelligent", "interesting", "intuitive", "jolly", "jovial", "joyful", + "kind", "lively", "logical", "lovely", "loyal", "lucky", "mature", + "mindful", "modest", + ] + animals = [ + "aardvark", "albatross", "alligator", "alpaca", "ant", "anteater", + "antelope", "ape", "armadillo", "baboon", "badger", "barracuda", "bat", + "bear", "beaver", "bee", "beetle", "bison", "boar", + "bobcat", "buffalo", "butterfly", "camel", "canary", "capybara", + "caracal", "caribou", "cassowary", "cat", "caterpillar", "cattle", + "chameleon", "cheetah", "chicken", "chimpanzee", "chinchilla", "cobra", + "cockatoo", "cougar", "cow", "coyote", "crab", "crane", "crocodile", + "crow", "deer", "dingo", "dog", "dolphin", "donkey", "dove", + "dragonfly", "duck", "eagle", "echidna", "eel", "elephant", "elk", + "emu", "falcon", "ferret", "finch", "firefly", "fish", "flamingo", + "fly", "fox", "frog", "gazelle", "gecko", "giraffe", "goat", "goldfish", + "goose", "gorilla", "grasshopper", "pig", "gull", "hamster", + "hare", "hawk", "hedgehog", "hippopotamus", "horse", + "hummingbird", "hyena", "iguana", "jackal", "jaguar", + "jellyfish", "kangaroo", "kingfisher", "koala", "lemur", + "leopard", "lion", "lizard", "llama", + ] + + return random.choice(adjectives).capitalize() + random.choice(animals).capitalize() + +def server(host: str='', port: int=7788): + from library import Library + lib = Library(host, port) + lib.serve_forever() + +def client(playername: str, host: str='localhost', port: int=7788): + from ui import UI + ui = UI(playername, host, port) + ui.loop() + +def main(): + client(name_gen()) + +if __name__ == "__main__": + main() diff --git a/network_utilities.py b/network_utilities.py index ff95aae..6fd02e1 100644 --- a/network_utilities.py +++ b/network_utilities.py @@ -32,8 +32,9 @@ def unpack_varint(conn: socket) -> int: return data def pack_string(text: str) -> bytes: - data = pack_varint(len(text)) - data += text.encode('utf-8') + utf = text.encode('utf-8') + data = pack_varint(len(utf)) + data += utf return data def unpack_string(conn: socket) -> str: diff --git a/reference.py b/reference.py new file mode 100644 index 0000000..7c4d357 --- /dev/null +++ b/reference.py @@ -0,0 +1,160 @@ +# Kyler Olsen +# Mar 2024 +import re + +__LINKS = { + '/ot/gen': ("Gen.", "Genesis", ), + '/ot/ex': ("Ex.", "Exodus", ), + '/ot/lev': ("Lev.", "Leviticus", ), + '/ot/num': ("Num.", "Numbers", ), + '/ot/deut': ("Deut.", "Deuteronomy", ), + '/ot/josh': ("Josh.", "Joshua", ), + '/ot/judg': ("Judg.", "Judges", ), + '/ot/ruth': ("Ruth", ), + '/ot/1-sam': ("1 Sam.", "1 Samuel", ), + '/ot/2-sam': ("2 Sam.", "2 Samuel", ), + '/ot/1-kgs': ("1 Kgs.", "1 Kings", ), + '/ot/2-kgs': ("2 Kgs.", "2 Kings", ), + '/ot/1-chr': ("1 Chr.", "1 Chronicles", ), + '/ot/2-chr': ("2 Chr.", "2 Chronicles", ), + '/ot/ezra': ("Ezra", ), + '/ot/neh': ("Neh.", "Nehemiah", ), + '/ot/esth': ("Esth.", "Esther", ), + '/ot/job': ("Job", ), + '/ot/ps': ("Ps.", "Psalm", "Psalms", ), + '/ot/prov': ("Prov.", "Proverbs", ), + '/ot/eccl': ("Eccl.", "Ecclesiastes", ), + '/ot/song': ("Song", "Song of Solomon", ), + '/ot/isa': ("Isa.", "Isaiah", ), + '/ot/jer': ("Jer.", "Jeremiah", ), + '/ot/lam': ("Lam.", "Lamentations", ), + '/ot/ezek': ("Ezek.", "Ezekiel", ), + '/ot/dan': ("Dan.", "Daniel", ), + '/ot/hosea': ("Hosea", ), + '/ot/joel': ("Joel", ), + '/ot/amos': ("Amos", ), + '/ot/obad': ("Obad.", "Obadiah", ), + '/ot/jonah': ("Jonah", ), + '/ot/micah': ("Micah", ), + '/ot/nahum': ("Nahum", ), + '/ot/hab': ("Hab.", "Habakkuk", ), + '/ot/zeph': ("Zeph.", "Zephaniah", ), + '/ot/hag': ("Hag.", "Haggai", ), + '/ot/zech': ("Zech.", "Zechariah", ), + '/ot/mal': ("Mal.", "Malachi", ), + '/nt/matt': ("Matt.", "Matthew", ), + '/nt/mark': ("Mark", ), + '/nt/luke': ("Luke", ), + '/nt/john': ("John", ), + '/nt/acts': ("Acts", ), + '/nt/rom': ("Rom.", "Romans", ), + '/nt/1-cor': ("1 Cor.", "1 Corinthians", ), + '/nt/2-cor': ("2 Cor.", "2 Corinthians", ), + '/nt/gal': ("Gal.", "Galatians", ), + '/nt/eph': ("Eph.", "Ephesians", ), + '/nt/philip': ("Philip.", "Philippians", ), + '/nt/col': ("Col.", "Colossians", ), + '/nt/1-thes': ("1 Thes.", "1 Thessalonians", ), + '/nt/2-thes': ("2 Thes.", "2 Thessalonians", ), + '/nt/1-tim': ("1 Tim.", "1 Timothy", ), + '/nt/2-tim': ("2 Tim.", "2 Timothy", ), + '/nt/titus': ("Titus", ), + '/nt/philem': ("Philem.", "Philemon", ), + '/nt/heb': ("Heb.", "Hebrews", ), + '/nt/james': ("James", ), + '/nt/1-pet': ("1 Pet.", "1 Peter", ), + '/nt/2-pet': ("2 Pet.", "2 Peter", ), + '/nt/1-jn': ("1 Jn.", "1 John", ), + '/nt/2-jn': ("2 Jn.", "2 John", ), + '/nt/3-jn': ("3 Jn.", "3 John", ), + '/nt/jude': ("Jude", ), + '/nt/rev': ("Rev.", "Revelation", ), + '/bofm/1-ne': ("1 Ne.", "1 Nephi", ), + '/bofm/2-ne': ("2 Ne.", "2 Nephi", ), + '/bofm/jacob': ("Jacob", ), + '/bofm/enos': ("Enos", ), + '/bofm/jarom': ("Jarom", ), + '/bofm/omni': ("Omni", ), + '/bofm/w-of-m': ("WofM", "W of M", "Words of Mormon", ), + '/bofm/mosiah': ("Mosiah", ), + '/bofm/alma': ("Alma", ), + '/bofm/hel': ("Hel.", "Helaman", ), + '/bofm/3-ne': ("3 Ne.", "3 Nephi", ), + '/bofm/4-ne': ("4 Ne.", "4 Nephi", ), + '/bofm/morm': ("Morm.", "Mormon", ), + '/bofm/ether': ("Ether", ), + '/bofm/moro': ("Moro.", "Moroni", ), + '/dc-testament/dc': ("D&C", "D & C", "DandC", "D and C", "D+C", "D + C", "Doctrine and Covenants", ), + '/dc-testament/od': ("OD", "Official Declaration", ), + '/pgp/moses': ("Moses", ), + '/pgp/abr': ("Abr.", "Abraham", ), + '/pgp/js-m': ("JS—M", "Joseph Smith—Matthew", "JS-M", "JSM", "Joseph Smith-Matthew", ), + '/pgp/js-h': ("JS—H", "Joseph Smith—History", "JS-H", "JSH", "Joseph Smith-History", ), + '/pgp/a-of-f': ("A of F", "AofF", "Articles of Faith", ), + '/ot': ("OT", "Old Testament", "The Old Testament", ), + '/nt': ("NT", "New Testament", "The New Testament", ), + '/bofm': ("BofM", "Book of Mormon", "The Book of Mormon", ), + '/pgp': ("PGP", "PofGP", "Pearl of Great Price", "The Pearl of Great Price", ), +} + +def __find_book(book: str) -> tuple[str, list[str]]: + matches: dict[str, str] = {} + for key, values in __LINKS.items(): + if book.lower().replace('.', '') in [i.lower().replace('.', '') for i in values]: + return (key, []) + else: + for value in values: + if value.lower().startswith(book.lower().replace('.', '')): + matches[key] = values[-1] + if len(matches) == 1: + return (list(matches.keys())[0], []) + else: + return ("", list(matches.values())) + +def convert_reference(ref: str) -> tuple[str, list[str]]: + pattern = re.findall(r'^.*\w\.?\s\d', ref) + if pattern: + i = len(pattern[-1])-1 + book, cv = ref[:i-1], \ + ref[i:].replace(' ', '').replace('–', '-').replace(':', '/') + link, possible = __find_book(book) + if link: + return (f"{link}/{cv}", []) + else: + return ("", possible) + else: + return __find_book(ref) + +def convert_url(url: str) -> str: + parts = url.strip('/').split('/') + + if not parts: + raise ValueError("Empty URL") + + book_key = f"/{'/'.join(parts[:2]) if len(parts) >= 2 else parts[0]}" + remainder = parts[2:] if len(parts) > 2 else [] + + if book_key not in __LINKS: + raise ValueError(f"Book key not found: {book_key}") + + book_name = __LINKS[book_key][-1] + + if not remainder: + return book_name + elif len(remainder) == 1: + return f"{book_name} {remainder[0]}" + elif len(remainder) == 2: + return f"{book_name} {remainder[0]}:{remainder[1]}" + else: + raise ValueError(f"Unexpected format in URL: {url}") + +if __name__ == '__main__': + # import json + # with open('refs.json', 'w', encoding='utf-8') as file: + # json.dump(__LINKS, file, indent=4) + with open('refs.txt', encoding='utf-8') as file: + # for i in file.readlines(): + for i in file.readlines()[::50]: + ref = convert_reference(i[:-1])[0] + if ref: + print(convert_url(ref)) diff --git a/server.py b/server.py index 1223251..c67ba8f 100644 --- a/server.py +++ b/server.py @@ -3,7 +3,7 @@ # Apr 2025 from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, ClassVar import select import datetime import network_utilities @@ -15,24 +15,30 @@ if TYPE_CHECKING: class Game: + FINAL_SCORE_LIST: ClassVar[int] = 50 + __library: Library __current_url: str + __current_url_parts: list[str] __clients: list[Player] __round_points: list[int] __total_scores: list[int] __active: bool __finished: bool __created: datetime.datetime + __difficulty: int def __init__(self, library: Library): self.__library = library self.__current_url = "" + self.__current_url_parts = [] self.__clients = [] self.__round_points = [] self.__total_scores = [] self.__active = False self.__finished = False self.__created = datetime.datetime.now() + self.__difficulty = 0 @property def active(self) -> bool: return self.__active @@ -42,40 +48,75 @@ class Game: def add_player(self, name: str, conn: socket): if not self.__active: - self.__clients.append(Player(name, self, conn)) - self.__total_scores.append(0) - for player in self.__clients: - player.player_joined(name) + new_player = Player(name, self, conn) + if self.__clients: + for player in self.__clients: + new_player.player_joined(player.name, False) + self.__clients.append(new_player) + self.__total_scores.append(0) + for player in self.__clients: + player.player_joined(name, False) + else: + new_player.player_joined(new_player.name, True) + self.__clients.append(new_player) + self.__total_scores.append(0) def start_game(self): self.__active = True def start_round(self, difficulty: int): if self.__active and not self.__finished: + self.__difficulty = difficulty self.__round_points = [0] * len(self.__clients) self.__library.get_verse(difficulty, self) def new_verse(self, url: str, text: str): for player in self.__clients: self.__current_url = url + self.__current_url_parts = url.strip('/').split('/') player.new_verse(text) def guess_reference(self, url: str, player: Player): if self.__active and not self.__finished: if url == self.__current_url: player.guess_correct() + self.__round_points[self.__clients.index(player)] = 4 + self.__difficulty for i, points in enumerate(self.__round_points): self.__total_scores[i] += points self.__clients[i].verse_guessed( points, self.__current_url, player.name) else: - player.guess_incorrect() + partially_correct = [] + for player_url, current_url in zip(url.strip('/').split('/'), self.__current_url_parts): + if player_url == current_url: + partially_correct.append(current_url) + else: + break + if partially_correct: + player.guess_partial_correct(f"/{'/'.join(partially_correct)}") + self.__round_points[self.__clients.index(player)] = len(partially_correct) + else: player.guess_incorrect() def end_game(self): self.__finished = True + items = sorted( + [(i.name, j) for i, j in zip(self.__clients, self.__total_scores)], + reverse=True, + key=lambda o: o[1] + ) + players = [i for i, _ in items] + scores = [i for _, i in items] for player in self.__clients: - player.game_over( - [i.name for i in self.__clients], self.__total_scores) + if player.name in players[:Game.FINAL_SCORE_LIST]: + player.game_over( + players[:Game.FINAL_SCORE_LIST], + scores[:Game.FINAL_SCORE_LIST] + ) + else: + player.game_over( + players[:Game.FINAL_SCORE_LIST] + [player.name], + scores[:Game.FINAL_SCORE_LIST] + [scores[players.index(player.name)]] + ) def update(self): if not self.__active and ( @@ -95,34 +136,48 @@ class Player: __name: str __game: Game __client: socket + __connected: bool def __init__(self, name: str, game: Game, conn: socket): self.__name = name self.__game = game self.__client = conn + self.__connected = True @property def name(self) -> str: return self.__name - def player_joined(self, name: str): + def player_joined(self, name: str, admin: bool): + print(f">> (1, {self.name}) player_joined({name}, {admin})") data = network_utilities.pack_varint(1) data += network_utilities.pack_string(name) + data += network_utilities.pack_varint(admin) self.__client.send(data) def new_verse(self, text: str): + print(f">> (2, {self.name}) new_verse({text})") data = network_utilities.pack_varint(2) data += network_utilities.pack_string(text) self.__client.send(data) def guess_incorrect(self): + print(f">> (3, {self.name}) guess_incorrect()") data = network_utilities.pack_varint(3) self.__client.send(data) + def guess_partial_correct(self, url): + print(f">> (7, {self.name}) guess_partial_correct({url})") + data = network_utilities.pack_varint(7) + data += network_utilities.pack_string(url) + self.__client.send(data) + def guess_correct(self): + print(f">> (4, {self.name}) guess_correct()") data = network_utilities.pack_varint(4) self.__client.send(data) def verse_guessed(self, points: int, url: str, player: str): + print(f">> (5, {self.name}) verse_guessed({points}, {url})") data = network_utilities.pack_varint(5) data += network_utilities.pack_varint(points) data += network_utilities.pack_string(url) @@ -130,24 +185,31 @@ class Player: self.__client.send(data) def game_over(self, players: list[str], scores: list[int]): + print(f">> (6, {self.name}) game_over({len(players)}, {len(scores)})") data = network_utilities.pack_varint(6) data += network_utilities.pack_string_array(players) data += network_utilities.pack_varint_array(scores) self.__client.send(data) self.__client.close() + self.__connected = False def update(self): - ready_to_read, _, _ = select.select([self.__client], [], [], 0) - if ready_to_read: - packet_id = network_utilities.unpack_varint(self.__client) - if packet_id == 2: - self.__game.start_game() - elif packet_id == 3: - difficulty = network_utilities.unpack_varint(self.__client) - self.__game.start_round(difficulty) - elif packet_id == 4: - url = network_utilities.unpack_string(self.__client) - self.__game.guess_reference(url, self) - elif packet_id == 5: - self.__game.end_game() + if self.__connected: + ready_to_read, _, _ = select.select([self.__client], [], [], 0) + if ready_to_read: + packet_id = network_utilities.unpack_varint(self.__client) + if packet_id == 2: + print(f"<< (2, {self.name}) start_game()") + self.__game.start_game() + elif packet_id == 3: + difficulty = network_utilities.unpack_varint(self.__client) + print(f"<< (3, {self.name}) start_round({difficulty})") + self.__game.start_round(difficulty) + elif packet_id == 4: + url = network_utilities.unpack_string(self.__client) + print(f"<< (4, {self.name}) guess_reference({url})") + self.__game.guess_reference(url, self) + elif packet_id == 5: + print(f"<< (5, {self.name}) end_game()") + self.__game.end_game() diff --git a/ui.py b/ui.py new file mode 100644 index 0000000..5ef4b6e --- /dev/null +++ b/ui.py @@ -0,0 +1,157 @@ + +from client import Player +from reference import convert_reference, convert_url +from time import sleep +from blessed import Terminal + + +class UI: + + __player: Player + __verse: str + __in_game: bool + __in_between_rounds: bool + __game_over: bool + __term: Terminal + __buffer: str + + def __init__(self, playername: str, host: str='localhost', port: int=7788): + self.__player = Player(playername, self) + self.__player.join_game(host, port) + self.__verse = "" + self.__in_game = False + self.__in_between_rounds = False + self.__game_over = False + self.__term = Terminal() + self.__buffer = "" + + def get_line(self): + with self.__term.cbreak(): + val = self.__term.inkey(timeout=0) + if not val: + return None + if val.is_sequence: + if val.name == 'KEY_ENTER': + line = self.__buffer + self.__buffer = "" + print() + return line + elif val.name == 'KEY_BACKSPACE': + self.__buffer = self.__buffer[:-1] + print(f'\r{self.__term.clear_eol}{self.__buffer}', end='', flush=True) + else: + self.__buffer += val + print(val, end='', flush=True) + return None + + def __reset(self): + self.__buffer = "" + print() + + def loop(self): + while not self.__game_over: + self.__player.update() + if text := self.get_line(): + if self.__in_between_rounds and self.__player.admin: + self.__next_round(text) + elif self.__in_between_rounds: + print("Waiting for the next round to start...") + elif self.__in_game: self.__guess_ref(text) + elif self.__player.admin: self.__start_game(text) + else: + print("Waiting for the game to start...") + sleep(0.1) + + def __next_round(self, text: str): + if text.isdigit() and 0 <= int(text) <= 10: + print(f"Starting round with difficulty: {text}") + self.__player.new_round(int(text)) + elif text.lower() == 'e': + self.__player.end_game() + else: + print("Invalid input!\nPlease enter a difficulty level between 1 and 10.") + + def __guess_ref(self, text: str): + try: + url, possible = convert_reference(text) + except Exception: + print( + "An Unknown Error Occurred.\n" + "Please Check Your Reference and Try Again." + ) + else: + if url: + try: ref = convert_url(url) + except Exception: ref = url.upper().replace('/','.').strip('.') + print(f"Your input was interpreted as: {ref}") + self.__player.guess_reference(url) + return + elif possible: + print( + "Sorry, that reference could not be found.\n" + "Did you mean one of these:" + ) + for i in possible: + print(i) + else: + print( + "Sorry, that reference could not be found.\n" + "Please Check Your Reference and Try Again." + ) + print(self.__verse) + + def __start_game(self, text: str = "1"): + print("Starting game...") + self.__in_between_rounds = True + self.__player.start_game() + self.__next_round(text) + + def player_joined(self, name: str, admin: bool): + if name == self.__player.name: + if admin: + print("You are the game host.") + print("Please enter the difficulty for the first round to start the game.") + print("(Difficulty Range: 1 Easy - 10 Hard)") + print(f"* {name} Joined the Game *") + else: print(f"{name} Joined the Game") + + def new_verse(self, text: str): + self.__reset() + self.__in_game = True + self.__in_between_rounds = False + self.__verse = text + print(self.__verse) + + def guess_incorrect(self): + print("That guess was incorrect.") + print(self.__verse) + + def guess_partial_correct(self, url): + try: ref = convert_url(url) + except Exception: ref = url.upper().replace('/','.').strip('.') + print(f"That guess was partially correct: {ref}") + print(self.__verse) + + def guess_correct(self): + print("That guess was correct!") + + def verse_guessed(self, points: int, url: str, player: str): + try: ref = convert_url(url) + except Exception: ref = url.upper().replace('/','.').strip('.') + print( + f"\nThe verse has been guessed by {player}.\n" + f"The reference is {ref}.\n" + f"You have been awarded {points} points for your guess." + ) + self.__in_between_rounds = True + if self.__player.admin: + print("Please enter the difficulty for the next round (1-10) or 'e' to end the game.") + + def game_over(self, players: list[str], scores: list[int]): + self.__game_over = True + print("\n--- THANKS FOR PLAYING! ---") + for player, score in zip(players, scores): + if player == self.__player.name: + print(f" * {player}: {score} *") + else: + print(f" {player}: {score}")