Compare commits

16 Commits

Author SHA1 Message Date
toucan_dev 8ce78103d0 🎨 code format 2026-07-26 15:38:04 +02:00
toucan_dev 5cffcba502 🐛 fix UI not propagated 2026-07-26 15:33:12 +02:00
toucan_dev 3d7a03fb2b list log levels in CLI 2026-07-26 15:26:45 +02:00
toucan_dev 7e26db26c7 ♻️ use entrypoint to load UI 2026-07-26 15:24:44 +02:00
toucan_dev 732afb65c5 test entrypoint class 2026-07-26 15:15:32 +02:00
toucan_dev 2c2e3b0597 entrypoint class 2026-07-26 15:15:23 +02:00
toucan_dev 181b928c25 🔧 define UI entrypoint 2026-07-26 14:46:22 +02:00
bbaudouin 583a026dc4 ajout d'images pour les ennemise 2026-07-21 21:27:23 +02:00
toucan_dev 9efdc27332 cli with argparse 2026-07-19 18:42:07 +02:00
toucan_dev 33052979f0 ♻️ use console ui 2026-07-19 17:48:09 +02:00
toucan_dev fe5b5890b2 console ui 2026-07-19 17:47:49 +02:00
toucan_dev d02b48b2b0 🔊 add some logs 2026-07-19 00:50:29 +02:00
toucan_dev 3746f32cd0 🔊 log config for app 2026-07-19 00:14:32 +02:00
toucan_dev b7daf32569 🔊 replace print by logger 2026-07-19 00:14:18 +02:00
toucan_dev e5eb96623f 🎨 code format 2026-07-19 00:00:58 +02:00
toucan_dev df93b8a587 🔊 log config for tests 2026-07-18 23:15:58 +02:00
14 changed files with 188 additions and 24 deletions
Executable
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

+3
View File
@@ -30,6 +30,9 @@ build-backend = "setuptools.build_meta"
[project.scripts] [project.scripts]
elironwars = "elironwars.app:app" elironwars = "elironwars.app:app"
[project.entry-points."elironwars.ui"]
console = "elironwars.console:ConsoleUi"
[tool.isort] [tool.isort]
profile = "black" profile = "black"
src_paths = ["src", "tests"] src_paths = ["src", "tests"]
+48 -15
View File
@@ -1,27 +1,40 @@
import argparse
import logging
import os
import sys import sys
from typing import Any
from .entrypoints import Entrypoints
from .level import Level from .level import Level
from .menu import Action, action_menu from .menu import Action
from .mob import BaseMob, load_character from .mob import BaseMob, load_character
logger = logging.getLogger(__name__)
# FIXME replace this global declaration
UI: Any = None
def play_room(room: list[BaseMob], level: Level) -> int: def play_room(room: list[BaseMob], level: Level) -> int:
if room is None: if room is None:
print("\n ------ Pièce vide ------ \n") logger.info('Empty room')
UI.empty_room()
return 1 return 1
for mob in room: for mob in room:
print(f"Un {mob.name} est devant toi !") logger.info('Meet mob %s', mob.name)
match action_menu([Action.COMBAT, Action.FUIR]): UI.meet_mob(mob)
match UI.action_menu([Action.COMBAT, Action.FUIR]):
case Action.COMBAT: case Action.COMBAT:
level.combat(mob, level.player) pvs = level.combat(mob, level.player)
UI.fight_pvs(level.player, mob, pvs)
if level.player.current_pv <= 0: if level.player.current_pv <= 0:
print("!!!!!!!!! Joueur mort !!!!!!!!!!") UI.player_dead()
sys.exit(0) sys.exit(0)
if mob.current_pv == 0: if mob.current_pv == 0:
print(f"Le {mob.name} est mort !!") UI.mob_dead(mob)
case Action.FUIR: case Action.FUIR:
print("Tu fuis la pièce") UI.run_away()
return 0 return 0
return 1 return 1
@@ -29,25 +42,45 @@ def play_room(room: list[BaseMob], level: Level) -> int:
def play_donjon(level: Level) -> None: def play_donjon(level: Level) -> None:
while level.exit_level: while level.exit_level:
print(f"Pièce actuelle : {level.current_room_index}") UI.entering_room(level.current_room_index)
match action_menu([Action.PIECE_SUIVANTE, Action.QUITTER]): match UI.action_menu([Action.PIECE_SUIVANTE, Action.QUITTER]):
case Action.PIECE_SUIVANTE: case Action.PIECE_SUIVANTE:
if level.current_room_index >= len(level.rooms): if level.current_room_index >= len(level.rooms):
print("fin du donjon !") logger.info('Exiting last room')
UI.last_room()
break break
logger.info('Entering next room')
level.current_room_index += play_room(level.rooms[level.current_room_index], level) level.current_room_index += play_room(level.rooms[level.current_room_index], level)
level.player.current_pv = level.player.max_pv level.player.current_pv = level.player.max_pv
case Action.QUITTER: case Action.QUITTER:
level.exit_level = False level.exit_level = False
print(f"Au revoir {level.player.name} !") logger.info('Leaving game')
UI.leave(level.player)
def app() -> None: def app() -> None:
loaded_character = load_character(sys.argv[1] if len(sys.argv) > 1 else "Jamin") entrypoints = Entrypoints()
parser = argparse.ArgumentParser(
prog='elironwars',
description='elironwars CLI',
)
parser.add_argument('--player', default='Jamin')
parser.add_argument('--ui', default='console', choices=entrypoints.ui.names)
parser.add_argument('--log-level', default='ERROR', choices=logging.getLevelNamesMapping().keys())
args = parser.parse_args()
os.makedirs('logs', exist_ok=True)
logging.basicConfig(filename='logs/app.log', level=logging.getLevelName(args.log_level))
logging.info('start')
loaded_character = load_character(args.player)
if not loaded_character: if not loaded_character:
sys.exit(-1) sys.exit(-1)
print(loaded_character) global UI
UI = entrypoints.load_ui(args.ui)
UI.select_player(loaded_character)
# Créer des instances de BaseMob # Créer des instances de BaseMob
yanaar = BaseMob("Yanaar", 15, 2, 1, 3, 2) yanaar = BaseMob("Yanaar", 15, 2, 1, 3, 2)
@@ -58,7 +91,7 @@ def app() -> None:
mobs_list = [yanaar, skroll, ulnarain] mobs_list = [yanaar, skroll, ulnarain]
level = Level(2, loaded_character, mobs_list) level = Level(2, loaded_character, mobs_list)
print(f"Nombre de pièce dans le donjon : {len(level.rooms)}") UI.entering_donjon(level)
play_donjon(level) play_donjon(level)
+60
View File
@@ -0,0 +1,60 @@
from .menu import Action, action_menu
class ConsoleUi:
def __init__(self):
pass
@staticmethod
def empty_room():
print("\n ------ Pièce vide ------ \n")
@staticmethod
def meet_mob(mob):
print(f"Un {mob.name} est devant toi !")
@staticmethod
def player_dead():
print("!!!!!!!!! Joueur mort !!!!!!!!!!")
@staticmethod
def mob_dead(mob):
print(f"Le {mob.name} est mort !!")
@staticmethod
def run_away():
print("Tu fuis la pièce")
@staticmethod
def leave(player):
print(f"Au revoir {player.name} !")
@staticmethod
def select_player(player):
print(player)
@staticmethod
def entering_room(room_name):
print(f"Pièce actuelle : {room_name}")
@staticmethod
def last_room():
print("fin du donjon !")
@staticmethod
def entering_donjon(level):
print(f"Nombre de pièce dans le donjon : {len(level.rooms)}")
@staticmethod
def action_menu(actions_autorisees: list[Action] = None) -> Action:
return action_menu(actions_autorisees)
@staticmethod
def fight_pvs(player, mob, pvs):
for pv in pvs:
print(f"{player.name} - {pv['player']} PV")
print(f"{mob.name} - {pv['mob']} PV")
UI = ConsoleUi()
+18
View File
@@ -0,0 +1,18 @@
import logging
from importlib.metadata import entry_points
logger = logging.getLogger(__name__)
class Entrypoints:
def __init__(self):
self.ui = entry_points(group='elironwars.ui')
def load_ui(self, ui):
logger.info('Loading ui %s', ui)
try:
ui_class = self.ui[ui].load()
return ui_class()
except KeyError:
logger.critical('Unable to load ui %s', ui)
+12 -4
View File
@@ -1,21 +1,26 @@
import logging
from .random_utils import generate_random_mob_list, generate_random_rooms from .random_utils import generate_random_mob_list, generate_random_rooms
logger = logging.getLogger(__name__)
class Level: class Level:
""" Class Level """ """ Class Level """
def __init__(self, difficulty, player, mob_list): def __init__(self, difficulty, player, mob_list):
""" Chargement du niveau """ """ Chargement du niveau """
print("Loading Level") logger.debug("Loading Level")
self.mobs_in_level = generate_random_mob_list(mob_list, difficulty) self.mobs_in_level = generate_random_mob_list(mob_list, difficulty)
self.exit_level = True self.exit_level = True
self.player = player self.player = player
self.rooms = generate_random_rooms(self.mobs_in_level, difficulty) # Liste des pièces du niveau self.rooms = generate_random_rooms(self.mobs_in_level, difficulty) # Liste des pièces du niveau
self.current_room_index = 0 # Index de la pièce actuelle self.current_room_index = 0 # Index de la pièce actuelle
print("Level Loaded") logger.debug("Level Loaded")
def combat(self, mob, player): def combat(self, mob, player):
pvs = []
while mob.current_pv > 0 and player.current_pv > 0: while mob.current_pv > 0 and player.current_pv > 0:
if mob.speed > player.speed: if mob.speed > player.speed:
mob.perform_attack(player) mob.perform_attack(player)
@@ -23,5 +28,8 @@ class Level:
else: else:
player.perform_attack(mob) player.perform_attack(mob)
mob.perform_attack(player) mob.perform_attack(player)
print(f"{player.name} - {player.current_pv} PV") pvs.append({
print(f"{mob.name} - {mob.current_pv} PV") 'player': player.current_pv,
'mob': mob.current_pv
})
return pvs
+10
View File
@@ -1,6 +1,9 @@
import logging
import os import os
from enum import Enum from enum import Enum
logger = logging.getLogger(__name__)
class Action(Enum): class Action(Enum):
PIECE_SUIVANTE = (1, "Pièce suivante") PIECE_SUIVANTE = (1, "Pièce suivante")
@@ -26,6 +29,10 @@ def action_menu(actions_autorisees: list[Action] = None) -> Action:
actions_autorisees = list(Action) if actions_autorisees is None else actions_autorisees actions_autorisees = list(Action) if actions_autorisees is None else actions_autorisees
logger.debug('Show action menu')
for action in actions_autorisees:
logger.debug('Action %s - "%s"', action.code, action.libelle)
while True: while True:
print("\n------ Menu Action ------\n") print("\n------ Menu Action ------\n")
@@ -37,9 +44,12 @@ def action_menu(actions_autorisees: list[Action] = None) -> Action:
clear_console() clear_console()
for action in actions_autorisees: for action in actions_autorisees:
if action.code == choix: if action.code == choix:
logger.info('User selected action %s - "%s"', action.code, action.libelle)
return action return action
print("Mauvais choix") print("Mauvais choix")
except ValueError: except ValueError:
clear_console() clear_console()
print("Veuillez saisir un nombre.") print("Veuillez saisir un nombre.")
logger.info('Bad input')
+8 -4
View File
@@ -1,12 +1,15 @@
import json import json
import logging
import os import os
from .base_mob import BaseMob from .base_mob import BaseMob
logger = logging.getLogger(__name__)
def build_character(): def build_character():
"""Demande à l'utilisateur de saisir les attributs du personnage.""" """Demande à l'utilisateur de saisir les attributs du personnage."""
print("Création d'un nouveau personnage :") logger.debug("Création d'un nouveau personnage :")
name = input("Nom du personnage : ") name = input("Nom du personnage : ")
max_pv = int(input("Points de vie maximum : ")) max_pv = int(input("Points de vie maximum : "))
strength = int(input("Force : ")) strength = int(input("Force : "))
@@ -35,9 +38,10 @@ def save_character(character: BaseMob) -> str:
def load_character(name: str) -> BaseMob | None: def load_character(name: str) -> BaseMob | None:
"""Charge un personnage à partir d'un fichier JSON.""" """Charge un personnage à partir d'un fichier JSON."""
logger.debug('Loading character %s', name)
filename = name + ".json" filename = name + ".json"
if not os.path.exists(filename): if not os.path.exists(filename):
print(f"Erreur : Le fichier {filename} n'existe pas.") logger.error(f"Erreur : Le fichier {filename} n'existe pas.")
return None return None
try: try:
@@ -48,7 +52,7 @@ def load_character(name: str) -> BaseMob | None:
required_keys = ["name", "max_pv", "current_pv", required_keys = ["name", "max_pv", "current_pv",
"strength", "protection", "speed", "mob_type"] "strength", "protection", "speed", "mob_type"]
if not all(key in character_data for key in required_keys): if not all(key in character_data for key in required_keys):
print(f"Erreur : Le fichier {filename} ne contient pas toutes les clés nécessaires.") logger.error(f"Erreur : Le fichier {filename} ne contient pas toutes les clés nécessaires.")
return None return None
# Construction du personnage # Construction du personnage
@@ -61,5 +65,5 @@ def load_character(name: str) -> BaseMob | None:
mob_type=character_data["mob_type"] mob_type=character_data["mob_type"]
) )
except json.JSONDecodeError: except json.JSONDecodeError:
print(f"Erreur : Le fichier {filename} n'est pas un JSON valide.") logger.error(f"Erreur : Le fichier {filename} n'est pas un JSON valide.")
return None return None
+3
View File
@@ -0,0 +1,3 @@
import logging
logging.basicConfig(level=logging.DEBUG)
+1 -1
View File
@@ -3,7 +3,7 @@ import pathlib
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from elironwars.mob import BaseMob, save_character, load_character from elironwars.mob import BaseMob, load_character, save_character
from elironwars.mob.default_builder import build_character from elironwars.mob.default_builder import build_character
ARTHUR_JSON = """{ ARTHUR_JSON = """{
+25
View File
@@ -0,0 +1,25 @@
import unittest
from elironwars.console import ConsoleUi
from elironwars.entrypoints import Entrypoints
class TestEntrypoints(unittest.TestCase):
def test_load_console_ui(self):
console_ep = 'console'
ep = Entrypoints()
self.assertIn(console_ep, ep.ui.names)
self.assertIsInstance(ep.load_ui(console_ep), ConsoleUi)
def test_load_unknown_ui(self):
unknown_ep = 'unknown'
ep = Entrypoints()
self.assertNotIn(unknown_ep, ep.ui.names)
self.assertIsNone(ep.load_ui(unknown_ep), ConsoleUi)
if __name__ == "__main__":
unittest.main()