diff --git a/.gitignore b/.gitignore index 5290d13..b694934 100644 --- a/.gitignore +++ b/.gitignore @@ -1,216 +1 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[codz] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py.cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock -#poetry.toml - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. -# https://pdm-project.org/en/latest/usage/project/#working-with-version-control -#pdm.lock -#pdm.toml -.pdm-python -.pdm-build/ - -# pixi -# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. -#pixi.lock -# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one -# in the .venv directory. It is recommended not to include this directory in version control. -.pixi - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# Redis -*.rdb -*.aof -*.pid - -# RabbitMQ -mnesia/ -rabbitmq/ -rabbitmq-data/ - -# ActiveMQ -activemq-data/ - -# SageMath parsed files -*.sage.py - -# Environments -.env -.envrc -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Abstra -# Abstra is an AI-powered process automation framework. -# Ignore directories containing user credentials, local state, and settings. -# Learn more at https://abstra.io/docs -.abstra/ - -# Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore -# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, -# you could uncomment the following to ignore the entire vscode folder -# .vscode/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc - -# Marimo -marimo/_static/ -marimo/_lsp/ -__marimo__/ - -# Streamlit -.streamlit/secrets.toml +.venv \ No newline at end of file diff --git a/players.txt b/Players.txt similarity index 100% rename from players.txt rename to Players.txt diff --git a/roles.txt b/Roles.txt similarity index 100% rename from roles.txt rename to Roles.txt diff --git a/logger.py b/logger.py deleted file mode 100644 index 3d65862..0000000 --- a/logger.py +++ /dev/null @@ -1,31 +0,0 @@ -import logging - -log = logging.getLogger(__name__) -log.setLevel(logging.DEBUG) -formatter = logging.Formatter( - "%(asctime)s \033[1m%(levelname)s\033[0m\033[0m %(message)s" -) - -# stdout -stream_handler = logging.StreamHandler() -stream_handler.setLevel(logging.DEBUG) -stream_handler.setFormatter(formatter) -log.addHandler(stream_handler) - - -# Add custom log level names with colors -logging.addLevelName( - logging.DEBUG, "\033[1;30m%s\033[1;0m" % logging.getLevelName(logging.DEBUG) -) # gray -logging.addLevelName( - logging.INFO, "\033[1;34m%s\033[1;0m" % logging.getLevelName(logging.INFO) -) # blue -logging.addLevelName( - logging.WARNING, "\033[1;33m%s\033[1;0m" % logging.getLevelName(logging.WARNING) -) # yellow -logging.addLevelName( - logging.ERROR, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.ERROR) -) # red -logging.addLevelName( - logging.CRITICAL, "\033[1;35m%s\033[1;0m" % logging.getLevelName(logging.CRITICAL) -) # \ No newline at end of file diff --git a/main.py b/main.py index 9ab617f..aee98f3 100644 --- a/main.py +++ b/main.py @@ -1,275 +1,97 @@ + import random -from typing import Dict, List, Optional -from dataclasses import dataclass -import sys -from logger import log -from fuzzywuzzy import process +import time -@dataclass -class Player: - name: str - role: str - alive: bool = True - protected: bool = False +playerFile = open("Players.txt") +rolesFile = open ("Roles.txt") + +PlayersName = [] +RolesList = [] +KilledDuringNight = [] +PlayersInLove = [] +players_dict = {} -class Game: - def __init__(self): - # single source of truth: name -> Player - self.players: Dict[str, Player] = {} + +def GiveRoleToPlayers(): + if PlayersName and RolesList: + for player in PlayersName: + ChoseRole = random.choice(RolesList) + players_dict[player] = {'name': player, 'role': ChoseRole, 'alive': True, 'InLove': False, 'Protected': False} + RolesList.remove(ChoseRole) + else: + print("Players are not initialized") + + +def CreateLists(): + for line in playerFile: + PlayersName.append(line.strip()) + for line in rolesFile: + RolesList.append(line.strip()) + playerFile.close() + rolesFile.close() + + +def Kill(player): + if players_dict[player]['InLove'] == False: + players_dict[player]['alive'] = False + KilledDuringNight.append(player) + elif players_dict[player]['InLove'] == True: + print("in love check") + for player in players_dict: + if players_dict[player]['InLove'] == True: + print("found one") + players_dict[player]['alive'] = False + KilledDuringNight.append(player) + elif players_dict[player]['Protected'] == True: + return + - # lovers - self.lovers: Optional[Tuple[str, str]] = None + +def Revive(player): + players_dict[player]["alive"] = True + KilledDuringNight.remove(player) +def PutInLove(player1, player2): + players_dict[player1]['InLove'] = True + players_dict[player2]['InLove'] = True + time.sleep(1) + print("The people selected wake up and show their role") + time.sleep(1) + print("They go back to sleep") - # ------------------------- - # setup / I/O - # ------------------------- - def load_lists(self) -> Optional[Dict[str, List[str]]]: - """ - Load players and roles from files and return them as lists. - Returns dict with keys 'players' and 'roles'. - """ - players_file = "players.txt" - roles_file = "roles.txt" +def Cupidon(): + print("You choose two people to be linked to the death") + player1 = SelectSomeone() + player2 = SelectSomeone() + PutInLove(player1, player2) - log.info("Loading lists...") - - try: - with open(players_file, "r", encoding="utf-8") as pf: - player_names = [line.strip() for line in pf if line.strip()] - except FileNotFoundError: - log.critical("Players file not found: %s", players_file) - return None - - try: - with open(roles_file, "r", encoding="utf-8") as rf: - roles_list = [line.strip() for line in rf if line.strip()] - except FileNotFoundError: - log.critical("Roles file not found: %s", roles_file) - return None - - log.info("Load complete!") - - return {"players": player_names, "roles": roles_list} - - # ------------------------- - # player / role assignment - # ------------------------- - def give_roles_to_players(self, player_names: Optional[List[str]] = None, - roles_list: Optional[List[str]] = None) -> None: - """ - Assign a random role to each player. Accepts optional lists (returned by load_lists). - If lists are not supplied, it will attempt to read files itself. - """ - - if not player_names: - log.info("Players are not initialized") - return - if not roles_list: - log.info("Roles are not initialized") - return - if len(roles_list) < len(player_names): - log.error("Not enough roles for players (roles: %d, players: %d)", - len(roles_list), len(player_names)) - return - - available_roles = list(roles_list) - random.shuffle(available_roles) - - # clear any existing players (safe to reassign) - self.players.clear() - - for name in player_names: - chosen_role = available_roles.pop() - self.players[name] = Player(name=name, role=chosen_role) - - log.debug("Assigned roles:") - for player in self.players.values(): - log.debug("%s: %s", player.name, player.role) - - # ------------------------- - # utilities - # ------------------------- - def select_someone(self, prompt: Optional[str] = None) -> Optional[str]: - """ - Prompt the user to enter a player name until a valid one is entered. - Returns None on EOF/KeyboardInterrupt. - """ - prompt = prompt or "Enter the name of the player: " - while True: - selected = input(prompt).strip() - - # exact match - if selected in self.players: - return selected - - # fuzzy matching - match = process.extract(selected, self.players, limit=1) - log.debug(match) - if match: - fuzz_player, fuzz_score = match[0][0], match[0][1] # player name, score - log.debug(fuzz_player) - log.debug(fuzz_score) - if fuzz_score >= 60: - log.info("You meant %s!", fuzz_player.name) - log.debug("Fuzz score: %s" % fuzz_score) - return fuzz_player - - # ------------------------- - # game actions - # ------------------------- - def put_in_love(self, player1: str, player2: str) -> None: - """Link two players such that if one dies the other dies too.""" - if player1 not in self.players or player2 not in self.players: - log.error("One or both players do not exist: %s, %s", player1, player2) - return - - self.lovers = (player1, player2) - - log.debug("Players now put in love: %s <-> %s", player1, player2) - - log.info("They are now bounded by love.") - log.info("They wake up and reveal their role to each other.") - log.info("Those two go back to sleep.") +def Savior(): + print("You choose someone to protect") + ProtectedPlayer = SelectSomeone() + players_dict[ProtectedPlayer]['Protected'] = False - def kill(self, player: str) -> None: - """Kill a player.""" - if player not in self.players: - log.error("Couldn't kill unknown player: %s", player) - return - target = self.players[player] - - # already dead? - if not target.alive: - log.error("%s is already dead!", player) - return - - # protected? - if target.protected: - log.debug("%s was protected and survives.", player) - return - - # in love? - if target in self.lovers: - log.debug("%s is in love! Killing them and their lover.", target) - for p in self.lovers: - # kill them and their lover - log.debug("Killed %s", p) - p.alive = False - return - - # else just kill them - log.debug("Killed %s" % p) - target.alive = False - - def revive(self, player: str) -> None: - """Revive a player.""" - if player not in self.players: - log.error("Tried to revive unknown player: %s", player) - return - p = self.players[player] - if not p.alive: - p.alive = True - log.info("%s has been revived.", player) +def SelectSomeone(): + while True: + SelectedPlayer = input("Enter the name of the player:") + if SelectedPlayer in PlayersName: + return SelectedPlayer else: - log.info("%s is already alive.", player) + print("This player don't exist") - # ------------------------- - # helpers - # ------------------------- - def status(self) -> None: - """Log current players' statuses for debugging.""" - - # player values - for p in self.players.values(): - log.debug("%s -> role: %s, alive: %s, Protected: %s", - p.name, p.role, p.alive, p.protected) - # lovers - log.debug("Lovers: %s" % ",".join(self.lovers)) - - def role(func): - """Decorator for roles""" - def wrapper(*args, **kwargs): - # the role is the name of the function thats decorated - role = func.__name__.capitalize() - log.info("%s wakes up." % role) - func(*args, **kwargs) - log.info("%s goes back to sleep." % role) - - # workaround to call status from the instance - # is it ok? no idea but i dont have self anyway - instance = args[0] - instance.status() - - return wrapper - - # ------------------------- - # roles - # ------------------------- - - @role - def cupidon(self) -> None: - """Interactively pick two players to link by love.""" - log.info("Choose two people to be linked to death.") - log.info("Give me the first person that shall be bounded!") - p1 = self.select_someone() - if p1 is None: - return - log.info("What about the second one?") - p2 = self.select_someone() - if p2 is None: - return - if p1 == p2: - log.info("Cannot link a player to themselves.") - return - self.put_in_love(p1, p2) - - @role - def savior(self) -> None: - """Interactively choose someone to protect.""" - log.info("Choose someone to protect.") - protected = self.select_someone() - if protected is None: - return - - self.players[protected].protected = True - log.debug("Protected: %s", protected) - - - # ------------------------- - # game flow - # ------------------------- - def first_day_cycle(self) -> None: - log.info("All the villagers fall asleep.") - self.cupidon() - self.savior() +def FirstDayCycle(): + print("The villagers fall asleep") + time.sleep(1) + print("Cupidon get up") + Cupidon() + print("Cupidon go back to sleep") + time.sleep(1) + print("The savior get up") + Savior() -if __name__ == "__main__": - print("---\nWerewolfGame\n---") - - # instantiate the game - game = Game() - - # I/O: read files and assign roles - loaded = game.load_lists() - try: - if loaded: - # start the game! - game.give_roles_to_players(loaded["players"], loaded["roles"]) - game.first_day_cycle() - - # CTRL+C - except KeyboardInterrupt: - print() # new line - log.info("Bye bye!") - sys.exit(0) - - # Any unhandled exception - except Exception as e: - log.exception("Unhandled exception: %s" % e) - sys.exit(1) - +CreateLists() +GiveRoleToPlayers() +FirstDayCycle() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 516af7e..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -fuzzywuzzy==0.18.0 \ No newline at end of file