import gradio as gr import numpy as np import os import gymnasium as gym from gymnasium import register, spaces from stable_baselines3 import PPO from huggingface_hub import hf_hub_download # --- PARAMÈTRES DU DÉPÔT HUGGING FACE --- REPO_ID = "Clemylia/MiRobot" MODEL_FILE = "mirobot_final_model.zip" ENV_SCRIPT_FILE = "MiRobotEnv.py" ENV_ID = 'MiRobot-v0' # --- CONSTANTES DE JEU --- GRID_SIZE = 10 FAIM_PENALTY_THRESHOLD = 0.9 INITIAL_LEVEL = 1 # --- INDICES D'ÉTAT --- CMD_AVANCER = 0 CMD_TOURNER = 1 ETAT_FAIM = 2 ETAT_SOMMEIL = 3 ETAT_HUMEUR = 4 # Map des actions ACTION_MAP_MODEL = {0: "S'Arrêter", 1: "Avancer", 2: "Tourner G", 3: "Tourner D"} ACTION_MAP_USER = { "AVANCER": 1, "TOURNER À GAUCHE": 2, "TOURNER À DROITE": 3, } # ---------------------------------------------------------------------- # 1. PRÉPARATION DU MODÈLE ET DE L'ENVIRONNEMENT # ---------------------------------------------------------------------- model = None env = None MiRobotEnv = None initial_faim = 0.0 initial_humeur = 0.0 try: print("Téléchargement des fichiers MiRobot...") TEMP_DIR = "./mirobot_assets" os.makedirs(TEMP_DIR, exist_ok=True) # --- 1. Chargement de la classe MiRobotEnv --- env_path = hf_hub_download(repo_id=REPO_ID, filename=ENV_SCRIPT_FILE, local_dir=TEMP_DIR) env_globals = {'gym': gym, 'np': np, 'spaces': spaces} with open(env_path, 'r') as f: exec(f.read(), env_globals) MiRobotEnv = env_globals['MiRobotEnv'] # --- 2. Enregistrement de l'environnement Custom --- register( id=ENV_ID, entry_point=MiRobotEnv, ) # --- 3. Chargement du Modèle et de l'Environnement --- model_path = hf_hub_download(repo_id=REPO_ID, filename=MODEL_FILE, local_dir=TEMP_DIR) model = PPO.load(model_path) env = gym.make(ENV_ID) env.reset() # Lecture initiale sécurisée de l'état initial_faim = env.unwrapped.state[ETAT_FAIM] * 100 initial_humeur = env.unwrapped.state[ETAT_HUMEUR] print("✅ MiRobot prêt pour l'interface Gradio.") except Exception as e: print(f"❌ ERREUR CRITIQUE lors du chargement de MiRobot: {e}") # ---------------------------------------------------------------------- # 2. LOGIQUE DU JEU # ---------------------------------------------------------------------- game_state_initial = { 'level': INITIAL_LEVEL, 'puppy_pos': [GRID_SIZE // 2, GRID_SIZE // 2], 'reward_pos': [0, 0], 'message': 'Bienvenue ! Téléchargez une récompense et commencez !', 'reward_asset_path': None } def _get_env_state(env_instance): """Accès sécurisé à l'état de l'environnement, même avec un wrapper.""" if env_instance is None: return None # Utilise .unwrapped pour accéder à l'instance de MiRobotEnv return env_instance.unwrapped.state def _reset_game(reward_path): """Réinitialise les positions et l'état interne du chiot.""" new_state = game_state_initial.copy() if env is not None: obs, info = env.reset() current_state = _get_env_state(env) faim_display = current_state[ETAT_FAIM] * 100 humeur_display = current_state[ETAT_HUMEUR] else: faim_display = 0.0 humeur_display = 0.0 new_state.update({ 'reward_asset_path': reward_path, 'message': f'Jeu réinitialisé. Niveau {INITIAL_LEVEL}. Placez la récompense !' }) return new_state, new_state['puppy_pos'][0], new_state['puppy_pos'][1], new_state['reward_pos'][0], new_state['reward_pos'][1], faim_display, humeur_display, new_state['message'] def handle_user_command(current_state, command_text, reward_path): """Gère une commande utilisateur et l'action du modèle RL.""" game_state = current_state current_env_state = _get_env_state(env) if model is None or env is None: return game_state, command_text, 5, 5, 0, 0, '❌ Erreur: Le modèle MiRobot n\'a pas pu être chargé !' game_state['reward_asset_path'] = reward_path # 2. Vérification de la faim (défaite) faim_actuelle = current_env_state[ETAT_FAIM] if faim_actuelle >= FAIM_PENALTY_THRESHOLD: game_state['message'] = f'💔 Défaite ! MiRobot a trop faim ({faim_actuelle:.0%}). Jeu réinitialisé.' return _reset_game(reward_path) command_upper = command_text.upper() if command_upper not in ACTION_MAP_USER: game_state['message'] = f"🤔 MiRobot n'a pas compris l'ordre '{command_text}'. Sa faim augmente..." current_env_state[CMD_AVANCER] = 0.0 current_env_state[CMD_TOURNER] = 0.0 env.step(0) faim_display = current_env_state[ETAT_FAIM] * 100 humeur_display = current_env_state[ETAT_HUMEUR] return game_state, command_text, game_state['puppy_pos'][0], game_state['puppy_pos'][1], faim_display, humeur_display, game_state['message'] # 4. Exécution de la décision du Modèle command_action_name = command_upper current_env_state[CMD_AVANCER] = 1.0 if command_action_name == "AVANCER" else 0.0 current_env_state[CMD_TOURNER] = 1.0 if command_action_name.startswith("TOURNER") else 0.0 mirobot_action_id, _ = model.predict(current_env_state, deterministic=True) # **CORRECTION CRUCIALE**: Convertir le tableau NumPy en entier mirobot_action_id = mirobot_action_id.item() new_obs, reward, terminated, truncated, info = env.step(mirobot_action_id) dx, dy = 0, 0 if mirobot_action_id == ACTION_MAP_USER[command_action_name]: game_state['message'] = f"👏 MiRobot a obéi à '{command_action_name}'. Récompense RL: {reward:.2f}" if command_action_name == "AVANCER": rx, ry = game_state['reward_pos'] px, py = game_state['puppy_pos'] if abs(rx - px) > abs(ry - py): dx = 1 if rx > px else -1 elif abs(ry - py) > 0: dy = 1 if ry > py else -1 else: # Cette ligne est maintenant sûre car mirobot_action_id est un entier real_action_name = ACTION_MAP_MODEL[mirobot_action_id] game_state['message'] = f"😥 MiRobot a désobéi ! Il a fait '{real_action_name}' au lieu de '{command_action_name}'. Récompense RL: {reward:.2f}" # Mise à jour de la position new_x = np.clip(game_state['puppy_pos'][0] + dx, 0, GRID_SIZE - 1) new_y = np.clip(game_state['puppy_pos'][1] + dy, 0, GRID_SIZE - 1) game_state['puppy_pos'] = [new_x, new_y] current_env_state = _get_env_state(env) faim_display = current_env_state[ETAT_FAIM] * 100 humeur_display = current_env_state[ETAT_HUMEUR] return game_state, command_text, new_x, new_y, faim_display, humeur_display, game_state['message'] def handle_bravo(current_state): """Gère l'événement de récompense.""" game_state = current_state current_env_state = _get_env_state(env) if env is None: return game_state, game_state['puppy_pos'][0], game_state['puppy_pos'][1], 0, 0, '❌ Erreur: Modèle non chargé.' px, py = game_state['puppy_pos'] rx, ry = game_state['reward_pos'] if px == rx and py == ry: game_state['level'] += 1 # Modification de l'état current_env_state[ETAT_FAIM] = np.clip(current_env_state[ETAT_FAIM] - 0.5, 0.0, 1.0) current_env_state[ETAT_HUMEUR] = np.clip(current_env_state[ETAT_HUMEUR] + 0.5, -1.0, 1.0) game_state['message'] = f'🥳 BRAVO ! MiRobot a bien réussi ! Niveau suivant : {game_state["level"]}. Repositionnez la récompense pour continuer !' else: game_state['message'] = '😕 MiRobot doit être sur la case de la récompense pour recevoir un "BRAVO" !' faim_display = current_env_state[ETAT_FAIM] * 100 humeur_display = current_env_state[ETAT_HUMEUR] return game_state, game_state['puppy_pos'][0], game_state['puppy_pos'][1], faim_display, humeur_display, game_state['message'] def _draw_grid(px, py, rx, ry, reward_path): """Dessine la grille de jeu avec le chiot et la récompense.""" if reward_path is None: return "
Veuillez télécharger une image de récompense pour afficher la grille.
" grid_html = f"