18 KiB
18 KiB
In [ ]:
# ---------------------------------------------------------------
# Données du TP — ne pas modifier
# ---------------------------------------------------------------
# Une carte = (nom, puissance, cout)
BOOSTERS = [
[("Gnome bricoleur", 3, 1), ("Yéti des roches", 5, 4), ("Golem de siège", 6, 5)],
[("Écuyer fidèle", 2, 1), ("Loup argenté", 4, 3), ("Chevalier du néant", 5, 5)],
[("Apprenti mage", 3, 2), ("Sorcière du marais", 4, 4), ("Dragon de bronze", 5, 5)],
[("Recrue de la garde", 2, 1), ("Berserker orc", 4, 3), ("Titan de pierre", 9, 7)],
[("Novice de l'ombre", 1, 1), ("Archer elfe", 3, 2), ("Léviathan des abysses", 10, 8)],
[("Sanglier tenace", 2, 2), ("Garde royal", 5, 4), ("Ogre lourdaud", 4, 6)],
[("Voleur agile", 3, 2), ("Prêtresse de la lune", 4, 3), ("Colosse runique", 7, 6)],
[("Gobelin ferrailleur", 2, 1), ("Chaman du totem", 4, 4), ("Hydre des marais", 6, 6)],
[("Éclaireur nain", 3, 2), ("Paladin d'argent", 5, 4), ("Behemoth ancien", 6, 7)],
[("Rat des égouts", 1, 1), ("Duelliste vétéran", 5, 3), ("Ver des sables", 6, 5)],
]
COUT_CHER = 5 # une carte est "chère" à partir de 5 manas
MAX_CHERES = 3 # au plus 3 cartes chères dans un deck jouable
def afficher_deck(deck):
"""Affiche un deck sous forme de tableau."""
for nom, puissance, cout in deck:
marque = " (chère)" if cout >= COUT_CHER else ""
print(f" {nom:<24} puissance {puissance:>2} coût {cout}{marque}")
print(f"{len(BOOSTERS)} boosters de 3 cartes.")
print("Booster 1 :")
afficher_deck(BOOSTERS[0])In [ ]:
# Exercice 1
# Écrire une fonction qui renvoie la somme des puissances des cartes d'un deck.
def puissance_totale(deck):
"""Renvoie la somme des puissances des cartes du deck."""
# À COMPLÉTER
...
# Vérification
test = [("A", 3, 1), ("B", 5, 6), ("C", 2, 2)]
assert puissance_totale(test) == 10
assert puissance_totale([]) == 0
print("Exercice 1 : OK")In [ ]:
# Exercice 2
# a) Compter les cartes chères (coût >= COUT_CHER) d'un deck.
# b) En déduire si le deck est jouable (au plus MAX_CHERES cartes chères).
def nb_cartes_cheres(deck):
"""Renvoie le nombre de cartes coûtant COUT_CHER manas ou plus."""
# À COMPLÉTER
...
def deck_jouable(deck):
"""Renvoie True si le deck respecte la règle de jouabilité."""
# À COMPLÉTER
...
# Vérification
test = [("A", 3, 1), ("B", 5, 6), ("C", 2, 7), ("D", 4, 5), ("E", 1, 8)]
assert nb_cartes_cheres(test) == 4
assert deck_jouable(test) is False
assert deck_jouable(test[:3]) is True
print("Exercice 2 : OK")In [ ]:
# Exercice 3
# Renvoyer la carte de plus grande puissance d'un booster.
# (Ne pas utiliser max() : on veut voir la boucle de recherche du maximum.)
def carte_la_plus_puissante(booster):
"""Renvoie la carte de plus grande puissance du booster."""
# À COMPLÉTER
...
# Vérification
assert carte_la_plus_puissante(BOOSTERS[0]) == ("Golem de siège", 6, 5)
assert carte_la_plus_puissante(BOOSTERS[4]) == ("Léviathan des abysses", 10, 8)
print("Exercice 3 : OK")In [ ]:
# Exercice 4
# Drafter un deck complet : pour chaque booster, prendre la carte la plus puissante.
def draft_naif(boosters):
"""Renvoie le deck obtenu en prenant à chaque fois la carte la plus puissante."""
# À COMPLÉTER
...
deck_naif = draft_naif(BOOSTERS)
afficher_deck(deck_naif)
print()
print("Puissance totale :", puissance_totale(deck_naif))
print("Cartes chères :", nb_cartes_cheres(deck_naif), "( maximum autorisé :", MAX_CHERES, ")")
print("Deck jouable ? :", deck_jouable(deck_naif))In [ ]:
# Exercice 5
# Drafter en respectant le quota de cartes chères.
#
# Indication : garder un compteur du nombre de cartes chères déjà prises.
# Pour chaque booster, construire la liste des cartes autorisées, puis prendre
# la plus puissante de cette liste.
def draft_contraint(boosters):
"""Renvoie le deck obtenu par un glouton qui respecte le quota de cartes chères."""
# À COMPLÉTER
...
deck_glouton = draft_contraint(BOOSTERS)
afficher_deck(deck_glouton)
print()
print("Puissance totale :", puissance_totale(deck_glouton))
print("Deck jouable ? :", deck_jouable(deck_glouton))In [ ]:
# ---------------------------------------------------------------
# Force brute — code fourni
# ---------------------------------------------------------------
from itertools import product
from time import perf_counter
def meilleur_deck_force_brute(boosters):
"""Essaie TOUS les decks possibles et renvoie le meilleur deck jouable."""
meilleur = None
meilleure_puissance = -1
for deck in product(*boosters):
if deck_jouable(deck) and puissance_totale(deck) > meilleure_puissance:
meilleur = list(deck)
meilleure_puissance = puissance_totale(deck)
return meilleur, meilleure_puissance
debut = perf_counter()
deck_optimal, puissance_optimale = meilleur_deck_force_brute(BOOSTERS)
duree = perf_counter() - debut
afficher_deck(deck_optimal)
print()
print("Puissance optimale :", puissance_optimale)
print(f"Calculé en {duree:.2f} s après avoir testé {3 ** len(BOOSTERS)} decks.")
print()
print("Glouton :", puissance_totale(deck_glouton), " | Optimal :", puissance_optimale)In [ ]:
# Ce que rapporte réellement chaque carte chère, comparée à la meilleure carte pas chère du même booster
print(f"{'Booster':<9}{'meilleure pas chère':<27}{'meilleure chère':<29}{'gain'}")
for i, booster in enumerate(BOOSTERS):
pas_cheres = [c for c in booster if c[2] < COUT_CHER]
cheres = [c for c in booster if c[2] >= COUT_CHER]
if cheres == []:
continue
meilleure_pas_chere = carte_la_plus_puissante(pas_cheres)
meilleure_chere = carte_la_plus_puissante(cheres)
gain = meilleure_chere[1] - meilleure_pas_chere[1]
print(f"{i + 1:<9}{meilleure_pas_chere[0] + f' ({meilleure_pas_chere[1]})':<27}"
f"{meilleure_chere[0] + f' ({meilleure_chere[1]})':<29}{gain:+d}")In [ ]:
print(f"{'boosters':<11}{'decks testés':<16}{'temps (s)'}")
for n in range(4, len(BOOSTERS) + 1):
debut = perf_counter()
meilleur_deck_force_brute(BOOSTERS[:n])
duree_n = perf_counter() - debut
print(f"{n:<11}{3 ** n:<16}{duree_n:.3f}")
# Extrapolation à un vrai draft d'Arène (30 boosters), à partir du temps mesuré ci-dessus
vitesse = 3 ** len(BOOSTERS) / duree_n # decks testés par seconde
secondes = 3 ** 30 / vitesse
print()
print(f"Vitesse mesurée : environ {vitesse:.0f} decks testés par seconde.")
print(f"Pour 30 boosters : {3 ** 30} decks à tester,")
print(f"soit environ {secondes / (365 * 24 * 3600):.0f} années de calcul.")