Aller au contenu principal

5) Une boucle de combat

Objectifs de la séance

  • Organiser deux équipes avec List<Personnage>
  • Utiliser un enum TypeAttaque pour 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);
info

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


Quelle méthode LINQ trie une collection par ordre décroissant ?


Quel `using` est nécessaire pour utiliser les méthodes LINQ en .NET 5 et antérieur ?


Que retourne `equipe.Where(p => p.PointsDeVie > 0)` ?


Quel avantage offre un `record ResultatCombat(string Vainqueur, int ToursJoues)` par rapport à une classe ordinaire ?


Quelle expression LINQ retourne le personnage avec la force maximale ?


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).


Bonne pratique - Math.Max pour les planchers

PointsDeVie = 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.


Bonne pratique - LINQ ne modifie pas la liste d'origine

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.


Bonne pratique - Travailler sur des copies

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

NotionRésumé
enum TypeAttaqueQualifie 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.