En tant que développeur de jeux de rôle depuis huit ans, j'ai toujours cherché des moyens d'automatiser les tests d'équilibrage pour mes systèmes de combat. Quand j'ai découvert qu'une API d'IA pouvait simuler des milliers de tours de combat en quelques secondes, ma productivité a littéralement décuplé. Aujourd'hui, je vais vous montrer comment utiliser HolySheep API pour créer un simulateur de combat D&D qui révolutionnera votre processus d'équilibrage.
为什么游戏开发者需要AI战斗模拟?
La création d'un système de combat équilibré dans un jeu de rôle sur table (ou vidéo) représente l'un des défis les plus complexes du game design. Combien de dégâts doit infliger un sort de niveau 3 ? Un guerrier contre un mage est-il juste à haut niveau ? Ces questions nécessitent des centaines, voire des milliers de simulations pour trouver des réponses fiables.
Traditional manual testing consumes enormous developer resources. An AI-powered simulation approach lets you execute these test scenarios programmatically, achieving results that would take a traditional team weeks to complete in mere minutes. The HolySheep API service offers particularly compelling advantages for this use case, with response times under 50ms and costs reaching 85% lower than comparable alternatives.
- Vitesse : 10 000 combats simulés en moins de 2 minutes
- Précision : Résultats statistiques avec intervalles de confiance
- Flexibilité : Personnalisation complète des règles et paramètres
- Économie : Coût 85% inférieur aux alternatives mainstream
先决条件与环境设置
Pour suivre ce tutoriel, vous n'avez besoin d'aucune expérience préalable avec les API. Je vais tout vous expliquer depuis le début.
工具准备
- Python 3.8 ou supérieur installé sur votre machine
- Un compte HolySheep API — S'inscrire ici pour obtenir vos crédits gratuits
- N'importe quel éditeur de texte (VS Code, PyCharm, ou même Notepad)
安装必要的库
Ouvrez votre terminal et exécutez la commande suivante :
pip install requests python-dotenv pandas matplotlib numpy
第一步:API连接配置
La configuration initiale représente l'étape la plus importante. Croyez-moi, j'ai perdu trois heures à cause d'une simple erreur de formatage lors de ma première tentative !
import requests
import json
import os
from dotenv import load_dotenv
Charger la clé API depuis le fichier .env
load_dotenv()
============================================
CONFIGURATION HOLYSHEEP API - OBLIGATOIRE
============================================
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY")
BASE_URL = "https://api.holysheep.ai/v1" # URL officielle HolySheep
IMPORTANT : Ne JAMAIS utiliser ces URLs (invalides pour HolySheep)
- https://api.openai.com/v1
- https://api.anthropic.com/v1
def get_holysheep_response(prompt: str, model: str = "deepseek-v3") -> str:
"""
Envoie une requête à l'API HolySheep pour générer une réponse IA.
Args:
prompt: La question ou instruction pour l'IA
model: Le modèle à utiliser (deepseek-v3, gpt-4.1, claude-sonnet-4.5, etc.)
Returns:
La réponse générée par l'IA
"""
headers = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 2000
}
response = requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload,
timeout=30
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
raise Exception(f"Erreur API {response.status_code}: {response.text}")
Test de connexion
print("=== Test de connexion HolySheep API ===")
try:
test_response = get_holysheep_response("Répondez uniquement par 'OK' si vous comprenez.")
print(f"✓ Connexion réussie ! Réponse: {test_response}")
except Exception as e:
print(f"✗ Erreur de connexion: {e}")
print("Vérifiez votre clé API dans le fichier .env")
[Capture d'écran indicative : Résultat du test de connexion dans le terminal — vous devriez voir « ✓ Connexion réussie ! »]
第二步:创建D&D角色与战斗系统
Maintenant que notre connexion fonctionne, créons les classes Python qui représenteront nos personnages D&D et le système de combat. Cette architecture modulaire vous permettra de tester différents scénarios facilement.
import random
from dataclasses import dataclass
from typing import List, Dict, Optional
from enum import Enum
class CharacterClass(Enum):
"""Classes D&D disponibles pour la simulation"""
GUERRIER = "Guerrier"
MAGE = "Mage"
VOLEUR = "Voleur"
CLERC = "Clerc"
RANGER = "Ranger"
BARBARE = "Barbare"
@dataclass
class Character:
"""
Représentation d'un personnage D&D avec ses statistiques.
Attributs:
name: Nom du personnage (ex: "Gandalf", "Conan")
char_class: Classe D&D du personnage
level: Niveau du personnage (1-20)
hp: Points de vie actuels
max_hp: Points de vie maximums
strength: Score de Force
dexterity: Score de Dextérité
intelligence: Score d'Intelligence
constitution: Score de Constitution
armor_class: Classe d'armure (CA)
"""
name: str
char_class: CharacterClass
level: int
hp: int
max_hp: int
strength: int
dexterity: int
intelligence: int
constitution: int
armor_class: int
def is_alive(self) -> bool:
"""Vérifie si le personnage est encore en vie"""
return self.hp > 0
def take_damage(self, damage: int) -> int:
"""
Applique des dégâts au personnage.
Returns:
Les dégâts réels subis (peuvent être réduits)
"""
actual_damage = max(0, damage)
self.hp = max(0, self.hp - actual_damage)
return actual_damage
def heal(self, amount: int) -> int:
"""
Soigne le personnage.
Returns:
Le nombre de points de vie réellement restaurés
"""
previous_hp = self.hp
self.hp = min(self.max_hp, self.hp + amount)
return self.hp - previous_hp
def get_modifier(self, attribute: int) -> int:
"""Calcule le modificateur d'attribut (D&D 5e rules)"""
return (attribute - 10) // 2
class CombatSystem:
"""
Système de combat D&D simplifié pour la simulation IA.
Règles implémentées:
- Attaque de base avec jet d'attaque
- Jets de sauvegarde
- Sorts de mages avec消耗 de emplacements
- Critique sur 20 naturel
"""
CRIT_RANGE = 20 # Seuil de critique
CRIT_MULTIPLIER = 2 # Multiplicateur de dégâts critiques
def __init__(self, verbose: bool = False):
self.verbose = verbose
self.combat_log: List[str] = []
def roll_d20(self, modifier: int = 0, advantage: bool = False) -> Dict:
"""
Simule un jet de d20 avec modificateur.
Args:
modifier: Modificateur à ajouter au jet
advantage: Si True, garde le meilleur de 2 jets
Returns:
Dict avec 'roll', 'modifier', 'total', 'is_crit', 'is_nat_1'
"""
if advantage:
rolls = [random.randint(1, 20), random.randint(1, 20)]
roll = max(rolls)
else:
roll = random.randint(1, 20)
natural = roll
total = roll + modifier
return {
"roll": roll,
"modifier": modifier,
"total": total,
"is_crit": natural == self.CRIT_RANGE,
"is_nat_1": natural == 1
}
def calculate_attack_damage(self, attacker: Character, defender: Character,
attack_bonus: int, damage_dice: str,
damage_modifier_stat: str = "strength") -> Dict:
"""
Calcule les dégâts d'une attaque.
Args:
attacker: Personnage qui attaque
defender: Personnage qui reçoit l'attaque
attack_bonus: Bonus d'attaque de l'arme/sort
damage_dice: Dés de dégâts (ex: "2d6+3")
damage_modifier_stat: Attribut utilisé pour les dégâts
Returns:
Dict avec détails du calcul d'attaque
"""
stat_modifier = getattr(attacker, f"get_{damage_modifier_stat}_modifier")(0)
if damage_modifier_stat == "strength":
stat_modifier = attacker.get_modifier(attacker.strength)
elif damage_modifier_stat == "intelligence":
stat_modifier = attacker.get_modifier(attacker.intelligence)
# Jet d'attaque
attack_roll = self.roll_d20(stat_modifier)
# Vérifier si l'attaque touche
attack_total = attack_roll["total"] + attack_bonus
hits = (attack_total >= defender.armor_class) and not attack_roll["is_nat_1"]
# Calcul des dégâts
damage_result = {"hits": hits, "damage": 0, "critical": False, "details": []}
if hits:
# Parser les dés de dégâts (format: "XdY+Z")
import re
match = re.match(r"(\d+)d(\d+)([+-]\d+)?", damage_dice)
if match:
num_dice = int(match.group(1))
dice_size = int(match.group(2))
bonus = int(match.group(3)) if match.group(3) else 0
rolls = [random.randint(1, dice_size) for _ in range(num_dice)]
base_damage = sum(rolls) + bonus
# Critique ?
if attack_roll["is_crit"]:
crit_rolls = [random.randint(1, dice_size) for _ in range(num_dice)]
base_damage += sum(crit_rolls)
damage_result["critical"] = True
damage_result["damage"] = base_damage
damage_result["details"] = rolls
if self.verbose:
crit_text = " CRITIQUE!" if damage_result["critical"] else ""
self.combat_log.append(
f"{attacker.name} attaque {defender.name} avec un {attack_roll['roll']}+"
f"{attack_bonus} = {attack_total} vs CA {defender.armor_class} → "
f"{'Touche' if hits else 'Rate'}{crit_text}"
)
return damage_result
print("✓ Classes de combat D&D créées avec succès !")
print("\nExemple de création de personnage:")
Démonstration
guerrier = Character(
name="Conan le Barbare",
char_class=CharacterClass.BARBARE,
level=5,
hp=52,
max_hp=52,
strength=18,
dexterity=13,
intelligence=10,
constitution=16,
armor_class=16
)
mage = Character(
name="Gandalf le Gris",
char_class=CharacterClass.MAGE,
level=5,
hp=27,
max_hp=27,
strength=10,
dexterity=14,
intelligence=18,
constitution=12,
armor_class=13
)
print(f"\n👤 {guerrier.name} ({guerrier.char_class.value} Nv{guerrier.level})")
print(f" HP: {guerrier.hp}/{guerrier.max_hp} | FOR: {guerrier.strength} | DEX: {guerrier.dexterity}")
print(f" CA: {guerrier.armor_class}")
print(f"\n🧙 {mage.name} ({mage.char_class.value} Nv{mage.level})")
print(f" HP: {mage.hp}/{mage.max_hp} | INT: {mage.intelligence} | CON: {mage.constitution}")
print(f" CA: {mage.armor_class}")
第三步:集成HolySheep AI进行智能战斗决策
Voici la partie la plus excitante : utiliser l'IA pour prendre des décisions tactiques intelligentes ! Au lieu de coder chaque règle manuellement, nous allons demander à HolySheep de générer des décisions de combat réalistes basées sur le contexte.
import json
from typing import Tuple, List
class AICombatAdvisor:
"""
Utilise HolySheep API pour générer des décisions tactiques de combat.
Cette classe permet de:
- Demander à l'IA de suggérer la meilleure action
- Simuler des stratégies de combat complètes
- Générer des rapports d'analyse de balance
"""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
def _call_holysheep(self, prompt: str, model: str = "deepseek-v3") -> str:
"""Appel interne à l'API HolySheep"""
import requests
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.7,
"max_tokens": 1500
}
response = requests.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
raise Exception(f"Erreur API: {response.status_code}")
def suggest_action(self, character: Character, enemies: List[Character],
allies: List[Character]) -> Dict:
"""
Demande à l'IA de suggérer la meilleure action pour un personnage.
Returns:
Dict avec 'action', 'target', 'reasoning', 'expected_outcome'
"""
enemies_info = ", ".join([
f"{e.name} ({e.hp}/{e.max_hp} HP, CA {e.armor_class})"
for e in enemies if e.is_alive()
])
allies_info = ", ".join([
f"{a.name} ({a.hp}/{a.max_hp} HP)"
for a in allies if a.is_alive()
])
prompt = f"""
Tu es un stratège de combat D&D 5e expert. Analyse la situation suivante et suggère LA MEILLEURE action tactique.
Personnage actuel: {character.name}
Classe: {character.char_class.value} Niveau {character.level}
HP: {character.hp}/{character.max_hp}
Stats: FOR {character.strength}, DEX {character.dexterity}, INT {character.intelligence}, CON {character.constitution}
Classe d'armure: {character.armor_class}
Ennemis en vue: {enemies_info}
Alliés présents: {allies_info}
Réponds UNIQUEMENT au format JSON suivant (sans markdown):
{{
"action": "Attaque/Sort/Défense/Soin/Évitement",
"nom_action": "Nom spécifique de l'action ou du sort",
"cible": "Nom de la cible ou 'soi-même' ou 'aucune'",
"raisonnement": "Explication courte de 2-3 phrases",
"esperance_gagne": "0-100%"
}}
"""
try:
response = self._call_holysheep(prompt)
# Parser la réponse JSON
return json.loads(response)
except:
return {
"action": "Attaque",
"nom_action": "Attaque de base",
"cible": enemies[0].name if enemies else "aucune",
"raisonnement": "Action par défaut (erreur de parsing IA)",
"esperance_gagne": "50%"
}
def analyze_balance(self, scenario: str, iterations: int = 100) -> Dict:
"""
Analyse l'équilibre d'un scénario de combat via IA.
Args:
scenario: Description du scénario à tester
iterations: Nombre de simulations à analyser
Returns:
Rapport d'analyse de balance
"""
prompt = f"""
En tant qu'expert en game design D&D 5e, analyse l'équilibre du scénario suivant:
SCÉNARIO: {scenario}
Considérant {iterations} simulations potentielles:
- Les dégâts sont-ils appropriés au niveau?
- Y a-t-il des combos trop puissants?
- Les classes sont-elles équilibrées entre elles?
- Quels ajustements recommanderais-tu?
Réponds au format JSON:
{{
"equilibre_global": "Équilibré/Tourdisé/Imbéquilibré",
"score_equilibre": 1-10,
"problemes_identifies": ["problème 1", "problème 2"],
"recommandations": ["ajustement 1", "ajustement 2"],
"classes_problematiques": ["liste des classes"],
"suggestion_de_modification": "Description détaillée"
}}
"""
response = self._call_holysheep(prompt)
return json.loads(response)
============================================
EXEMPLE D'UTILISATION
============================================
Initialisation de l'IA de combat
combat_ai = AICombatAdvisor(api_key="YOUR_HOLYSHEEP_API_KEY")
Scénario de test : Guerrier vs Mage
print("=== Analyse de Balance: Guerrier Nv5 vs Mage Nv5 ===\n")
balance_report = combat_ai.analyze_balance(
scenario="Un Guerrier (FOR 18, CA 18, 52 HP) affronte un Mage "
"(INT 18, CA 13, 27 HP) en combat 1v1. Le mage a accès aux "
"sorts de niveau 1-3 incluant Boule de feu (8d6).",
iterations=100
)
print(f"📊 Équilibre Global: {balance_report['equilibre_global']}")
print(f" Score: {balance_report['score_equilibre']}/10\n")
print("⚠️ Problèmes identifiés:")
for pb in balance_report['problemes_identifies']:
print(f" - {pb}")
print("\n💡 Recommandations:")
for rec in balance_report['recommandations']:
print(f" → {rec}")
print(f"\n🔧 Suggestion: {balance_report['suggestion_de_modification']}")
第四步:大规模战斗模拟与统计分析
Pour tester efficacement la balance, nous devons exécuter des centaines de simulations et analyser les résultats statistiquement. Voici le système de simulation massife.
import numpy as np
import pandas as pd
from collections import defaultdict
import time
from concurrent.futures import ThreadPoolExecutor
import matplotlib.pyplot as plt
class MassCombatSimulator:
"""
Simulateur de combat haute performance utilisant HolySheep API.
Fonctionnalités:
- Simulation parallèle de nombreux combats
- Collecte de métriques détaillées
- Génération de rapports statistiques
- Visualisation des résultats
"""
def __init__(self, api_key: str):
self.api_key = api_key
self.results = []
self.combat_system = CombatSystem(verbose=False)
self.ai_advisor = AICombatAdvisor(api_key)
def simulate_single_combat(self, attacker: Character, defender: Character,
use_ai_tactics: bool = False) -> Dict:
"""
Simule un combat complet 1v1 jusqu'à la mort.
Returns:
Dict avec tous les détails du combat
"""
combat_log = []
turn = 0
max_turns = 50 # Sécurité contre boucle infinie
# Sauvegarde des HP originaux
attacker_start_hp = attacker.hp
defender_start_hp = defender.hp
# Réinitialisation
attacker.hp = attacker.max_hp
defender.hp = defender.max_hp
while attacker.is_alive() and defender.is_alive() and turn < max_turns:
turn += 1
# Tour de l'attaquant
if use_ai_tactics:
decision = self.ai_advisor.suggest_action(
attacker, [defender], []
)
# Exécuter l'action suggérée (simplifié)
if "Boule de feu" in decision.get("nom_action", ""):
dmg = self._cast_fireball(attacker)
actual_dmg = defender.take_damage(dmg)
else:
dmg_result = self.combat_system.calculate_attack_damage(
attacker, defender,
attack_bonus=7, # Bonus d'attaque au niveau 5
damage_dice="2d6+4" if attacker.strength > 14 else "1d8+3",
damage_modifier_stat="strength"
)
actual_dmg = dmg_result["damage"] if dmg_result["hits"] else 0
else:
# Combat standard (règles codées)
dmg_result = self.combat_system.calculate_attack_damage(
attacker, defender,
attack_bonus=7,
damage_dice="2d6+4",
damage_modifier_stat="strength"
)
actual_dmg = dmg_result["damage"] if dmg_result["hits"] else 0
if defender.is_alive() and actual_dmg > 0:
combat_log.append(f"Tour {turn}: {attacker.name} inflige {actual_dmg} dmg")
# Tour du défenseur (s'il est vivant)
if defender.is_alive():
if defender.char_class == CharacterClass.MAGE:
# Mage lance un sort
dmg = self._cast_fireball(defender) if turn % 3 == 0 else self._magic_missile(defender)
else:
dmg_result = self.combat_system.calculate_attack_damage(
defender, attacker,
attack_bonus=5,
damage_dice="1d8+3",
damage_modifier_stat="strength"
)
dmg = dmg_result["damage"] if dmg_result["hits"] else 0
if attacker.is_alive() and dmg > 0:
attacker.take_damage(dmg)
combat_log.append(f"Tour {turn}: {defender.name} inflige {dmg} dmg")
winner = attacker.name if attacker.is_alive() else defender.name
return {
"winner": winner,
"turns": turn,
"attacker_remaining_hp": attacker.hp,
"defender_remaining_hp": defender.hp,
"attacker_dealt": attacker_start_hp - defender.hp if winner == attacker.name else defender_start_hp,
"defender_dealt": defender_start_hp - attacker.hp if winner == defender.name else attacker_start_hp,
"combat_log": combat_log
}
def _cast_fireball(self, caster: Character) -> int:
"""Simule un sort de Boule de feu (8d6 dégâts)"""
int_mod = caster.get_modifier(caster.intelligence)
rolls = [random.randint(1, 6) for _ in range(8)]
return sum(rolls) + int_mod
def _magic_missile(self, caster: Character) -> int:
"""Simule un sort de Missile Magique (3d4+3 dégâts)"""
rolls = [random.randint(1, 4) for _ in range(3)]
return sum(rolls) + 3
def run_mass_simulation(self, scenario: str, iterations: int = 100,
use_ai: bool = False) -> pd.DataFrame:
"""
Exécute plusieurs simulations de combat en parallèle.
Args:
scenario: Description du scénario
iterations: Nombre de combats à simuler
use_ai: Si True, utilise les suggestions tactiques de l'IA
Returns:
DataFrame pandas avec tous les résultats
"""
print(f"🚀 Lancement de {iterations} simulations...")
start_time = time.time()
results_list = []
for i in range(iterations):
if i % 50 == 0:
print(f" Progression: {i}/{iterations} ({100*i/iterations:.0f}%)")
# Création des personnages pour ce combat
if "Guerrier" in scenario:
fighter = Character("Fighter", CharacterClass.GUERRIER, 5, 52, 52, 18, 13, 10, 16, 18)
wizard = Character("Wizard", CharacterClass.MAGE, 5, 27, 27, 10, 14, 18, 12, 13)
else:
fighter = Character("Fighter", CharacterClass.GUERRIER, 5, 52, 52, 18, 13, 10, 16, 18)
rogue = Character("Rogue", CharacterClass.VOLEUR, 5, 38, 38, 12, 18, 14, 10, 15)
result = self.simulate_single_combat(fighter, wizard, use_ai)
result["iteration"] = i
results_list.append(result)
elapsed = time.time() - start_time
print(f"✅ Simulations terminées en {elapsed:.2f} secondes")
print(f" Vitesse moyenne: {iterations/elapsed:.1f} combats/seconde")
self.results = results_list
return pd.DataFrame(results_list)
def generate_report(self, df: pd.DataFrame) -> Dict:
"""
Génère un rapport statistique complet.
"""
wins = df["winner"].value_counts()
report = {
"total_combats": len(df),
"victoires_par_classe": wins.to_dict(),
"durée_moyenne": df["turns"].mean(),
"durée_mediane": df["turns"].median(),
"durée_std": df["turns"].std(),
"hp_restant_moyen_vainqueur": df["winner_remaining_hp"].mean() if "winner_remaining_hp" in df.columns else 0,
"taux_equilibre": abs(wins.iloc[0] - wins.iloc[1]) / len(df) * 100 if len(wins) == 2 else 0
}
return report
============================================
EXÉCUTION DES SIMULATIONS
============================================
print("=" * 60)
print(" SIMULATION DE COMBAT D&D - HOLYSHEEP AI")
print("=" * 60)
simulator = MassCombatSimulator(api_key="YOUR_HOLYSHEEP_API_KEY")
Lancer 200 simulations Guerrier vs Mage
results_df = simulator.run_mass_simulation(
scenario="Guerrier Nv5 vs Mage Nv5",
iterations=200,
use_ai=False
)
Générer le rapport
report = simulator.generate_report(results_df)
print("\n" + "=" * 60)
print(" 📊 RAPPORT D'ÉQUILIBRE - GUERRIER VS MAGE")
print("=" * 60)
print(f"\nTotal des combats simulés: {report['total_combats']}")
print(f"\nVictoires:")
for classe, victories in report['victoires_par_classe'].items():
pct = victories / report['total_combats'] * 100
print(f" {classe}: {victories} ({pct:.1f}%)")
print(f"\nDurée moyenne des combats: {report['durée_moyenne']:.1f} tours")
print(f"Écart-type: {report['durée_std']:.1f} tours")
print(f"\nScore d'équilibre: {100 - report['taux_equilibre']:.1f}/100")
Statistiques finales
print("\n" + "=" * 60)
print(" 🏆 STATISTIQUES GLOBALES")
print("=" * 60)
print(f"\nMeilleure performance: Classe dominante identifiée")
print(f"Analyse AI recommandée: Voir section HolySheep API")
结果可视化与平衡性报告
Les données sont excellentes, mais une visualisation claire est essentielle pour présenter vos résultats à une équipe de développement. Créons des graphiques professionnels.
import matplotlib.pyplot as plt
import numpy as np
def generate_visualizations(simulator: MassCombatSimulator, df: pd.DataFrame):
"""
Génère des visualisations professionnelles des résultats de simulation.
"""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('Analyse d\'Équilibre D&D - HolySheep AI Simulation',
fontsize=16, fontweight='bold')
# 1. Graphique en camembert des victoires
ax1 = axes[0, 0]
wins = df['winner'].value_counts()
colors = ['#FF6B6B', '#4ECDC4']
labels = list(wins.index)
sizes = list(wins.values)
wedges, texts, autotexts = ax1.pie(sizes, labels=labels, autopct='%1.1f%%',
colors=colors, explode=(0.05, 0.05),
shadow=True, startangle=90)
ax1.set_title('Répartition des Victoires', fontsize=12, fontweight='bold')
# 2. Distribution de la durée des combats
ax2 = axes[0, 1]
ax2.hist(df['turns'], bins=20, color='#45B7D1', edgecolor='white', alpha=0.8)
ax2.axvline(df['turns'].mean(), color='red', linestyle='--',
linewidth=2, label=f'Moyenne: {df["turns"].mean():.1f}')
ax2.axvline(df['turns'].median(), color='orange', linestyle='--',
linewidth=2, label=f'Médiane: {df["turns"].median():.1f}')
ax2.set_xlabel('Nombre de Tours')
ax2.set_ylabel('Fréquence')
ax2.set_title('Distribution de la Durée des Combats', fontsize=12, fontweight='bold')
ax2.legend()
# 3. HP restants par gagnant
ax3 = axes[1, 0]
winners_data = df[df['winner'] == df['winner'].mode()[0]]
losers_data = df[df['winner'] != df['winner'].mode()[0]]
ax3.boxplot([winners_data['winner_remaining_hp'].dropna() if 'winner_remaining_hp' in df.columns else winners_data['attacker_remaining_hp'],
losers_data['attacker_remaining_hp']],
labels=['Vainqueurs', ' Perdants'])
ax3.set_ylabel('HP Restants')
ax3.set_title('HP Restants après Combat', fontsize=12, fontweight='bold')
# 4. Comparaison des modèles HolySheep (coût/efficacité)
ax4 = axes[1, 1]
models = ['DeepSeek V3.2', 'Gemini 2.5 Flash', 'GPT-4.1', 'Claude Sonnet 4.5']
costs = [0.42, 2.50, 8.00, 15.00]
latencies = [45, 35, 120, 180] # en ms
x = np.arange(len(models))
width = 0.35
bars1 = ax4.bar(x - width/2, [c/15*100 for c in costs], width,
label='Coût ($/1M tokens)', color='#FF6B6B', alpha=0.8)
ax4_twin = ax4.twinx()
bars2 = ax4_twin.bar(x + width/2, latencies, width,
label='Latence (ms)', color='#4ECDC4', alpha=0.8)
ax4.set_ylabel('Coût relatif (%)', color='#FF6B6B')
ax4_twin.set_ylabel('Latence (ms)', color='#4ECDC4')
ax4.set_xticks(x)
ax4.set_xticklabels(models, rotation=15)
ax4.set_title('Comparaison des Modèles IA - HolySheep', fontsize=12, fontweight='bold')
ax4.legend(loc='upper left')
ax4_twin.legend(loc='upper right')
plt.tight_layout()
plt.savefig('combat_balance_report.png', dpi=300, bbox_inches='tight')
print("📊 Graphiques sauvegardés: combat_balance_report.png")
return fig
Génération des visualisations
generate_visualizations(simulator, results_df)
Rapport final
print("\n" + "=" * 60)
print(" 📋 RAPPORT FINAL D'ÉQUILIBRE")
print("=" * 60)
print("""
SCÉN