Aller au contenu

Exercices pour s'entraîner

Prenez l'habitude de revenir vous entraîner régulièrement avec ces exercices tout au long de l'année. Ils sont généralement accompagnés de pistes et de leur solution pour vous permettre de progresser.

Rappels

  • Chaque programme Python doit être sauvegardé sous forme de fichier texte avec l'extension .py.
    Enregistrez ce fichier dans le dossier [D02-Modularité] avec le nom donné à l'exercice : ProgD02.51.py, ProgD02.52.py, etc...
  • Pour exécuter ce programme, il suffit de le sauvegarder puis d'appuyer sur la touche [F5].

Consignes communes

Dans chacun des exercices de cette partie, il vous est demandé :

  • de programmer la structure de données signalée ;
  • d'établir la documentation du module ;
  • d'effectuer les tests nécessaires et de les placer dans le « main » (programme principal) de ce module.

Exercice D02.51

Définir un classe Date pour représenter une date avec trois attributs (entiers positifs ou nuls) : jour, mois et annee.

Les méthodes de cette classe doivent permettrent :

  • d'initialiser une date ;
  • d'afficher cette date sous les formes suivantes : « 24 septembre 2020 » ou bien « 24/09/2020 » ;
  • de comparer deux dates en surchargeant les opérateurs de test d'égalité « == » et d'inégalité « < ».

exemple de tests

>>> d1 = Date()
>>> print(d1)
1 janvier 0

>>> d1
01/01/0000

>>> d2 = Date(24, 9, 2020)
>>> print(d2)
24 septembre 2020

>>> d1 == d2
False

>>> d1 < d2
True

>>> d3 = Date(23, 9, 2020)
>>> d3
23/09/2020

>>> d2 < d3 
False
Une piste - un code à compléter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Date:
    """
    Une classe pour représenter une date, au format jour/mois/année.
    Initialisation : Date(j, m, a).
    j, m, a - entiers tels que :
        1 <= j <= 31 initialisé à 1 par défaut.
        1 <= m <= 12 initialisé à 1 par défaut.
        0 <= a       initialisée à 0 par défaut.
    Le test d'égalité == indique si deux dates sont identiques
    Le test d'inégalité < indique si la première date est plus ancienne que la seconde
    """

    def __init__(self, j=1, m=1, a=0):
        pass


    def __repr__(self):
        """
        L'appel au nom de la variable renvoie une chaîne sous la forme :
        01/01/0000
        """

    def __str__(self):
        """
        print() renvoie un affichage sous la forme :
        1 janvier 0
        """

    def __eq__(self, autreDate):
        pass

    def __lt__(self, autreDate):
        pass


if __name__ == "__main__":
    # À compléter
Une solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class Date:
    """
    Une classe pour représenter une date, au format jour/mois/année.
    Initialisation : Date(j, m, a).
    j, m, a - entiers tels que :
        1 <= j <= 31 initialisé à 1 par défaut.
        1 <= m <= 12 initialisé à 1 par défaut.
        0 <= a       initialisée à 0 par défaut.
    Le test d'égalité == indique si deux dates sont identiques
    Le test d'inégalité < indique si la première date est plus ancienne que la seconde
    """

    def __init__(self, j=1, m=1, a=0):
        self.jour = j
        self.mois = m
        self.annee = a

    def __repr__(self):
        """
        L'appel au nom de la variable renvoie une chaîne sous la forme :
        01/01/0000
        """
        j = str(self.jour)
        if self.jour < 10:
            j = "0"+j
        m = str(self.mois)
        if self.mois < 10:
            m = "0"+m
        a = str(self.annee)
        while len(a) < 4:
            a = "0"+a
        return j + "/" + m +"/" + a

    def __str__(self):
        """
        print() renvoie un affichage sous la forme :
        1 janvier 0
        """
        mois = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"]
        m = mois[self.mois-1]
        return f"{self.jour} {m} {self.annee}"

    def __eq__(self, autreDate):
        return self.jour == autreDate.jour and self.mois == autreDate.mois and self.annee == autreDate.annee

    def __lt__(self, autreDate):
        if self.annee < autreDate.annee:
            return True
        elif self.annee == autreDate.annee:
            if self.mois < autreDate.mois:
                return True
            elif self.mois == autreDate.mois:
                if self.jour < autreDate.jour:
                    return True
        return False


if __name__ == "__main__":
    print("Date initialisée par défaut")
    d1 = Date()
    print(d1)

    print("\nUne autre date")
    d2 = Date(24, 9, 2020)
    print(d2)

    print("\nLes deux dates sont-elles égales ?")
    print(d1==d2)

    print("\nLa première est-elle inférieure à la seconde ?")
    print(d1<d2)


    print("\nLa deuxième date est-elle inférieure à la troisième ?")
    d3 = Date(23, 9, 2020)
    print(d3)
    print(d2<d3)

Exercice D02.52

Définir un classe Ensemble qui représente un ensemble d'entiers positifs ou nuls, sans doublons. Cette classe a un unique attribut nommé valeurs qui est un tableau d'entiers tous distincts, triés par ordre croissant.

Les méthodes de cette classe doivent permettrent :

  • d'initialiser un ensemble (vide, ou à partir d'une liste déjà donnée) ;
  • de s'assurer que le tableau valeurs ne contient que des entiers distincts ;
  • d'ordonner le tableau valeurs par ordre croissant ;
  • d'afficher ce tableau ;
  • de renvoyer le tableau valeurs ;
  • d'indiquer si un entier val passé en paramètre est dans l'ensemble considéré ou pas (vous pouvez surcharger l'opérateur in, mais il faudrait réussir à écrire un code qui n'utilise pas in) ;
  • d'ajouter un entier val à cet ensemble (bien évidemment, directement à la bonne place...).

exemple de tests

>>> from random import randint
>>> e = Ensemble([randint(1, 100) for i in range(15)])
>>> print(e)
[17, 23, 29, 33, 35, 36, 46, 71, 77, 79, 83, 84, 86, 95]

>>> 50 in e
False

>>> 79 in e
True

>>> e.ajoute(50)
>>> e.ajoute(79)
>>> print(e)
[17, 23, 29, 33, 35, 36, 46, 50, 71, 77, 79, 83, 84, 86, 95]
Une piste

Dans l'initalisation, vous pouvez utiliser un algorithme éudié l'an dernier sur les tableaux pour le trier par ordre croissant.

Une piste - un code à compléter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Ensemble:
    """
    Une classe pour représenter un ensemble d'entiers positifs ou nuls, sans doublons
    L'opérateur "in" renvoie vrai si l'entier saisit en paramètre appartient à l'ensemble
    ajoute(val) : ajoute val à l'ensemble si cet entier n'est pas déjà dans l'ensemble
    """

    def __init__(self, liste = None):
        pass


    def __repr__(self):
        pass


    def __str__(self):
        pass


    def __contains__(self, val):
        pass


    def ajoute(self, val):
        pass


if __name__ == "__main__":
    # À compléter
Une solution
class Ensemble:
    """
    Une classe pour représenter un ensemble d'entiers positifs ou nuls, sans doublons
    L'opérateur "in" renvoie vrai si l'entier saisit en paramètre appartient à l'ensemble
    ajoute(val) : ajoute val à l'ensemble si cet entier n'est pas déjà dans l'ensemble
    """

    def __init__(self, liste = None):
        self.valeurs = []
        if liste is not None:
            for elt in liste:
                if elt not in self.valeurs:
                    self.valeurs.append(elt)
            self.valeurs.sort()

    def __repr__(self):
        return str(self.valeurs)

    def __str__(self):
        return self.__repr__()

    def __contains__(self, val):
        for elt in self.valeurs:
            if elt == val:
                return True
        return False

    def ajoute(self, val):
        if not self.__contains__(val):
            resultat = []
            i = 0
            while self.valeurs[i] < val:
                resultat.append(self.valeurs[i])
                i = i+1
            resultat.append(val)
            for k in range(i, len(self.valeurs)):
                resultat.append(self.valeurs[k])
            self.valeurs = resultat


if __name__ == "__main__":
    from random import randint

    print("Création d'un ensemble :")
    e = Ensemble([randint(1, 100) for _ in range(40)])
    print(e)

    print("\nAppartenance :")
    print("50 est-il dans l'ensemble ?", 50 in e)
    print("67 est-il dans l'ensemble ?", 67 in e)

    print("\nInsertion :")
    print("On ajoute 50.")
    e.ajoute(50)
    print("On ajoute 67")
    e.ajoute(67)
    print(e)

Exercice D02.53

Un objet de la classe Carte est muni de trois attributs :

  • couleur (chaîne de caractères entre 'PIQUE', 'COEUR', 'CARREAU' et 'TREFLE') ;
  • nom (chaîne de caractères entre '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Valet', 'Dame', 'Roi' et 'As') ;
  • valeur (entier compris entre 2 et 14).

Pour définir un objet de cette classe, il faut deux arguments : le nom et la couleur de la carte. Il n'y a pas d'initialisation par défaut.

  1. Programmez la classe Carte en respectant les spécifications données dans le docstring.

    1
    2
    3
    4
    5
    6
    7
    8
    class Carte:
        """
        Une classe pour représenter une carte à jouer
        get_couleur() renvoie la couleur de la carte
        get_nom() renvoie le nom de la carte
        meme_nom(autreCarte) renvoie True si les cartes ont le même nom
        est_battue_par(autreCarte) renvoie True si la carte considérée a une valeur inférieure à celle de autreCarte
        """
    

    Exemple de tests

    >>> c1 = Carte('10', 'PIQUE')
    >>> print(c1)
    10 de PIQUE
    
    >>> c1
    10 de PIQUE
    
    >>> c2 = Carte('10', 'ROUGE')
    AssertionError: La couleur ne convient pas
    
    >>> c2 = Carte('12', 'PIQUE')
    AssertionError: Le nom ne convient pas
    
    >>> c1.get_couleur()
    'PIQUE'
    
    >>> c1.get_nom()
    '10'
    
    >>> c2 = Carte('10', 'TREFLE')
    >>> c1.meme_nom(c2)
    True
    
    >>> c1.est_battue_par(c2)
    False
    
    Une piste - un code à compléter
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Carte:
        """
        Une classe pour représenter une carte à jouer
        get_couleur() renvoie la couleur de la carte
        get_nom() renvoie le nom de la carte
        meme_nom(autreCarte) renvoie True si les cartes ont le même nom
        est_battue_par(autreCarte) renvoie True si la carte considérée a une valeur inférieure à celle de autreCarte
        """
    
        def __init__(self, nom, couleur):
            pass
    
    
        def __repr__(self):
            pass
    
    
        def __str__(self):
            pass
    
    
        def get_couleur(self):
            pass
    
    
        def get_nom(self):
            pass
    
    
        def meme_nom(self, autreCarte):
            pass
    
    
        def est_battue_par(self, autreCarte):
            pass
    
    Une autre piste

    Pour faciliter votre travail, vous pouvez utiliser/copier/coller les variables globales suivantes en les plaçant au début de votre programme, en dehors de la définition de la classe Carte.

    1
    2
    couleurs = ['PIQUE', 'COEUR', 'CARREAU', 'TREFLE']
    noms  = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Valet', 'Dame', 'Roi', 'As']
    
    Une solution
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    couleurs = ['PIQUE', 'COEUR', 'CARREAU', 'TREFLE']
    noms  = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Valet', 'Dame', 'Roi', 'As']
    
    class Carte:
        """
        Une classe pour représenter une carte à jouer
        get_couleur() renvoie la couleur de la carte
        get_nom() renvoie le nom de la carte
        meme_nom(autreCarte) renvoie True si les cartes ont le même nom
        est_battue_par(autreCarte) renvoie True si la carte considérée a une valeur inférieure à celle de autreCarte
        """
    
        def __init__(self, nom, couleur):
            assert couleur in couleurs, 'La couleur ne convient pas'
            assert nom in noms, 'Le nom ne convient pas'
            self.couleur = couleur
            self.nom = nom
            self.valeur = noms.index(self.nom)
    
        def __repr__(self):
            return f"{self.nom} de {self.couleur}"
    
        def __str__(self):
            return self.__repr__()
    
        def get_couleur(self):
            return self.couleur
    
        def get_nom(self):
            return self.nom
    
        def meme_nom(self, autreCarte):
            return self.nom == autreCarte.nom
    
        def est_battue_par(self, autreCarte):
            return self.valeur < autreCarte.valeur
    
  2. Programmez la classe PaquetCartes en respectant les spécifications données.
    Cette classe a un seul attribut paquet qui représente le tableau (la liste) des cartes contenues dans le paquet. Lors de l'initiatisation :

    • Si la liste fournie est vide, il faut vérifier que nb vaut soit 32, soit 54 ;
    • Sinon la liste devient le paquet de cartes (pour faciliter les choses, il n'est pas utile de vérifier que chaque carte respecte les spécifications de la classe Carte).
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    class PaquetCartes:
        """
        Une classe pour représenter un paquet de cartes à jouer
        PaquetCartes(nb, liste) :
            Lorsque liste vaut None, génère et mélange un paquet de nb cartes (nb doit valoir 32 ou 52).
            Lorsque liste est fourni et n'est pas vide, cette liste correspond au paquet.
        distribue_carte() renvoie la première Carte du jeu et met à jour le paquet de Cartes.
        distribue_jeu(nbJoueurs, nbCartes) renvoie un tableau de nbJoueurs paquets de cartes
        contenant nbCartes cartes s'il est possible de distribuer nbCartes à nbJoueurs.
        """
    
        def __init__(self, nb, liste=None):
            pass
    
    
        def __repr__(self):
            pass
    
    
        def __str__(self):
            pass
    
    
        def melanger(self):
            pass
    
    
        def distribue_carte(self):
            pass
    
    
        def distribue_jeu(self, nbJoueurs, nbCartes):
            pass
    

    Exemple de tests

    >>> paquet1 = PaquetCartes(20)
    AssertionError: Le nombre de cartes du paquet est soit 32, soit 54
    
    >>> paquet1 = PaquetCartes(32)
    >>> paquet1
    [7 de COEUR, 7 de TREFLE, 9 de PIQUE, 9 de COEUR, 9 de TREFLE, 10 de COEUR, Valet de PIQUE, Valet de COEUR, Dame de COEUR, Roi de CARREAU, Valet de TREFLE, As de COEUR, 10 de CARREAU, Dame de CARREAU, Valet de CARREAU, 8 de PIQUE, 7 de CARREAU, As de PIQUE, 7 de PIQUE, Roi de COEUR, As de CARREAU, Dame de TREFLE, Dame de PIQUE, 9 de CARREAU, 10 de TREFLE, As de TREFLE, 8 de TREFLE, 8 de CARREAU, Roi de TREFLE, 10 de PIQUE, 8 de COEUR, Roi de PIQUE]
    
    >>> paquets = paquet1.distribue_jeu(4, 10)
    AssertionError: Distribution impossible, 4*10 est supérieur au nombre de cartes du paquet : 32.
    
    >>> paquets = paquet1.distribue_jeu(4, 8)
    >>> paquets
    [[7 de COEUR, Dame de TREFLE, 8 de PIQUE, As de TREFLE, 10 de PIQUE, Roi de TREFLE, 9 de TREFLE, Dame de PIQUE],
     [7 de TREFLE, 10 de COEUR, Dame de CARREAU, 7 de PIQUE, 9 de COEUR, 9 de PIQUE, Roi de PIQUE, Roi de CARREAU],
     [Dame de COEUR, 8 de CARREAU, Valet de CARREAU, 8 de COEUR, 10 de CARREAU, As de PIQUE, Valet de PIQUE, Roi de COEUR],
     [As de COEUR, As de CARREAU, 10 de TREFLE, 9 de CARREAU, 7 de CARREAU, Valet de TREFLE, 8 de TREFLE, Valet de COEUR]]
    
    >>> paquet2 = paquets[0]
    >>> print(paquet2)
    [7 de COEUR, Dame de TREFLE, 8 de PIQUE, As de TREFLE, 10 de PIQUE, Roi de TREFLE, 9 de TREFLE, Dame de PIQUE]
    
    >>> carte1 = paquet2.distribue_carte()
    >>> carte1
    7 de COEUR
    
    >>> carte2 = paquet2.distribue_carte()
    >>> carte2
    Dame de TREFLE
    
    >>> carte1.meme_nom(carte2)
    False
    
    >>> carte1.est_battue_par(carte2)
    True
    
    Une solution - le code complet
    from random import randint
    
    couleurs = ['PIQUE', 'COEUR', 'CARREAU', 'TREFLE']
    noms  = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Valet', 'Dame', 'Roi', 'As']
    
    
    class Carte:
        """
        Une classe pour représenter une carte à jouer
        get_couleur() renvoie la couleur de la carte
        get_nom() renvoie le nom de la carte
        meme_nom(autreCarte) renvoie True si les cartes ont le même nom
        est_battue_par(autreCarte) renvoie True si la carte considérée a une valeur inférieure à celle de autreCarte
        """
    
        def __init__(self, nom, couleur):
            assert couleur in couleurs, 'La couleur ne convient pas'
            assert nom in noms, 'Le nom ne convient pas'
            self.couleur = couleur
            self.nom = nom
            self.valeur = noms.index(self.nom)
    
        def __repr__(self):
            return f"{self.nom} de {self.couleur}"
    
        def __str__(self):
            return self.__repr__()
    
        def get_couleur(self):
            return self.couleur
    
        def get_nom(self):
            return self.nom
    
        def meme_nom(self, autreCarte):
            return self.nom == autreCarte.nom
    
        def est_battue_par(self, autreCarte):
            return self.valeur < autreCarte.valeur
    
    
    
    class PaquetCartes:
        """
        Une classe pour représenter un paquet de cartes à jouer
        PaquetCartes(nb, liste) :
            Lorsque liste vaut None, génère et mélange un paquet de nb cartes (nb doit valoir 32 ou 52).
            Lorsque liste est fourni et n'est pas vide, cette liste correspond au paquet.
        distribue_carte() renvoie la première Carte du jeu et met à jour le paquet de Cartes.
        distribue_jeu(nbJoueurs, nbCartes) renvoie un tableau de nbJoueurs Paquets de cartes
        contenant nbCartes cartes s'il est possible de distribuer nbCartes à nbJoueurs.
        """
    
        def __init__(self, nb, liste=None):
            if liste is None:
                assert nb == 32 or nb == 54, "Le nombre de cartes du paquet est soit 32, soit 54"
                if nb == 52:
                    self.paquet = [Carte(nom, couleur) for nom in noms for couleur in couleurs]
                else:
                    self.paquet = [Carte(noms[i], couleur) for i in range(5, len(noms)) for couleur in couleurs]
    
            else:
                self.paquet = liste
    
            self.melanger()
    
        def __repr__(self):
            return str(self.paquet)
    
        def __str__(self):
            return self.__repr__()
    
        def melanger(self):
            nb_cartes = len(self.paquet)
            dernier_indice = nb_cartes-1
            for i in range(nb_cartes):
                # On retire un élément au hasard :
                elt = self.paquet.pop(randint(0, dernier_indice))
                # On le place à la fin
                self.paquet.append(elt)
    
        def distribue_carte(self):
            carte = self.paquet.pop(0)
            return carte
    
        def distribue_jeu(self, nbJoueurs, nbCartes):
            assert nbJoueurs*nbCartes <= len(self.paquet), f"Distribution impossible, {nbJoueurs}*{nbCartes} est supérieur au nombre de cartes du paquet : {len(self.paquet)}."
            jeux = []
            for i in range(nbJoueurs):
                jeux.append([])
            for n in range(nbCartes):
                for j in range(nbJoueurs):
                    jeux[j].append(self.distribue_carte())
            paquets = []
            for liste in jeux:
                paquets.append(PaquetCartes(nbCartes, liste))
            return paquets
    
    
    if __name__ == "__main__":
        print("Création d'un paquet de 32 cartes :")
        paquet1 = PaquetCartes(32)
        print(paquet1)
    
        print("\nOn distribue pour 4 joueurs :")
        paquets = paquet1.distribue_jeu(4, 8)
        for paquet in paquets:
            print(paquet)
    
        print("\nOn récupère le premier paquet, on en fait un jeu :")
        paquet2 = paquets[0]
        print(paquet2)
    
        print("\nOn extrait les deux premières cartes, on teste si la première est battue par la deuxième :")
        carte1 = paquet2.distribue_carte()
        print(carte1)
        carte2 = paquet2.distribue_carte()
        print(carte2)
        print("Sont-elles de même valeur ?", carte1.meme_nom(carte2))
        print("La première est-elle battue ?", carte1.est_battue_par(carte2))