This commit is contained in:
2025-06-20 16:44:18 -04:00
parent 6b3f94bd68
commit 9bb383e03c
5 changed files with 0 additions and 0 deletions

1
src/__init__.py Normal file
View File

@ -0,0 +1 @@
pass

7
src/expansion.py Normal file
View File

@ -0,0 +1,7 @@
class Expansion:
def __init__(self, parent, globalConfig):
setattr(parent, self.__class__.ID, self)
class PlayerExpansion:
def __init__(self, player, localConfig):
pass

311
src/player_settings.py Normal file
View File

@ -0,0 +1,311 @@
"""
Needs to manage any number of players
Needs to work with mulitple rounds/turns
Needs to handle modifying number of players
"""
import abc
import copy
import json
import logging
from pathlib import Path
_log = logging.getLogger('NoPE-Lib')
class PlayersManager:
"""
Manager of players for a given game.
Since this class implements most methods available to classic dict,
you can think of this class as a python dict.
Attributes:
gameID (str): The gameID of the active game
Methods:
save:
"""
defaultPlayerConfig = {"flags": [], "expansions": {}, "games": {}}
def __init__(self, gameID: str=None, activePlayers: list[str]=None, playersPath: str='./players.json', loggerID: str='PlayersManager', includedExpansions: tuple[str]=None):
"""
Initialises a list of players.
Args:
gameID (str): The ID of the game used in the configuration file.
activePlayers (list of str): The names of the players that are playing. Defaults to none which includes all players.
playersPath (str, optional): The path of the players.json file. Defaults to './players.json'.
loggerID (str, optional): The ID used for logging. Defaults to 'Players'.
"""
# Store the arguments
self._log = _log.getChild(loggerID)
self._currentGameID = gameID
# Deal with the path
self._playersPath = Path(playersPath)
# Get the config file
with open(self._playersPath, 'r') as f:
self._cfg = json.load(f)
# Create the players
activePlayers = activePlayers if activePlayers is not None else self._cfg["players"].keys()
self._player_data = {
name: Player(name, self, **player_cfg)
for name, player_cfg in self._cfg["players"].items()
if name in activePlayers
}
def __getitem__(self, playerName: str):
"""
Get a player object.
If the player wasn't active, make them active.
If the player didn't exist previously, create a default config and make them active.
"""
if playerName not in self._cfg["players"]:
# Create the brand new player
self._cfg["players"][playerName] = copy.deepcopy(self.defaultPlayerConfig)
newPlayer = Player(playerName, self, **self._cfg["players"][playerName])
# Make the player active
self._player_data[playerName] = newPlayer
self._log.debug(f"Created a new player called {playerName}")
return newPlayer
elif playerName not in self._player_data:
# Fetch the player's data and make them active
playerAdded = Player(playerName, self, **self._cfg["players"][playerName])
self._player_data[playerName] = playerAdded
self._log.debug(f"Fetched {playerName}'s data and made them active")
return playerAdded
else:
return self._player_data[playerName]
def __setitem__(self, playerName: str, config: dict, makeNewPlayerObject=False):
"""
Replace a player's config with another one. It possible to also automatically replace the
player object by specifying the corresponding argument.
"""
self._log.debug(f"Changed {playerName}'s config")
if makeNewPlayerObject:
previousInstance = self._player_data[playerName]
self._player_data[playerName] = Player(playerName, self, **config)
del previousInstance
self._cfg["players"][playerName] = config
def __delitem__(self, playerName):
self._log.debug(f"Removing {playerName} from active players")
del self._player_data[playerName]
def __len__(self):
return len(self._player_data)
def __iter__(self):
return self._player_data.__iter__()
def __repr__(self):
return f"Manager is playing {self.gameID} with {len(self._player_data)} active players"
@property
def gameID(self):
return self._currentGameID
@gameID.setter
def gameID(self, gameID: str):
self._currentGameID = gameID
self._player_data = {name: Player(name, self, **cfg) for name, cfg in self._cfg["players"].items()}
@gameID.deleter
def gameID(self):
self._currentGameID = None
self._player_data = {name: Player(name, self, **cfg) for name, cfg in self._cfg["players"].items()}
def keys(self):
""" Iterator of the active players' names """
return self._player_data.keys()
def values(self):
""" Iterator of the active players' objects """
return self._player_data.keys()
def items(self):
""" Two iterators of the activate players' names and object """
return self._player_data.items()
def save(self):
with open(self._playersPath, 'w') as f:
self._cfg = json.dump(f, self._cfg)
class Player:
"""
Attributes:
flags:
availableExpansions:
expansionsConfig:
gameSave: The game settings are not guaranteed to have data in it.
gameState:
Methods:
generateConfig:
"""
def __init__(self, name: str, manager: PlayersManager, **cfg):
self._name = name
self._manager = manager
for key, val in cfg.items():
setattr(self, "_" + key, val)
def __repr__(self):
return f"Player {self._name} has {len(self._flags)} flag(s), {len(self._expansions)} expansions and {len(self._games)} saved game"
@property
def name(self):
return self._name
@property
def flags(self):
return self._flags
@flags.setter
def flags(self, newFlags):
self._flags = newFlags
# The container was changed and must be transmited to the manager
self._manager[self._name] = self.generateConfig()
@property
def expansionsConfig(self):
return self._expansionsConfig
@expansionsConfig.setter
def expansions(self, newExpansions):
self._expansionsConfig = newExpansions
# The container was changed and must be transmited to the manager
self._manager[self._name] = self.generateConfig()
@property
def gameSave(self):
"""The save for the current game. If no configuration was found None is returned."""
return self._games.get(self._manager.gameID)
@gameSave.setter
def gameSave(self, newGameSave):
self._games[self._manager.gameID] = newGameSave
def generateConfig(self):
return {"flags": self._flags, "expansions": self._expansions, "games": self._games}
def punish(self, value, preferedExpansion: str=None):
do_something = lambda value: value
additionalInfos = {"expansionID": "challengeDB", "balancedValue": 0.2, "showOnScreen": "Do 100 push-up", "error": None, "done": False}
# NOTE Make a result class instead and an error class
additionalInfos = do_something(value)
return additionalInfos
class Expansion:
"""
Attributes:
Methods:
step:
reset:
close:
"""
@abc.abstractmethod
def __init__(self):
"""
Raise an error if not available
"""
pass
@abc.abstractmethod
def step(self, action):
"""
Call close if an error is thrown
"""
pass
@abc.abstractmethod
def reset(self):
pass
@abc.abstractmethod
def close(self):
pass
class ExpansionManager:
"""
Manager of the availability of the expansions.
Attributes:
includedExpansions: tuple of str
Container of the expansions to try making available
releventExpansions: dict of Expansion
Container of the relevent expansions
"""
tags = ["shock", "spice", "sour", "drink", "challenge"]
def __init__(self, playersManager: PlayersManager, includedExpansions: tuple[str]=None):
self.playersManager = playersManager
self._includedExpansions = tuple(includedExpansions) if includedExpansions is not None else ()
self._releventExpansions = {} # TODO Populate the dictionnary
@property
def includedExpansions(self):
return self._includedExpansions
@includedExpansions.setter
def includedExpansions(self, newIncluded: tuple[str]):
self._includedExpansions = tuple(newIncluded)
self._includedChanged()
@includedExpansions.deleter
def includedExpansions(self):
self._includedExpansions = ()
@property
def releventExpansions(self):
return self._releventExpansions
def _includedChanged(self):
"""
Compute the list of only the relevant expansions.
The expansions that do not exists are excluded.
The expansions that have no players assigned to them are excluded.
The expansions that are not responding are excluded.
"""
pass
def _activePlayersChanged(self, activatePlayers):
pass
def _errorOccured(self, expansionID):
pass
def _expansionPlayersChanged(self, expansionID):
pass
def resetExpansion(self, expansion: Expansion, suppressError: bool=False):
"""
If the expansion was relevent, close it.
In any case, try creating a new
"""
# If the expansion was relevent, close it
# Try creating a new instance of the expansion
# Update the list of relevent expansions
pass
if __name__ == "__main__":
# Test run to make sure nothing is flagrantly flawed
configPath = Path(__file__).parent / "players.json"
manager = PlayersManager(playersPath=configPath, gameID="gameID0")
# Iteration
for playerName in manager:
print(playerName)
# Modification of a players data
brosef = manager["Brosef"]
brosef.gameSave = [1]
print(manager["Brosef"].gameSave)
brosef.state = ["alive"]

14
src/players.json Normal file
View File

@ -0,0 +1,14 @@
{
"expansions":
{
"shockColar1": {"players": ["Brosef"], "class": "pishock", "type": ["shock"]},
"robotBarman": {"players": ["Tango", "TRS_MML"], "class": "roboticBarman", "type": ["drink"]},
"challengeDB": {"players": ["TRS_MML"], "class": "challengeDataBase", "type": ["challenge"]},
"sourCandy": {"players": [], "class": "sourCandy", "type": ["food"]}
},
"players": {
"Brosef": {"flags": [], "expansions": {"exampleExpansion": {"playerOption": 5}}, "games": {"gameID0": 2}},
"TRS_MML": {"flags": [], "expansions": {"exampleExpansion": {"playerOption": 5}}, "games": {"gameID0": 1}},
"Tango": {"flags": [], "expansions": {"exampleExpansion": {"playerOption": 5}}, "games": {"gameID0": 0}}
}
}

6
src/standardFlags.py Normal file
View File

@ -0,0 +1,6 @@
NO_ALCOHOL = 'no-alcohol'
NO_SHOCK = 'no-shock'
NO_SPICE = 'no-spice'
NO_SOUR = 'no-sour'
EASY_MODE = 'pussy-mode'
HARD_MODE = 'hard-mode'