Exercices en POO
Consignes
- Ne pas se faire aider par des IA ou genAI
Série 1
- Définir une classe
Personne
avec les attributsnom
,prenom
etage
. Ajouter une méthodeafficher
qui affiche les informations de la personne. Implémenter les méthodes__eq__
et__str__
.- Créer quelques personnes et les afficher..
- Afficher le résultat du
==
- On souhaite gérer un collection de consoles et jeux rétro avec la POO. Définir les classes
VideoGame
etVideoGameConsole
. Chaque classe propose les propriétés:name
,releaseYear
. La classeVideoGame
a en plus la propriétédeveloper
qui est la compagnie ou le développeur qui a développé le jeu. La classeVideoGameConsole
a en plus la propriétémanufacturer
qui est la compagnie qui a fabriqué la console. Implémenter la méthode__str__
.- Créer une liste
consoles
qui contient les consoles suivantes avec lesname
,manufacturer
etreleaseYear
suivants:- DegaDrive, Dega, 1992.
- Satourne, Dega, 1995.
- Super Nontendo, Nontendo, 1991.
- Nontendo, Nontendo, 1983.
- Ponystation, Pony, 1996.
- Créer une liste
games
qui contient les jeux suivants avec lesname
,developer
etreleaseYear
suivants:- Sanic, Dega, 1991.
- Spodermin, Morvel, 1992.
- Y-Men, Morvel, 1993.
- Nario, Nontendo, 1985.
- Zebda, Nontendo, 1986.
- First Fantasy, Rectangle, 1987.
- Paper Gear, Bonami, 1987.
- A partir de la liste
consoles
, afficher:- La liste des consoles fabriquées par Nontendo.
- La liste des consoles sorties à partir du 1er janvier 1990.
- A partir de la liste
games
, afficher:- La liste des jeux développés par Morvel.
- La liste des jeux sortis à partir du premier janvier 1985.
- Créer une liste
- Définir une classe
Point
qui possède deux propriétés x et y de type correspondant aux coordonnées du point.- Implémenter le constructeur
__init__(self, x, y)
qui permet d'initialiser les coordonnées. - Implémenter la méthode
__str__
qui représente le point de cette façon (ici, x = 2 et y = 3):Point | x : 2 | y : 3
- Implémenter la méthode
translate(self, tx, ty)
qui ajoutetx
àx
etty
ày
. - Implémenter la méthode
distance(self, point)
qui retourne la distance entre le point courant et le point passé en paramètre et retourne cela sous forme d'un point. - Instancier deux points et :
- Afficher la distance entre les deux points.
- Translater le premier point de 2 en x et 3 en y.
- Afficher les deux points.
- Afficher la nouvelle distance entre les deux points.
- Implémenter le constructeur
- Définir une classe
BankAccount
modélisant un compte en banque. La classe possède deux propriétés initialisées dans le constructeur.balance
correspond au solde du compte.managementCost
qui correspond au frais de gestion du compte.- Implémenter le constructeur
__init__(...)
. - Implémenter la méthode
__str__(self)
qui affiche les informations du compte de cette façon:Compte ( solde: 1000€ | frais de gestion: 13€ )
- Implémenter la méthode
debit(self, amount)
qui enlèveamount
au solde du compte uniquement si le solde est suffisant. Elle retourne un booléen qui renvoietrue
si le débit a réussi, sinonfalse
. - Implémenter la méthode
send(self, bankAccount, amount)
qui transfère de l'argent vers un autre compte uniquement si le solde est suffisant. La méthode retourne un booléen calculé de la même façon que la méthodedebit
. - Instancier deux comptes et effectuer des opérations de débit et de transfert. Afficher l'état de réussite de chaque opération et l'état des comptes après chaque opération.
- Implémenter le constructeur
- On souhaite générer des prédictions météorologiques sur une durée de 30 jours et faire des traitements dessus. Définir la classe
WeatherForecast
avec la propriétéday
(entier entre 1 et 30) ettemperature
(entier qui représente la température en Celsius).- Implémenter le constructeur
- Implémenter la méthode
to_farhenheit(self)
qui retourne la température en Fahrenheit avec la formule suivante:F = C * 9/5 + 32
- Implémenter la méthode
__str__
qui affiche la prévision de cette façon:Jour 1: 20°C | 68°F
- Instancier une liste de de 30 prévisions pour chacun des jours avec des températures aléatoires sous forme d'entier entre -10 et 40 degrés.
- Calculer les résultats suivants sans utiliser
avg
,min
,max
.- La température moyenne sur les 30 jours.
- La température la plus faible ainsi que les jours où cette température a été enregistrée. Par exemple si la température la plus faible est -10, on doit afficher les jours où -10 a été enregistré.
- La température la plus élevée ainsi que les jours où cette température a été enregistrée.
- Afficher les prévisions triées par température croissante avec et sans utiliser les méthodes de tri prédéfinies.
- Afficher les prévisions triées par température décroissante avec et sans utiliser les méthodes de tri prédéfinies.
- Le nombre de jours où la température est supérieure à 20°C et le nombre de jours où la température est inférieure à 0°C.
- Un dictionnaire qui compter le nombre prévisions qui ont les mêmes températures. Par exemple si 5 jours ont une température de
20°C
, et les reste des jours ont-5°C
le dictionnaire sera{"20°C": 5, "-5°C": 25}
.
- Générateur de personnage RPG et un simulateur de combat.
- Définir la classe
Character
avec les attributsname
,level
,attack
ethealth
. Implémenter une méthodeattack(self, target)
(où target est unCharacter
) qui diminue la santé de la cible. Ajouter une méthodeis_alive(self)
qui retourneTrue
si le personnage est vivant,False
sinon. Implémenter le constructeur qui initialise les attributsname
,level
,attack
(entier aléatoire entre 1 et 5) ethealth
(entier aléatoire entre 10 et 20). Implémenter une méthodeheal(self)
qui rétablithealth
de5 + level // 10
(// est la division entière). - Définir la fonction
fight(character1, character2)
qui simule un combat entre deux personnages. Le combat se déroule de la façon suivante:- Tant que les deux personnages sont vivants, ils s'attaquent à tour de rôle.
- Le personnage 1 attaque le personnage 2, puis le personnage 2 attaque le personnage 1.
- L'attaque de p1 envers p2 diminue le
health
de p2 avec la formule suivantep2.health - p1.attack * (réel aléatoire entre 0.75 et 1.25)
. - Le déroulement du combat est affiché au fur et à mesure.
health
doit toujours être >= 0
- Définir la fonction
fight_alternative(character1, character2)
qui reprend le même algo quefight
, mais permet au joueur actuel d'appeler sa méthodeheal
en plus d'attaquer. La méthodeheal
est appelée si un nombre aléatoire en 50 et 100 généré au moment où son tour débute est strictement inférieur à son(numéro du tour * level) / health
. Par exemple, si le joueur 1 a un niveau de 5 et 2 points de vie et qu'au tour 30, le nombre aléatoire est 60, alors le joueur 1 peut se soigner car(30 * 5) / 2 = 75 est supérieur à 60
. Par contre, si le nombre aléatoire généré était de 75 ou plus, alors le joueur ne peut pas se soigner.
- Définir la classe
- Définir une classe
Book
avec les propriétés suivantes Python:nb_pages: number,
title: string,
author: string,
isbn: string
(ISBN (The International Standard Book Number)) etmarked_page: number
.- Dans la classe
Book
, définir une méthodemark_page(self, page)
qui permet de mettre à jour la valeur demarked_page
avec la valeur de l'argumentpage
. ⚠ bien vérifier quepage
soit < ànb_pages
. - Créer une classe
Library
(Bibliothèque) qui contient un tableau de livres qui est passé au constructeur. ⚠ Il y'a une relation entreLibrary
etBook
mais ce n'est pas une relation d'héritage. Pour ce cas, on dit que c'est une agrégation. - Dans la classe
Library
, définir une méthodelist_authors()
qui retourne un tableau contenant uniquement les noms des auteurs. - Dans la classe
Library
, définir une méthodesum_of_marked_pages()
qui retourne la somme desmarked_page
de tous les livres. - Instancier une
Library
avec trois livres - Afficher les résultats des appels des méthodes
list_authors()
etsum_of_marked_pages()
. - Est-ce que la classe
Book
peuvent exister et être utilisée indépendamment deLibrary
?
- Dans la classe
Solution Person
class Person:
def __init__(self, first_name: str, last_name: str, age: int):
self.first_name = first_name
self.last_name = last_name
self.age = age
def __str__(self):
return f"Person(name: {self.first_name} {self.last_name}, age: {self.age})"
def __eq__(self, other: object) -> bool:
return isinstance(other, Person) and self.first_name == other.first_name \
and self.last_name == other.last_name and self.age == other.age
p1 = Person("Ram", "Bo", 40)
p2 = Person("Ryuuji", "Itadori", 17)
p3 = Person("Ryuuji", "Itadori", 17)
print(p1)
print(p2)
print(p3)
print(p1 == 10)
print(p1 == p2)
print(p2 == p3)
Solution Retro games and consoles
class VideoGameConsole:
def __init__(self, name: str, manufacturer: str, release_year: int):
self.name = name
self.release_year = release_year
self.manufacturer = manufacturer
def __str__(self):
return f"The {self.name} was release in {self.release_year} and manufactured by {self.manufacturer}"
def __repr__(self):
return self.__str__()
class VideoGame:
def __init__(self, name: str, developer: str, release_year: int):
self.name = name
self.release_year = release_year
self.developer = developer
def __str__(self):
return f"The game {self.name} was release in {self.release_year} and was developed by {self.developer}"
def __repr__(self):
return self.__str__()
consoles: list[VideoGameConsole] = [
VideoGameConsole("DegaDrive", "Dega", 1992),
VideoGameConsole("Satourne", "Dega", 1995),
VideoGameConsole("Super Nontendo", "Nontendo", 1991),
VideoGameConsole("Nontendo", "Nontendo", 1983),
VideoGameConsole("Ponystation", "Pony", 1996)
]
games: list[VideoGame] = [
VideoGame("Sanic", "Dega", 1991),
VideoGame("Spodermin", "Morvel", 1992),
VideoGame("Y-Men", "Morvel", 1993),
VideoGame("Nario", "Nontendo", 1985),
VideoGame("Zebda", "Nontendo", 1986),
VideoGame("First Fantasy", "Rectangle", 1987),
VideoGame("Paper Gear", "Bonami", 1987)
]
nontendo_consoles: list[VideoGameConsole] = []
ninety_consoles : list[VideoGameConsole] = []
for console in consoles:
if console.manufacturer == "Nontendo":
nontendo_consoles.append(console)
if console.release_year >= 1990:
ninety_consoles.append(console)
print("La liste des consoles fabriquées par Nontendo", nontendo_consoles)
# Syntaxe de la Compréhension
print("La liste des consoles fabriquées par Nontendo méthode 2", [c for c in consoles if c.manufacturer == "Nontendo"])
print("La liste des consoles >= 1990", ninety_consoles)
print("La liste des consoles >= 1990 méthode 2", [c for c in consoles if c.release_year >= 1990])
morvel_games: list[VideoGame] = []
eighty_five_consoles : list[VideoGame] = []
for game in games:
if game.developer == "Morvel":
morvel_games.append(game)
if game.release_year >= 1985:
eighty_five_consoles.append(game)
print("La liste des jeux développés par Morvel", morvel_games)
print("La liste des jeux développés par Morvel méthode 2", [g for g in games if g.developer == "Morvel"])
print("La liste des jeux >= 1990", eighty_five_consoles)
print("La liste des jeux >= 1990 méthode 2", [g for g in games if g.release_year >= 1985])
Série 2
- Donner deux exemples de relation d'héritage à un niveau.
- Par exemple l'étudiant est une personne.
- Donner deux exemples de relation d'héritage à deux niveaux.
- Par exemple : un téléphone est un appareil électronique, un appareil électronique est un appareil.
- Trouver les relations d'héritage possibles pour chacun de ses groupes de classes (traiter chaque ligne indépendamment):
Être vivant
,Homme
,Animal
Meuble
,Fauteuil
,Armoire
,Table
.Aliment
,Pâtisserie
,Mille-Feuilles
,Croissant
,Pain
,Fruit
,Pomme
,Banane
- Aucun code python n'est demandé.
- Donner une classe parente pour chaque groupe de classes (traiter chaque ligne indépendamment):
- Ordinateur portable, ordinateur fixe
- Ordinateur portable, ordinateur fixe, Switch, Xbox, Playstation
- Voiture, Camion, Vélo, Trottinette
- Un fermier élève des animaux et cultive des arbres fruitiers. Il a un nom et peut arroser. Chaque animal a une date de naissance et un état de santé. Chaque Arbre fruitier a une hauteur Le fermier a deux vaches, 4 poules, un pommier et deux poiriers. Définir les classes en UML et les coder en Python.
- Un garage répare des véhicules et vend des pièces. Il répare des voitures, des camions, des vélos et des trottinettes. Il vend des pneus, des moteurs, des batteries et des phares. Définir les classes en UML et les coder en Python.
- Répondre aux questions suivantes :
- L'héritage correspondant à une relation être ou avoir ?
- Donner deux avantages de l'héritage.
- Est-ce que
t
est un objet, classe ou une instancet = Shell()
? - Que représente
super()
en Python ?
Solutions
- Exemples de relation d'héritage à un niveau :
Personne
hérite deÊtre vivant
.Homme
hérite dePersonne
.
- Exemples de relation d'héritage à deux niveaux :
Téléphone
hérite deAppareil électronique
qui hérite deAppareil
.Véhicule
hérite deMoyen de transport
qui hérite deObjet mouvant
.
- Relations d'héritage possibles :
Être vivant
est une classe parente deHomme
etAnimal
.Meuble
est une classe parente deFauteuil
,Armoire
etTable
.Aliment
est une classe parente dePâtisserie
,Fruit
etPain
.
- Classe parente pour chaque groupe de classes :
Ordinateur
est une classe parente deOrdinateur portable
etOrdinateur fixe
.Appareil électronique
est une classe parente deOrdinateur portable
,Ordinateur fixe
,Switch
,Xbox
etPlaystation
.Objet mouvant
est une classe parente deVoiture
,Camion
,Vélo
etTrottinette
.
- Questions
- L'héritage correspondant à une relation être.
- Deux avantages de l'héritage:
- Réutilisation du code.
- Factorisation du code.
t
est une instance de la classeShell
ou un objet de typeShell
.super()
permet d'appeler une méthode de la classe parente.
Fermier
class Animal:
def __init__(self, date_naissance, etat_sante):
"""etat_stante est égal à 0 si tout va bien et 1 si l'animal est malade"""
self.date_naissance = date_naissance
self.etat_sante = etat_sante
def __repr__(self):
return f"({self.__class__.__name__}: {self.date_naissance})"
class Vache(Animal):
pass
class Poule(Animal):
pass
class ArbreFruitier:
def __init__(self, hauteur):
self.hauteur = hauteur
def __repr__(self):
return f"({self.__class__.__name__}: {self.hauteur})"
class Pommier(ArbreFruitier):
pass
class Poirier(ArbreFruitier):
pass
class Fermier:
def __init__(self, nom, animaux = [], arbres_fruitiers = []):
self.nom = nom
self.animaux = animaux
self.arbres_fruitiers = arbres_fruitiers
def arroser(self):
print("💦🌱")
def __str__(self):
return f"{self.nom}. Animaux: {self.animaux}. Fruits: {self.arbres_fruitiers}"
animaux = [
Vache("2024-01-10", 0),
Vache("2023-05-11", 1),
Poule("2001-06-02", 0),
Poule("2020-06-24", 0),
Poule("2023-05-11", 0),
Poule("2023-05-11", 0),
]
arbres_fruitiers = [
Pommier(10),
Poirier(50),
Poirier(25)
]
fermier = Fermier("Fave", animaux, arbres_fruitiers)
print(fermier)
Série 3
- Définir la classe
StringUtils
qui contient les méthodes statiques suivantes:is_palindrome(word: str) -> bool
qui retourneTrue
si le mot est un palindrome,False
sinoncount_vowels(word: str) -> int
qui retourne le nombre de voyelles dans le motcount_uppercase(word: str) -> int
qui retourne le nombre de majuscules dans le motcount_lowercase(word: str) -> int
qui retourne le nombre de minuscules dans le mot- Appeler les différentes méthodes de cette classe
- Définir la classe
TaxUtils
qui contient les méthodes statiques suivantes:compute_ttc(price_ht: float, vat_rate: float) -> float
qui retourne le montant TTCcompute_tva(price: float, vat_rate: float) -> float
qui retourne le montant de la TVA- Appeler les différentes méthodes de cette classe
- Définir la classe abstraite
Shape
qui contient les méthodes abstraites suivantes:area() -> float
qui retourne l'aire de la formeperimeter() -> float
qui retourne le périmètre de la forme- Définir les classes
Rectangle
,Circle
etTriangle
qui héritent deShape
et implémentent les méthodes abstraites.
Solutions
StringUtils.py
class StringUtils:
@staticmethod
def is_palindrome_method_1(s: str) -> bool:
return s == s[::-1]
@staticmethod
def is_palindrome_method_2(s: str) -> bool:
for i in range(len(s) // 2):
if s[i] != s[-i-1]:
return False
return True
@staticmethod
def count_vowels_method_1(s: str) -> int:
count = 0
for c in s.lower():
if c == "a" or c == "e" or c == "i" or c == "o" or c == "u" or c == "y":
count += 1
return count
@staticmethod
def count_vowels_method_2(s: str) -> int:
count = 0
for c in s.lower():
if c in "aeiouy":
count += 1
return count
@staticmethod
def count_vowels_method_3(s: str) -> int:
return len([c for c in s.lower() if c in "aeiouy"])
@staticmethod
def count_uppercase_method_1(s: str) -> int:
count = 0
upper_s = s.upper()
for i in range(len(s)):
# sans le deuxième test (le grand and), la méthode n'est très efficace car elle ne gère pes les string qui contiennt autre chose que des lettres
if s[i] == upper_s[i] and ("A" <= s[i] <= "Z" or "a" <= s[i] <= "z"):
count += 1 # équivalent à count = count + 1
return count
@staticmethod
def count_uppercase_method_2(s: str) -> int:
count = 0
for i in range(len(s)):
if s[i] in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
count += 1 # équivalent à count = count + 1
return count
@staticmethod
def count_uppercase_method_3(s: str) -> int:
count = 0
for i in range(len(s)):
# ordre du dictionnaire de Français
if "A" <= s[i] <= "Z":
count += 1 # équivalent à count = count + 1
return count
print(StringUtils.is_palindrome_method_1("Hello"))
print(StringUtils.is_palindrome_method_1("kayak"))
print(StringUtils.is_palindrome_method_2("Hello"))
print(StringUtils.is_palindrome_method_2("kayak"))
print(StringUtils.count_vowels_method_1("Python"),
StringUtils.count_vowels_method_2("Python"),
StringUtils.count_vowels_method_3("Python"))
print(StringUtils.count_uppercase_method_1("I Love Python"),
StringUtils.count_uppercase_method_2("I Love Python"),
StringUtils.count_uppercase_method_3("I Love Python"))
print("slicing")
numbers = [2, 5, -1, 6, 9, 11, 21]
print(numbers[::], numbers[::2], numbers[::-1], numbers[::-3])
print(numbers[1:5],numbers[1:], numbers[:4], numbers[:-2])
print(numbers[1:5:2], numbers[1:5:-1], numbers[5:1:-1], numbers[-2:0:-2])
GeometricShapes.py
from abc import ABC, abstractmethod
import math
class Shape(ABC):
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimeter(self) -> float:
pass
class Rectangle(Shape):
def __init__(self, length: float, width: float):
super().__init__()
self.lenght = length
self.width = width
def area(self) -> float:
return self.width * self.lenght
def perimeter(self) -> float:
return (self.width + self.lenght) * 2
class Circle(Shape):
def __init__(self, radius: float):
super().__init__()
self.radius = radius
def area(self) -> float:
return (self.radius ** 2) * math.pi
def perimeter(self) -> float:
return self.radius * 2 * math.pi
class Triangle(Shape):
"""On suppose qu'on donne une base et une hauteur ainsi que les deux autres côté.
A faire en exercice: se baser uniquement sur les côtés"""
def __init__(self, b: float, h: float, c2: float, c3: float):
super().__init__()
self.b = b
self.h = h
self.c2 = c2
self.c3 = c3
def perimeter(self) -> float:
return self.b + self.c2 + self.c3
def area(self) -> float:
return (self.b * self.h) / 2
shapes = (Rectangle(10, 2.5), Rectangle(9, 3), Circle(9), Circle(1.8), Triangle(10, 10, 10, 14.142))
for shape in shapes:
print("area", shape.area(), "perimeter", shape.perimeter())
Série 4
- Nous souhaitons définir les classes d'un futur jeu MOBA qui va cartonner sévère. Le jeu sera en 2D en vue du dessus. Dans ce jeu nous aurons des héros qui affrontent des monstres.
- Les héros et les monstres ont tous des HP (points de vie), des MP (points de magie), un nom, une position dans la carte et une hitbox (rectangle de détection des dégâts).
- Un héro peut être soit un tank, soit un mage, soit un soigneur ou soit un guerrier. Chaque héro a un niveau et une compétence propre (le héros fait 10 dégâts, le mage fait 20 dégâts et perd 5 mp, le tank donne 2 de ses hp et le soigneur fait gagner 30 hp et perd 8 mp). Tous les héros commencent au niveau 1 avec des valeurs de HP et MP aléatoires compris entre 100 et 200 et entre 50 et 100 respectivement. Les guerriers commencent avec 0 MP.
- Un monstre peut être soit un minion, soit un buldozer. Les monstres ont 0 MP et ont des hp aléatoires compris entre 30 et 70.
- Représenter les différentes classes en UML et les coder en Python.
Solution moba
import random
from abc import abstractmethod, ABC
class Point:
def __init__(self, x, y) -> None:
self.x = x
self.y = y
class Rectangle:
def __init__(self, p: Point, width: int, height: int) -> None:
self.p = p
self.width = width
self.height = height
class Character:
def __init__(self, name, hp, mp, position: Point, hitbox: Rectangle) -> None:
self.name = name
self.hp = hp
self.mp = mp
self.position = position
self.hitbox = hitbox
def __str__(self) -> str:
""" " __nom__ -> dunder (jargon python)"""
return (
f"{self.__class__.__name__} (name={self.name}, hp={self.hp}, mp={self.mp})"
)
def __repr__(self):
"""print d'une liste appelle cette méthode au lieu de __str__"""
return self.__str__()
class Hero(Character, ABC):
def __init__(self, name, position, hitbox) -> None:
hp = random.randint(100, 200)
mp = random.randint(50, 100)
super().__init__(name, hp, mp, position, hitbox)
self.level = 1
def attack(self, monster):
monster.hp -= 2
print(self, "inflige 2 de dégâts. Nouvel état du monstre", monster)
@abstractmethod
def perform_skill(self, character):
print(
self,
"a appliqué une compétence a un personnage dont le nouvel état est:",
character,
)
class Warrior(Hero):
def __init__(self, name, position, hitbox) -> None:
super().__init__(name, position, hitbox)
self.mp = 0
def perform_skill(self, character):
character.hp = max(character.hp - 10, 0)
super().perform_skill(self, character)
class Mage(Hero):
def perform_skill(self, character):
if self.mp < 5:
return
character.hp = max(character.hp - 20, 0)
self.mp -= 5
super().perform_skill(character)
class Tank(Hero):
def perform_skill(self, character):
if self.hp < 2:
return
character.hp += 2
self.hp -= 2
super().perform_skill(character)
class Healer(Hero):
def perform_skill(self, character):
if self.mp < 8:
return
character.hp += 30
self.mp -= 8
super().perform_skill(character)
class Monster(Character):
def __init__(self, name, position, hitbox) -> None:
super().__init__(name, random.randint(30, 70), 0, position, hitbox)
def attack(self, hero):
hero.hp -= 1
print(self, "inflige 1 de dégât. Nouvel état du héro", hero)
class Minion(Monster):
pass
class Buldozer(Monster):
pass
mage = Mage(
"Ryze",
Point(10, 10),
Rectangle(Point(0, 0), 100, 100),
)
warrior = Warrior(
"Gerran",
Point(50, 100),
Rectangle(Point(0, 0), 100, 100),
)
healer = Healer(
"Sorakan",
Point(50, 50),
Rectangle(Point(0, 0), 100, 100),
)
tanky = Tank(
"tanky",
Point(70, 50),
Rectangle(Point(0, 0), 100, 100),
)
minion1 = Minion(
"m1",
Point(200, 500),
Rectangle(Point(0, 0), 100, 100),
)
minion2 = Minion(
"m2",
Point(200, 500),
Rectangle(Point(0, 0), 100, 100),
)
b1 = Buldozer(
"b1",
Point(200, 500),
Rectangle(Point(0, 0), 100, 100),
)
heroes = [mage, healer, warrior, tanky]
monsters = [minion1, minion2, b1]
print("héros", heroes)
print("monstres", monsters)
warrior.attack(b1)
mage.perform_skill(minion1)
healer.perform_skill(mage)