Projet : objets cartes

Encore un projet d'initiation dans le codage des objets. On s'intéresse ici aux méthodes spéciales qui permettent d'utiliser les objets comme des nombres.

Vous allez coder une classe Carte, ce qui permettra par la suite de jouer à la bataille. La classe carte en elle-même ne fera pas grand chose, mais elle illustrera la notion de value object :

Définition

Un value object est un objet ne pouvant pas être modifié une fois créé : il ne possède aucune méthode lui permettant de changer ses attributs qu'il faut renseigner à sa création.

Ce projet va vous faire utiliser des méthodes d'améliorations d'objets comme :

Remémorez vous ces parties avant de commencer le projet.

Projet

Vscode

Créez un dossier projet-cartes sur votre ordinateur et ouvrez leu avec visual studio code pour un faire votre projet.

Programme principal & User stories

Le but du projet est de pouvoir jouer à une variante de la bataille :

But

On veut pouvoir mélanger un jeu de 32 cartes (sans joker) puis le séparer en 2 pioches de 16 cartes, un tas par joueur.

A chaque tour les deux joueurs prennent la première carte de leur pioche et la révèle. Le joueur ayant la plus grande carte (7 < 8 < 9 < 10 < V < D < R < 1 et si égalité de rang alors : ♠ > ♥ > ♦ > ♣︎) prend les deux cartes et les place dans sa pile de défausse (initialement vide).

Lorsqu'un joueur doit prendre une carte alors que sa pioche est vide, il mélange les cartes de sa défausse qui forment une nouvelle pioche. Si la pioche et la défausse sont vides, le joueur perd la partie.

Carte UML

La pioche et la défausse pouvant être facilement modélisées par des listes, il nous reste à créer une classe Carte pour avoir tous les éléments de base de notre projet.

Proposez une modélisation UML d'une classe Carte pour notre projet

solution

Un constructeur, un formatage en chaîne de caractères pour affichage à l'écran et des opérateurs de comparaison :

carte UML

User stories

Le projet nécessite de faire plein de choses. Pour vous aider à réaliser ce but, on va se placer des objectifs intermédiaires, sous la forme de user stories.

Je vous en propose une ci-après qui exhibe la capacité à créer un jeu de 32 cartes et à afficher les cartes à l'écran :

User Story

  • Nom : "Voyance"
  • Utilisateur : un voyant extralucide.
  • Story : On veut pouvoir tirer les cartes
  • Actions :
    1. créer un paquet de 32 cartes (sans joker)
    2. prendre au hasard 3 cartes du paquet
    3. afficher à l'écran les trois cartes, dans l'ordre où elles ont été tirées

Par rapport au jeu, il manque essentiellement la fonctionnalité permettant d'ordonner les cartes :

Créez une user story qui exhibe la fonctionnalité de pouvoir ordonner les cartes.

En affichant 10 cartes tirées avec remise dans l'ordre où elles ont été tirées, puis dans l'ordre croisant.

corrigé

  • Nom : "Ordonnancement"
  • Utilisateur : un adepte de réussite
  • Story : On veut pouvoir ranger les cartes par ordre croissant
  • Actions :
    1. choisir 10 cartes au hasard (on peut avoir les mêmes cartes)
    2. afficher à l'écran les 10 cartes, dans l'ordre où elles ont été tirées
    3. afficher à l'écran les 10 cartes, dans l'ordre croissant

Créons les fichiers pour nos users stories, même si le code n'est pas encore clair. Par exemple pour la user story "voyance", on crée un fichier story_voyance.py contenant :

# création d'un paquet de 32 cartes
# prendre au hasard 3 cartes du paquet
# afficher à l'écran les trois cartes, dans l'ordre où elles ont été tirées

On ajoutera petit à petit le code permettant d'implémenter la story, au fur et à mesure de l'avancement du projet.

Créez les deux fichiers de story.

Code

Créez les fichiers qui nous permettront de coder la carte :

  • carte.py
  • test_carte.py

Constructeur

Le constructeur d'une carte nécessite 2 paramètres : la valeur et la couleur.

En considérant que les deux paramètres couleur et valeur sont des chaînes de caractères, quelles sont les possibilités admissibles pour construire une carte ?

corrigé

Par exemple, pour les valeurs :

  • "sept"
  • "huit"
  • "neuf"
  • "dix"
  • "valet"
  • "dame"
  • "roi"
  • "as"

Pour les couleurs :

  • "pique"
  • "cœur"
  • "carreau"
  • "trèfle"

Implémentez le constructeur de la classe Carte et ses tests en supposant que l'utilisateur entre les bonnes valeurs de paramètres.

Affichage à l'écran

Pour permettre un affichage à l'écran plus convivial :

Codez la méthode __str__ d'une carte. Le code suivant doit pouvoir fonctionner :

>>> from carte import Carte
>>> ace_pique = Carte("as", "pique")
>>> print(ace_pique)
as de pique

Faites un test de cette méthode en testant la représentation sous la forme d'une chaîne de caractères d'une Carte.

La représentation sous la forme d'une chaîne de caractères un objet x est le résultat de str(x).

Lorsque l'on écrit print(ace_pique), python transforme l'objet en chaîne de caractères avec la commande str qui elle-même cherche la méthode __str__. Les trois instructions suivantes sont donc équivalentes :

  1. print(ace_pique)
  2. print(str(ace_pique))
  3. print(ace_pique.__str__())

Représentation de l'objet

Vous verrez parfois une autre méthode de représentation d'un objet utilisant la commande repr(). Cette fonction doit permettre de reconstruire l'objet si nécessaire.

Par exemple :

>>> from carte import Carte
>>> ace_pique = Carte("as", "pique")
>>> print(repr(ace_pique))
Carte('as', 'pique')

On utilise souvent repr() pour du débogage (donc de l'affichage développeur), alors que str() est utilisé pour de l'affichage utilisateur.

  • on utilise str(objet) (crée avec la méthode __str__) pour un affichage à l'écran. On transforme l'objet en un texte.
  • on utilise repr(objet) (crée avec la méthode __repr__) pour représenter l'objet sous la forme d'une chaîne de caractères. On doit pouvoir reconstruire un objet identique avec la commande eval (eval(repr(objet)) doit rendre un objet similaire à objet.

Un petit tuto français pour expliciter les différences entre les deux représentations : https://www.youtube.com/watch?v=ejGYAnf_X24

Créez et testez la méthode __repr__

corrigé

class Carte:
    # ...

    def __repr__(self):
        return "Carte(" + repr(self.valeur) + ", " + repr(self.couleur) + ")"
    
    # ...

Constantes de classes

Avant de pouvoir finir la partie de création d'une carte, il nous reste un problème à résoudre. Comment indiquer à l'utilisateur les possibilités de valeur et de couleurs ?

La solution communément utilisée pour cela est de créer des constantes :

Créez les constantes :

  • SEPT, HUIT, NEUF, DIX, VALET, DAME, ROI, AS
  • PIQUE, COEUR, CARREAU, TREFLE

En leur associant les chaînes de caractères adéquates.

Il ne faudra qu'utiliser ces constantes pour créer les cartes et ne plus directement utiliser des chaînes de caractères comme "sept" qui sont des MAGIC NUMBERS.

Par exemple, on écrira Carte(AS, TREFLE) plutôt que Carte("as", "trèfle)

Utilisez dans le code et les tests les constantes à la place des chaînes de caractères.

Vous n'êtes pas obligé d'importer toutes les constantes, une à une. En utilisant juste import carte, vous pourrez utiliser carte.PIQUE (constante PIQUE dans l'espace de nom de carte) directement par exemple.

Enfin, pour grouper ces constantes, vous pourrez :

Créer deux autres constantes, qui rassemblent les couleurs et les valeurs entre elles :

  • VALEURS = [SEPT, HUIT, NEUF, DIX, VALET, DAME, ROI, AS]
  • COULEURS = [TREFLE, CARREAU, COEUR, PIQUE]

Remarquez que l'on a rangé les différentes valeurs par ordre croissant de valeur et de couleur.

User story voyance

Vous avez tous les outils nécessaires pour créer la user story "voyance" :

Codez la user story "voyance".

Vous pourrez utiliser la fonction random.sample pour tirer des cartes sans remise d'un paquet.

Comparaisons

Codez et testez les opérateurs de comparaisons :

  • == qui correspond a à la méthode __eq__
  • != qui correspond a à la méthode __ne__
  • < qui correspond a à la méthode __lt__
  • > qui correspond a à la méthode __gt__
  • <= qui correspond a à la méthode __le__
  • >= qui correspond a à la méthode __ge__

Il suffit de coder == et < pour obtenir toutes les comparaison.

Ceci devrait être suffisant pour la deuxième user story :

Codez la seconde user story.

Vous pourrez utiliser la fonction random.choices pour tirer des cartes avec remise d'un paquet.

Jeu

Vous pouvez maintenant finir le projet en codant le jeu !

La règle du jeu est :

  1. mélangez un jeu de 32 cartes en deux pioches de 16 cartes, une pour chaque joueur
  2. chaque joueur dispose également d'une défausse, initialement vide
  3. N = 1
  4. chaque joueur dévoile la carte du dessus de leur pioche
  5. le joueur ayant la carte la plus élevée remporte la carte de l'adversaire et pose les deux cartes (la sienne et celle de son adversaire) dans sa défausse
  6. si un joueur n'a plus de cartes dans sa pioche, il mélange les cartes de sa défausse pour en faire un nouvelle pioche
  7. si un joueur n'a plus de carte dans sa pioche, il perd la partie
  8. N = N + 1
  9. si N est inférieur ou au nombre maximum de tour, retour en 4, sinon le jeu s'arrête.

Codez le jeu dans un fichier main.py