Files
TermNSI/POO/TP/TP_Pokemon_notebook.ipynb
T

26 KiB

TP : Pokémon Arena - Programmation Orientée Objet

Thème : Classes, attributs, méthodes, interactions entre objets


Contexte

Depuis 1996, Pokémon est l'une des franchises les plus populaires au monde. En 2026, avec la sortie de Pokémon Legends: Z-A, la licence bat tous les records.

Dans ce TP, vous allez créer un simulateur de combat Pokémon en utilisant la Programmation Orientée Objet. Vous modéliserez des Pokémon, des dresseurs et des combats.


Mode d'emploi du notebook

  • Complétez les cellules marquées # À compléter, puis exécutez-les (Maj + Entrée).
  • Après chaque exercice, une cellule Vérification teste votre code. Si un test échoue, lisez la dernière ligne du message d'erreur : pour une AssertionError, elle indique ce qui ne va pas ; une AttributeError signale en général un attribut ou une méthode pas encore écrits.
  • Quand vous modifiez une classe, réexécutez sa cellule avant de relancer les vérifications. En cas de doute : menu Noyau puis Redémarrer et tout exécuter.

Partie 1 : La classe Pokémon

Exercice 1 : Créer la classe de base

Complétez le constructeur et la méthode __repr__ de la classe Pokemon.

Attributs :

  • nom : nom du Pokémon (str)
  • type_pokemon : type élémentaire (str) : "Feu", "Eau", "Plante", "Electrik", etc.
  • niveau : niveau du Pokémon (int, par défaut 1)
  • pv_max : points de vie maximum (int, par défaut 100)
  • pv : points de vie actuels (int, initialisé à pv_max)
  • attaque : puissance d'attaque (int, par défaut 10)

Méthodes :

  • __init__ : constructeur
  • __repr__ : renvoie le Pokémon sous la forme "Pikachu (Electrik) - Nv.25 - 80/100 PV"

Les méthodes de l'exercice 2 sont déjà présentes dans la classe : vous les compléterez ensuite, dans la même cellule.

In [ ]:
class Pokemon:
    def __init__(self, nom, type_pokemon, niveau=1, pv_max=100, attaque=10):
        """
        Constructeur de la classe Pokemon.

        :param nom: (str) Nom du Pokémon
        :param type_pokemon: (str) Type élémentaire
        :param niveau: (int) Niveau du Pokémon
        :param pv_max: (int) Points de vie maximum
        :param attaque: (int) Puissance d'attaque
        """
        # À compléter (exercice 1)
        pass

    def __repr__(self):
        """Représentation textuelle du Pokémon."""
        # À compléter (exercice 1)
        pass

    def est_ko(self):
        """
        Vérifie si le Pokémon est KO.

        :return: (bool) True si PV <= 0, False sinon
        """
        # À compléter (exercice 2)
        pass

    def subir_degats(self, degats):
        """
        Inflige des dégâts au Pokémon.
        Les PV ne peuvent pas descendre en dessous de 0.

        :param degats: (int) Nombre de dégâts subis
        """
        # À compléter (exercice 2)
        pass

    def attaquer(self, cible):
        """
        Attaque un autre Pokémon.
        Affiche un message et inflige des dégâts à la cible.

        :param cible: (Pokemon) Pokémon ciblé par l'attaque
        """
        # À compléter (exercice 2, puis exercice 3 en bonus)
        pass

    def soigner(self, soin):
        """
        Soigne le Pokémon.
        Les PV ne peuvent pas dépasser pv_max.

        :param soin: (int) Nombre de PV récupérés
        """
        # À compléter (exercice 2)
        pass

Vérification de l'exercice 1

Affichage attendu : Pikachu (Electrik) - Nv.25 - 80/80 PV

In [ ]:
pikachu = Pokemon("Pikachu", "Electrik", niveau=25, pv_max=80, attaque=15)

assert pikachu.nom == "Pikachu", "L'attribut nom est incorrect"
assert pikachu.type_pokemon == "Electrik", "L'attribut type_pokemon est incorrect"
assert pikachu.niveau == 25, "L'attribut niveau est incorrect"
assert pikachu.pv_max == 80, "L'attribut pv_max est incorrect"
assert pikachu.pv == 80, "Les PV doivent être initialisés à pv_max"
assert pikachu.attaque == 15, "L'attribut attaque est incorrect"
assert repr(pikachu) == "Pikachu (Electrik) - Nv.25 - 80/80 PV", "__repr__ ne renvoie pas le bon texte"

magicarpe = Pokemon("Magicarpe", "Eau")
assert magicarpe.niveau == 1 and magicarpe.pv_max == 100 and magicarpe.pv == 100 and magicarpe.attaque == 10, \
    "Les valeurs par défaut sont incorrectes"

print(pikachu)
print("Exercice 1 : tous les tests sont passés.")

Exercice 2 : Méthodes de combat

Revenez dans la cellule de la classe Pokemon et complétez les méthodes est_ko, subir_degats, attaquer et soigner, en suivant leur docstring. Réexécutez ensuite la cellule de la classe.

La méthode attaquer inflige à la cible autant de dégâts que la valeur d'attaque du Pokémon, et affiche un message de la forme :

Pikachu attaque Salamèche et inflige 15 dégâts !

Vérification de l'exercice 2

Affichage attendu :

Pikachu attaque Salamèche et inflige 15 dégâts !
Salamèche (Feu) - Nv.20 - 55/70 PV
In [ ]:
pikachu = Pokemon("Pikachu", "Electrik", niveau=25, pv_max=80, attaque=15)
salameche = Pokemon("Salamèche", "Feu", niveau=20, pv_max=70, attaque=12)

pikachu.attaquer(salameche)
print(salameche)

assert salameche.pv == 55, "attaquer doit retirer attaque PV à la cible"
assert pikachu.pv == 80, "attaquer ne doit pas modifier les PV de l'attaquant"
assert salameche.est_ko() is False, "est_ko doit renvoyer False quand il reste des PV"

salameche.subir_degats(100)
assert salameche.pv == 0, "Les PV ne doivent pas descendre en dessous de 0"
assert salameche.est_ko() is True, "est_ko doit renvoyer True quand les PV sont à 0"

salameche.soigner(30)
assert salameche.pv == 30, "soigner doit ajouter les PV indiqués"
salameche.soigner(100)
assert salameche.pv == 70, "Les PV ne doivent pas dépasser pv_max"

print("Exercice 2 : tous les tests sont passés.")

Exercice 3 : Système de faiblesses (Bonus)

En Pokémon, certains types sont plus efficaces contre d'autres :

  • Eau bat Feu (×2 dégâts)
  • Feu bat Plante (×2 dégâts)
  • Plante bat Eau (×2 dégâts)
  • Electrik bat Eau (×2 dégâts)

Exécutez la cellule ci-dessous, qui définit le dictionnaire des faiblesses.

In [ ]:
# Dictionnaire des faiblesses : type_attaquant -> liste des types faibles
FAIBLESSES = {
    "Eau": ["Feu"],
    "Feu": ["Plante"],
    "Plante": ["Eau"],
    "Electrik": ["Eau"]
}

Modifiez ensuite la méthode attaquer de la classe Pokemon : si le type de la cible est faible contre celui de l'attaquant, les dégâts sont doublés et le message C'est super efficace ! s'affiche avant le message d'attaque.

def attaquer(self, cible):
    """
    Attaque un autre Pokémon avec prise en compte des faiblesses.
    """
    degats = self.attaque

    # Vérifier si le type de la cible est faible contre notre type
    if self.type_pokemon in FAIBLESSES:
        if cible.type_pokemon in FAIBLESSES[self.type_pokemon]:
            degats *= 2
            print("C'est super efficace !")

    print(f"{self.nom} attaque {cible.nom} et inflige {degats} dégâts !")
    cible.subir_degats(degats)

Réexécutez la cellule de la classe Pokemon, puis la vérification ci-dessous.

Vérification de l'exercice 3

Affichage attendu :

C'est super efficace !
Carapuce attaque Salamèche et inflige 20 dégâts !
Salamèche attaque Carapuce et inflige 12 dégâts !
In [ ]:
carapuce = Pokemon("Carapuce", "Eau", niveau=15, pv_max=75, attaque=10)
salameche = Pokemon("Salamèche", "Feu", niveau=15, pv_max=70, attaque=12)

carapuce.attaquer(salameche)
assert salameche.pv == 50, "Eau contre Feu : les dégâts doivent être doublés"

salameche.attaquer(carapuce)
assert carapuce.pv == 63, "Feu contre Eau : les dégâts ne doivent pas être doublés"

print("Exercice 3 : tous les tests sont passés.")

Partie 2 : La classe Dresseur

Exercice 4 : Créer la classe Dresseur

Un dresseur possède une équipe de Pokémon (maximum 6).

Attributs :

  • nom : nom du dresseur (str)
  • equipe : liste de Pokémon (list, vide au départ)

Méthodes :

  • __init__ : constructeur
  • ajouter_pokemon(pokemon) : ajoute un Pokémon à l'équipe (max 6)
  • pokemon_disponibles() : renvoie la liste des Pokémon non KO
  • premier_pokemon_dispo() : renvoie le premier Pokémon non KO ou None
  • tous_ko() : renvoie True si tous les Pokémon sont KO
  • __repr__ : renvoie le dresseur et son équipe, un Pokémon par ligne
In [ ]:
class Dresseur:
    def __init__(self, nom):
        """
        Constructeur de la classe Dresseur.

        :param nom: (str) Nom du dresseur
        """
        # À compléter
        pass

    def ajouter_pokemon(self, pokemon):
        """
        Ajoute un Pokémon à l'équipe si possible (max 6).

        :param pokemon: (Pokemon) Pokémon à ajouter
        :return: (bool) True si ajouté, False sinon
        """
        # À compléter
        pass

    def pokemon_disponibles(self):
        """
        Renvoie la liste des Pokémon non KO.

        :return: (list) Liste des Pokémon encore en état de combattre
        """
        # À compléter
        pass

    def premier_pokemon_dispo(self):
        """
        Renvoie le premier Pokémon non KO.

        :return: (Pokemon/None) Premier Pokémon dispo ou None
        """
        # À compléter
        pass

    def tous_ko(self):
        """
        Vérifie si tous les Pokémon sont KO.

        :return: (bool) True si tous KO, False sinon
        """
        # À compléter
        pass

    def __repr__(self):
        """Représentation du dresseur."""
        # À compléter
        pass

Vérification de l'exercice 4

Affichage attendu :

Dresseur Sacha - 2 Pokémon
  - Pikachu (Electrik) - Nv.25 - 80/80 PV
  - Dracaufeu (Feu) - Nv.50 - 150/150 PV
In [ ]:
sacha = Dresseur("Sacha")
assert sacha.nom == "Sacha" and sacha.equipe == [], "Un dresseur commence avec une équipe vide"

assert sacha.ajouter_pokemon(Pokemon("Pikachu", "Electrik", 25, 80, 15)) is True
assert sacha.ajouter_pokemon(Pokemon("Dracaufeu", "Feu", 50, 150, 25)) is True

assert repr(sacha) == "Dresseur Sacha - 2 Pokémon\n  - Pikachu (Electrik) - Nv.25 - 80/80 PV\n  - Dracaufeu (Feu) - Nv.50 - 150/150 PV", \
    "__repr__ ne renvoie pas le bon texte"
print(sacha)
assert sacha.tous_ko() is False

sacha.equipe[0].subir_degats(100)
assert sacha.premier_pokemon_dispo().nom == "Dracaufeu", "Pikachu est KO, le premier disponible est Dracaufeu"
assert len(sacha.pokemon_disponibles()) == 1, "Un seul Pokémon est encore disponible"

sacha.equipe[1].subir_degats(200)
assert sacha.tous_ko() is True, "Tous les Pokémon sont KO"
assert sacha.premier_pokemon_dispo() is None, "Aucun Pokémon disponible : il faut renvoyer None"

for i in range(4):
    sacha.ajouter_pokemon(Pokemon(f"Pokémon {i}", "Normal"))
assert sacha.ajouter_pokemon(Pokemon("Septième", "Normal")) is False, "Une équipe est limitée à 6 Pokémon"
assert len(sacha.equipe) == 6

print("Exercice 4 : tous les tests sont passés.")

Partie 3 : La classe Combat

Exercice 5 : Simulateur de combat

Créez une classe Combat qui simule un affrontement entre deux dresseurs.

Attributs :

  • dresseur1 : premier dresseur (Dresseur)
  • dresseur2 : second dresseur (Dresseur)
  • tour : numéro du tour actuel (int, 0 au départ)

Méthodes :

  • __init__ : constructeur
  • afficher_etat() : affiche le numéro du tour et les Pokémon actifs des deux dresseurs
  • lancer_combat() : lance le combat jusqu'à ce qu'un dresseur n'ait plus de Pokémon
In [ ]:
class Combat:
    def __init__(self, dresseur1, dresseur2):
        """
        Constructeur de la classe Combat.

        :param dresseur1: (Dresseur) Premier dresseur
        :param dresseur2: (Dresseur) Second dresseur
        """
        # À compléter
        pass

    def afficher_etat(self):
        """Affiche l'état actuel du combat."""
        print(f"\n{'='*40}")
        print(f"Tour {self.tour}")
        print(f"{'='*40}")
        # Afficher les Pokémon actifs
        # À compléter
        pass

    def lancer_combat(self):
        """
        Lance le combat tour par tour.
        Chaque tour, les deux Pokémon actifs s'attaquent.
        Quand un Pokémon est KO, le suivant prend sa place.
        Le combat s'arrête quand un dresseur n'a plus de Pokémon.
        """
        print(f"\n*** COMBAT : {self.dresseur1.nom} VS {self.dresseur2.nom} ***")

        while not self.dresseur1.tous_ko() and not self.dresseur2.tous_ko():
            self.tour += 1
            self.afficher_etat()

            # Récupérer les Pokémon actifs
            poke1 = self.dresseur1.premier_pokemon_dispo()
            poke2 = self.dresseur2.premier_pokemon_dispo()

            # Le Pokémon 1 attaque
            # À compléter

            # Vérifier si Pokémon 2 est KO
            # À compléter

            # Le Pokémon 2 attaque (s'il n'est pas KO)
            # À compléter

            # Vérifier si Pokémon 1 est KO
            # À compléter

        # Afficher le vainqueur
        # À compléter
        pass

Vérification de l'exercice 5

Début de l'affichage attendu :

*** COMBAT : Sacha VS Pierre ***

========================================
Tour 1
========================================
Sacha: Pikachu (80/80 PV)
Pierre: Onix (120/120 PV)

Pikachu attaque Onix et inflige 15 dégâts !
Onix attaque Pikachu et inflige 18 dégâts !

========================================
Tour 2
========================================
Sacha: Pikachu (62/80 PV)
Pierre: Onix (105/120 PV)

Pikachu attaque Onix et inflige 15 dégâts !
Onix attaque Pikachu et inflige 18 dégâts !

...

Pikachu est KO !
Sacha envoie Dracaufeu !

...

Tous les Pokémon de Pierre sont KO !
*** SACHA REMPORTE LE COMBAT ! ***

Le combat doit durer 11 tours et se terminer par la victoire de Sacha.

In [ ]:
sacha = Dresseur("Sacha")
sacha.ajouter_pokemon(Pokemon("Pikachu", "Electrik", 25, 80, 15))
sacha.ajouter_pokemon(Pokemon("Dracaufeu", "Feu", 50, 150, 25))
sacha.ajouter_pokemon(Pokemon("Tortank", "Eau", 45, 140, 22))

pierre = Dresseur("Pierre")
pierre.ajouter_pokemon(Pokemon("Onix", "Roche", 30, 120, 18))
pierre.ajouter_pokemon(Pokemon("Racaillou", "Roche", 20, 80, 12))

combat = Combat(sacha, pierre)
combat.lancer_combat()

assert pierre.tous_ko() is True, "Pierre doit avoir perdu tous ses Pokémon"
assert sacha.tous_ko() is False, "Sacha doit avoir encore au moins un Pokémon"
assert combat.tour == 11, "Le nombre de tours n'est pas le bon"

print("\nExercice 5 : tous les tests sont passés.")

Partie 4 : Améliorations

Exercice 6 : Centre Pokémon

Complétez la classe CentrePokemon, qui permet de soigner toute l'équipe d'un dresseur.

In [ ]:
class CentrePokemon:
    def __init__(self, ville):
        """
        :param ville: (str) Ville où se trouve le centre
        """
        self.ville = ville

    def soigner_equipe(self, dresseur):
        """
        Soigne tous les Pokémon d'un dresseur à 100% de leurs PV.

        :param dresseur: (Dresseur) Dresseur dont l'équipe doit être soignée
        """
        # À compléter
        pass

Vérification de l'exercice 6

In [ ]:
sacha = Dresseur("Sacha")
sacha.ajouter_pokemon(Pokemon("Pikachu", "Electrik", 25, 80, 15))
sacha.ajouter_pokemon(Pokemon("Dracaufeu", "Feu", 50, 150, 25))
sacha.equipe[0].subir_degats(100)
sacha.equipe[1].subir_degats(40)

centre = CentrePokemon("Argenta")
centre.soigner_equipe(sacha)
print(sacha)

assert sacha.equipe[0].pv == 80, "Pikachu doit retrouver tous ses PV"
assert sacha.equipe[1].pv == 150, "Dracaufeu doit retrouver tous ses PV"

print("Exercice 6 : tous les tests sont passés.")

Exercice 7 : Capacités spéciales (Pour aller plus loin)

Voici une classe Capacite pour donner des attaques spéciales aux Pokémon.

Modifiez la classe Pokemon pour qu'un Pokémon possède une liste de capacités (max 4) et puisse choisir son attaque. Vous pouvez aussi ajouter des méthodes à la classe Capacite (par exemple une méthode qui décrémente les PP à chaque utilisation).

In [ ]:
class Capacite:
    def __init__(self, nom, puissance, type_capacite, pp_max):
        """
        :param nom: (str) Nom de la capacité
        :param puissance: (int) Puissance de base
        :param type_capacite: (str) Type de la capacité
        :param pp_max: (int) Nombre d'utilisations max
        """
        self.nom = nom
        self.puissance = puissance
        self.type_capacite = type_capacite
        self.pp_max = pp_max
        self.pp = pp_max

Résumé des classes

Classe Attributs Méthodes principales
Pokemon nom, type_pokemon, niveau, pv_max, pv, attaque attaquer(), subir_degats(), est_ko(), soigner()
Dresseur nom, equipe ajouter_pokemon(), premier_pokemon_dispo(), tous_ko()
Combat dresseur1, dresseur2, tour afficher_etat(), lancer_combat()
CentrePokemon ville soigner_equipe()

Code de test final

Toutes vos classes sont prêtes ? Lancez le combat complet.

In [ ]:
# Création des Pokémon
pikachu = Pokemon("Pikachu", "Electrik", 25, 80, 15)
dracaufeu = Pokemon("Dracaufeu", "Feu", 50, 150, 25)
tortank = Pokemon("Tortank", "Eau", 45, 140, 22)
onix = Pokemon("Onix", "Roche", 30, 120, 18)
racaillou = Pokemon("Racaillou", "Roche", 20, 80, 12)

# Création des dresseurs
sacha = Dresseur("Sacha")
sacha.ajouter_pokemon(pikachu)
sacha.ajouter_pokemon(dracaufeu)
sacha.ajouter_pokemon(tortank)

pierre = Dresseur("Pierre")
pierre.ajouter_pokemon(onix)
pierre.ajouter_pokemon(racaillou)

# Lancement du combat
combat = Combat(sacha, pierre)
combat.lancer_combat()

# Soins au Centre Pokémon
centre = CentrePokemon("Argenta")
centre.soigner_equipe(sacha)
print()
print(sacha)

Auteurs : Florian Mathieu, Enzo Frémeaux, Timothée Decoster

Licence CC BY-SA

Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International.