5) Une boucle de combat
Objectifs de la séance
- Organiser deux équipes avec
List<Personnage> - Utiliser un
enum TypeAttaquepour qualifier les dégâts - Implémenter une boucle de combat
while - Trier les personnages avec LINQ (
OrderByDescending) - Encapsuler le résultat d'un combat dans un
record
Notions théoriques
enum pour qualifier les actions
Un enum rend le code plus lisible que des constantes entières ou des chaînes magiques.
enum TypeAttaque { Physique, Magique, Distance }
void AppliquerDegats(Personnage cible, int degats, TypeAttaque type)
{
Console.WriteLine($"Attaque {type} : {cible.Nom} perd {degats} PV");
}
Boucle de combat while
// Le combat continue tant que les deux équipes ont des membres vivants
while (equipe1.Count > 0 && equipe2.Count > 0)
{
// Le premier de chaque équipe attaque l'autre
Personnage attaquant = equipe1[0];
Personnage defenseur = equipe2[0];
attaquant.Attaquer(defenseur);
defenseur.PointsDeVie -= attaquant.Force; // ou via une méthode
if (defenseur.PointsDeVie <= 0)
{
Console.WriteLine($"{defenseur.Nom} est vaincu !");
equipe2.Remove(defenseur);
}
tours++;
}
LINQ pour trier et filtrer
LINQ (Language INtegrated Query) permet de manipuler des collections avec une syntaxe expressive.
using System.Linq;
List<Personnage> equipe = new() { /* ... */ };
// Trier par force décroissante
List<Personnage> parForce = equipe.OrderByDescending(p => p.Force).ToList();
// Filtrer les personnages vivants
List<Personnage> vivants = equipe.Where(p => p.PointsDeVie > 0).ToList();
// Trouver le plus fort
Personnage? plusFort = equipe.MaxBy(p => p.Force);
LINQ s'applique à toute collection implémentant IEnumerable<T>. Les méthodes LINQ sont des extensions définies dans System.Linq. Ajoutez using System.Linq; en haut du fichier si nécessaire (automatiquement inclus avec les using globaux .NET 6+).
record pour le résultat de combat
record ResultatCombat(string Vainqueur, int ToursJoues);
ResultatCombat resultat = new("Équipe Alpha", 5);
Console.WriteLine(resultat);
// ResultatCombat { Vainqueur = Équipe Alpha, ToursJoues = 5 }
Exemple pratique
using System;
using System.Collections.Generic;
using System.Linq;
enum TypeAttaque { Physique, Magique, Distance }
record ResultatCombat(string Vainqueur, int ToursJoues);
// Classes Personnage, Guerrier, Mage, Archer — reprises des séances précédentes
// (code omis pour la lisibilité, voir séance 4)
static ResultatCombat LancerCombat(List<Personnage> alpha, List<Personnage> beta)
{
int tours = 0;
while (alpha.Count > 0 && beta.Count > 0)
{
tours++;
Personnage att = alpha[0];
Personnage def = beta[0];
att.Attaquer(def);
def.PointsDeVie -= att.Force;
if (def.PointsDeVie <= 0)
{
Console.WriteLine($" → {def.Nom} est éliminé !");
beta.Remove(def);
}
// Les équipes s'alternent
(alpha, beta) = (beta, alpha);
}
string vainqueur = alpha.Count > 0 ? "Alpha" : "Beta";
return new ResultatCombat(vainqueur, tours);
}
// Programme
List<Personnage> equipeAlpha = new()
{
new Guerrier("Kael", 100, 15, 8),
new Archer ("Theron", 90, 12, 30),
};
List<Personnage> equipeBeta = new()
{
new Mage("Aria", 80, 10, 50),
new Guerrier("Drak", 110, 14, 6),
};
Console.WriteLine("=== Tri par force (équipe Alpha) ===");
foreach (Personnage p in equipeAlpha.OrderByDescending(p => p.Force))
Console.WriteLine($" {p.Nom} — Force : {p.Force}");
Console.WriteLine("\n=== Combat ===");
ResultatCombat resultat = LancerCombat(equipeAlpha, equipeBeta);
Console.WriteLine($"\n=== Résultat ===");
Console.WriteLine(resultat);
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Étape 1 — Ajouter PointsDeVie modifiable et la méthode EstVivant
Pour le combat, PointsDeVie doit pouvoir diminuer. Ajoutez une méthode EstVivant() et une méthode RecevoirDegats(int degats).
Math.Max pour les planchersPointsDeVie = Math.Max(0, PointsDeVie - degats) garantit que les PV ne passent jamais en négatif. Les PV négatifs compliquent les vérifications (p.PointsDeVie <= 0 ou p.PointsDeVie < 0 ?). En bloquant à 0, EstVivant() retourne toujours false dès que le personnage est éliminé, sans ambiguïté.
Étape 2 — Créer deux équipes et les trier par force
Créez deux listes de personnages, puis affichez chaque équipe triée par force décroissante avec LINQ.
equipeAlpha.OrderByDescending(...) retourne une nouvelle séquence triée. La liste originale equipeAlpha reste dans son ordre initial. Si vous avez besoin de la liste triée de façon persistante, appelez .ToList() pour la matérialiser.
Étape 3 — Implémenter la boucle de combat while
Implémentez une boucle qui fait s'affronter les deux équipes jusqu'à ce que l'une soit éliminée.
Dans l'exemple, on crée new(equipeAlpha) pour travailler sur une copie. Si on supprimait des éléments de la liste originale pendant le combat, l'état du programme deviendrait incohérent et difficile à réinitialiser pour un second combat. Toujours distinguer l'état persistant (les vraies équipes) de l'état temporaire (le combat en cours).
📌 Une solution
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
enum TypeAttaque | Qualifie les types d'attaque avec des noms lisibles. |
List.Remove(element) | Supprime un élément de la liste par référence. |
OrderByDescending(p => p.Force) | Trie par force décroissante (LINQ). |
Where(p => p.EstVivant()) | Filtre les éléments satisfaisant une condition (LINQ). |
MaxBy(p => p.Force) | Retourne l'élément avec la valeur max (LINQ, .NET 6+). |
record ResultatCombat(...) | Type de données immuable avec égalité structurelle et ToString() automatiques. |
Aperçu de la prochaine séance
Dans la prochaine séance, vous allez persister vos personnages en base de données MySQL avec ADO.NET. Vous apprendrez à installer un package NuGet, à ouvrir une connexion, à exécuter des requêtes paramétrées et à r écupérer des données — le tout dans une classe dédiée PersonnageDao.