Projet : objets dés

Projet sur le codage d'objets en python. Notre but est de pouvoir créer des dés virtuels pour pouvoir jouer au 421.

Méthode de conception

Lorsque l'on crée un objet qui correspond à un objet réel, il faut faire en sorte que le code l'utilise comme on le ferait dans la réalité.

Fonctionnalités d'un Dé

Si on veut pouvoir utiliser nos dés virtuels comme un vrai dé physique, la classe doit être capable de :

La valeur du dé doit être un entier entre 1 et 6.

Modélisation UML

Des spécifications précédentes, on doit pouvoir en déterminer la modélisation UML.

Proposez une modélisation UML de la classe .

corrigé

uml

Squelette python

Créez un dossier projet-dés sur votre ordinateur et ouvrez-le avec visual studio code pour un faire votre projet.

On va créer tous les fichiers nécessaires dans le projet, mais avant de coder directement commençons par créer le squelette du projet.

Créez le fichier dé.py dans le projet et mettez uniquement les méthodes de la classe sans le code proprement dit.

Pour que votre code soit du code python valide, il faut que chaque bloc ait au moins une instruction. Vous pourrez utiliser l'instruction pass qui ne fait rien.

corrigé

Fichier dé.py :

class Dé:
    def __init__(self):
        pass

    def lancer(self):
        pass

Conventions

  • les noms de classe commencent par une majuscule
  • l'implémentation de la classe est placée dans un fichier de même nom mais avec une minuscule

En python, beaucoup de choses sont des conventions (variable privée, premier nom est self, ...) mais tout le monde s'y tient car la lecture du code en devient aisée. Il est facile de savoir de quel type est le nom rencontré en python si l'on utilise les façons de faire classiques, décrites dans la PEP 8 de python.

On a pas fini ! Il faut encore écrire les tests relatifs à notre classe. Pour l'instant on ne peut guère que vérifier que l'on crée bien un objet de la classe .

Créez le fichier test_dé.py et testez que le constructeur de la classe crée bien un objet de la classe .

Vous pourrez utiliser la fonction python isinstance

corrigé

Fichier test_dé.py :

fromimportdef test_init():=()
    assert isinstance(,)

Comme le retour du constructeur est directement l'objet, la variable est inutile et on peut très bien directement écrire le test suivant :

fromimportdef test_init():
    assert isinstance((),)

Ne confondez pas est la classe et Dé() qui est le résultat du constructeur, c'est à dire un objet de la classe .

Il nous reste une chose à faire pour que notre code python soit conforme à l'UML : la déclaration des attributs. En python les attributs sont affecté dans le constructeur.

Créez les attributs des objets de la classe .

corrigé

Fichier dé.py :

class Dé:
    def __init__(self):
        self.valeur = 1

    def lancer(self):
        pass

Ajoutez un test qu projet qui vérifie que l'attribut valeur d'un objet de la classe fraîchement crée est bien à !

corrigé

Fichier test_dé.py :

fromimport# ...

def test_valeur():=()
    assert.valeur == 1

On peut encore une fois raccourcir le test si l'on veut en utilisant directement le retour du constructeur :

fromimport# ...

def test_valeur():
    assert().valeur == 1

Code Classe

Terminons l'implémentation de la classe en codant la méthode Dé.lancer().

Dans la documentation et lorsque l'on décrit une méthode, som nom de la méthode est toujours accolé au nom de la classe qui la définit. Par exemple : Dé.lancer() signifie :

À retenir

Lorsque l'on décrit une méthode, on ne montre jamais self. Ce n'est en effet pas un paramètre de la méthode à proprement parlé, c'est l'objet de la méthode : il est à gauche du . lors de l'appel de la fonction.

On pourrait (mais on ne le fait pas parce que c'est moins clair) replacer le code suivant :

>>> un_dé =()
>>> un_dé.lancer()

Par le code ci-après qui est équivalent :

>>> un_dé =()
>>>.lancer(un_dé)

On a toutes les information nécessaire pour le faire :

Implémentez la méthode Dé.lancer(). et ses tests.

Utilisez le module random de python

corrigé

Fichier dé.py :

class Dé:
   # ...

    def lancer(self):
        self.valeur = random.randrange(1, 6 + 1)

On a pas fini le code puisqu'il manque les tests de la méthode Dé.lancer() ! On a cependant un soucis car il est impossible de tester le hasard (on pourrait n'avoir pas de chance et lancer 10 fois le dé sans que la valeur ne change sans que ce soit mal codé). Comme on a besoin que nos fonctionnent toujours, il faut trouver ce qui est toujours vrai après un lancer :

Implémentez un test qui vérifie que la méthode s'est bien exécutée Dé.lancer() et que la valeur de la valeur reste entre 1 et 6.

corrigé

Fichier test_dé.py :

fromimport# ...

def test_lancer():=().lancer()
    assert 1 <=.valeur <= 6

User stories

Définition

Une user story est un récit qui nous permet de savoir comment et par qui va être utilisé notre code.

L'idée est d'écrire une succession d'actions faites par un utilisateur typique afin de réaliser une tâche précise avec notre programme. Par exemple :

User story

  • Nom : "Aléatoire ?"
  • Utilisateur : un professeur.
  • Story : On vérifie que le lancer de dé ressemble à de l'aléatoire.
  • Actions :
    1. créer un dé sans paramètre
    2. afficher à l'écran sa valeur (ça doit être 1)
    3. lancer le dé 10 fois et affiche la valeur du dé après chaque lancer. Quelle est la probabilité que le dé ne change jamais ?.

Codez cette user story si on devait la coder :

En utilisant la modélisation UML du Dé, codez la user story "Aléatoire" en python dans le fichier story_aléatoire.py.

corrigé

Il y a une probabilité de $\frac{1}{6^{10}} = 1.6 \cdot 10^{-8}$ que le dé ne change jamais de valeur en 10 lancers.

La user story donnerait en python :

fromimport# 1. créer un dé sans paramètre=() 

# 2. afficher à l'écran sa valeur (ça doit être 1)
print(.valeur)

# 3. lancer le dé 10 fois et affiche la valeur du dé après chaque lancer
for i in range(10):.lancer()
    print(.valeur)

La user story fait office de test fonctionnel qui permet de vérifier que le code correspond aux attentes des utilisateurs :

Définition

Un test fonctionnel est un programme qui démontre les fonctionnalités d'une application. Il est l'implémentation d'une user story.

Au final :

À retenir

Un programme aura :

  • toujours des tests unitaires car ils garantissent que ce que vous avez codé est correct
  • très souvent des tests fonctionnels car ils garantissent que ce que vous avez codé pourra être utile

On exécutera la batterie de tests unitaires à chaque fois que l'on code ou que l'on modifie une fonction, les tests fonctionnels sont exécutés a chaque fois que l'on achève une fonctionnalité.

Les fonctionnalités développées doivent toutes faire parti d'au moins une user story, sinon c'est YAGNI.

Affichage

Pour l'instant, lorsque l'on tente d'afficher un dé on obtient le charabia suivant :

>>>=()
>>> print()
<__main__.object at 0x10a691010>

Ce qui n'est pas très informatif. On peut bien sur afficher sa valeur :

>>>=()
>>> print(.valeur)
1

Mais représenter un dé par un entier ce n'est pas très joli. Nous allons coder une méthode spécifique pour représenter notre objet sous la forme d'une chaîne de caractère : la méthode Dé.texte().

Créez (et faites les tests) une méthode Dé.texte() qui permette d'écrire :

>>> d =()
>>> print(d.texte())>>> d.valeur = 4
>>> print(d.texte())

Vous pourrez utiliser les caractères : "⚀", "⚁", "⚂", "⚃", "⚄" et "⚅" pour vos représentations.

Vous pourrez maintenant utiliser cette méthode pour l'affichage de tous vos dés, en particulier pour les user stories !

Programme principal

Avant de coder le programme principal :

  1. vérifiez que les tests unitaires fonctionnent
  2. vérifiez que vos user stories sont toutes fonctionnelles

Une fois tout ok, on peut commencer à créer le code du main.py :

Créez un fichier main.py qui :

  1. demande à l'utilisateur :
    • la valeur initiale du dé
    • la valeur pour laquelle arrêter les lancers
  2. lance le dé jusqu'à tant que sa valeur est différente de la valeur demandée par l’utilisateur soit trouvée.
  3. le programme affiche le nombre de lancer nécessaire (cela peut être 0)

corrigé

Fichier main.py :

fromimport Dé

valeur_initiale = int(input("valeur initiale du dé : "))
valeur_finale = int(input("valeur finale du dé : "))=().valeur = valeur_initiale

nombre_lancer = 0
while.valeur != valeur_finale:.lancer()
    nombre_lancer += 1

print("Il a fallu : ", nombre_lancer, "lancers")

Code Python de la classe Dé