5) Combat tour par tour
Collections et combat tour par tour
Le jeu prend forme ! Dans cette séance, vous allez créer deux équipes, implémenter une boucle de combat tour par tour et trier les personnages par force grâce à un Comparator.
Objectifs de la séance
- Utiliser
ArrayList<Personnage>pour deux équipes - Créer un
enum TypeAttaquepour catégoriser les attaques - Implémenter une boucle de combat
whilequi s'arrête quand une équipe est vaincue - Trier une liste de personnages par force avec
Comparator
Notions théoriques
enum — type énuméré
Un enum (type énuméré) définit un ensemble fixe de constantes nommées. C'est plus sûr que d'utiliser des String ou des int pour représenter des catégories.
public enum TypeAttaque {
PHYSIQUE,
MAGIQUE,
SOIN
}
Utilisation :
TypeAttaque type = TypeAttaque.PHYSIQUE;
if (type == TypeAttaque.PHYSIQUE) {
System.out.println("Attaque physique !");
}
Contrairement aux String, les enums sont comparés avec == (pas besoin de .equals()). Ils sont aussi utilisables dans des switch, ce qui les rend très pratiques.
Vérifier si une équipe est encore en vie
Pour déterminer si une équipe a encore des combattants avec des PV positifs :
public static boolean equipeEnVie(ArrayList<Personnage> equipe) {
for (Personnage p : equipe) {
if (p.getPointsDeVie() > 0) {
return true;
}
}
return false;
}
Boucle de combat while
while (equipeEnVie(equipeA) && equipeEnVie(equipeB)) {
// tour de l'équipe A
for (Personnage attaquant : equipeA) {
if (attaquant.getPointsDeVie() > 0) {
Personnage cible = trouverCibleVivante(equipeB);
if (cible != null) {
attaquant.attaquer(cible);
}
}
}
// tour de l'équipe B (symétrique)
}
Trier avec Comparator
Comparator.comparingInt() crée un comparateur simple basé sur un entier :
import java.util.Comparator;
// Trier par force croissante
equipeA.sort(Comparator.comparingInt(Personnage::getForce));
// Trier par force décroissante
equipeA.sort(Comparator.comparingInt(Personnage::getForce).reversed());
Personnage::getForce est une référence de méthode. C'est une notation raccourcie pour p -> p.getForce(). Nous les étudierons en détail dans le cours Java Expert.
Exemple pratique
import java.util.ArrayList;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
ArrayList<Personnage> equipeA = new ArrayList<>();
equipeA.add(new Guerrier("Aragorn", 150, 30));
equipeA.add(new Mage("Gandalf", 100, 20, 50));
ArrayList<Personnage> equipeB = new ArrayList<>();
equipeB.add(new Guerrier("Orc Chef", 120, 25));
equipeB.add(new Guerrier("Orc Soldat", 80, 18));
System.out.println("=== Ordre de combat (par force) ===");
equipeA.sort(Comparator.comparingInt(Personnage::getForce).reversed());
for (Personnage p : equipeA) {
p.afficher();
}
System.out.println("\n=== Début du combat ===");
int tour = 1;
while (equipeEnVie(equipeA) && equipeEnVie(equipeB)) {
System.out.println("--- Tour " + tour + " ---");
jouerTour(equipeA, equipeB);
jouerTour(equipeB, equipeA);
tour++;
}
System.out.println("\n=== Résultat ===");
if (equipeEnVie(equipeA)) {
System.out.println("L'équipe A a gagné !");
} else {
System.out.println("L'équipe B a gagné !");
}
}
public static boolean equipeEnVie(ArrayList<Personnage> equipe) {
for (Personnage p : equipe) {
if (p.getPointsDeVie() > 0) {
return true;
}
}
return false;
}
public static void jouerTour(ArrayList<Personnage> attaquants, ArrayList<Personnage> defenseurs) {
for (Personnage attaquant : attaquants) {
if (attaquant.getPointsDeVie() > 0) {
Personnage cible = trouverCibleVivante(defenseurs);
if (cible != null) {
attaquant.attaquer(cible);
}
}
}
}
public static Personnage trouverCibleVivante(ArrayList<Personnage> equipe) {
for (Personnage p : equipe) {
if (p.getPointsDeVie() > 0) {
return p;
}
}
return null;
}
}
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous allez implémenter le combat complet : deux équipes, boucle tour par tour, affichage du vainqueur.
Étape 1 — Créer l'enum TypeAttaque
Créez TypeAttaque.java avec les valeurs PHYSIQUE et MAGIQUE.
En Java, chaque type public (classe, interface, enum) doit être dans son propre fichier. TypeAttaque.java contiendra uniquement l'enum. Cela facilite la navigation dans le projet.
Étape 2 — Implémenter la méthode equipeEnVie()
Créez une méthode statique equipeEnVie(ArrayList<Personnage> equipe) qui retourne true si au moins un personnage a des PV positifs.
Dès qu'on trouve un personnage vivant, on retourne true immédiatement sans continuer à parcourir la liste. C'est plus efficace et plus lisible que d'utiliser un booléen accumulateur.
Étape 3 — La boucle de combat principale
Écrivez la boucle while qui fait jouer alternativement les deux équipes jusqu'à ce que l'une soit vaincue.
Gardez un compteur de tours. Cela permet de détecter les boucles infinies (combat qui ne se termine jamais) et d'afficher le déroulé du combat de façon lisible. En production, ajoutez une limite maximale de tours pour éviter les boucles infinies.
Étape 4 — Trier les équipes par force avant le combat
Avant de lancer le combat, triez equipeA par force décroissante pour que les plus puissants attaquent en premier.
Triez les équipes avant de lancer la boucle de combat, pas pendant. Modifier une liste pendant une boucle for-each provoque une ConcurrentModificationException. Si le tri dynamique est nécessaire, utilisez un index ou une copie de la liste.