Partage
  • Partager sur Facebook
  • Partager sur Twitter

Un jeu d'échec en console

Un problème de classe

Sujet résolu
29 août 2010 à 10:36:47

Bonjour les zéro,

comme indiqué dans le titre, je suis entrain de réaliser un petit jeu d'échec simpliste en console; histoire de revoir comment fonctionnent les classes et tout le tralala qui s'en suit. :-°
Mon jeu permet uniquement (pour le moment) de déplacer des pions virtuels dans n'importe quelle case de l'échiquier.
J'ai commencé par coder quelque chose utilisant trois classes. <Mère>, <Joueur>, <Pièce> où les deux dernières héritent de <Mère>.

Mon problème: J'appelle une méthode située dans la classe <Pièce> depuis la classe <Joueur>. Impossible de faire fonctionner ce petit jeu. :colere2:

J'ai supprimé la classe <Pièce> et inséré ses méthodes dans <Joueur> pour voir si tout marchait bien.
Résultat, tout va très bien ^^ .
Je veux absolument implémenter la classe <Pièce> mais je n'y arrive pas.

Pourriez-vous m'aider s'il vous plaît ?
echecMain.py (Contient quelques appels de fonctions (3))

from echecClases import *

#Mes variables.
j = Joueur()
p = Piece()

#Le programme principale
j.selectPiece()
j.updatePlateau()
print(P.get_plateau()) #On veut voir le résultat


echecClasse.py (Ne prenez pas peur, il y a beaucoup de doc-string mais le code est très facile à lire et à comprendre. Rien de bien beau dans la classe <Mère> à part sa méthode *updatePlateau*. Quelques méthodes inutiles pour l'instant à savoir get/set 'Nom' dans la classe <Joueur>

##
#Nombre de classes: (3)
#  - Mere
#  | - __init__()
#  | - updatePlateau()
#  |
#  - Piece(Mere)
#  | - __init__()
#  | - verifCoup()
#  | - bougerPiece()
#  | - get_plateau() 
#  |
#  - Joueur(Mere)
#  |  - __init__()
#  |  - property(_get_nom, _set_nom)
#  |  - selectPiece()
 
 
#Classe (1)
class Mere(object):
    """
    Initialisation du plateau de jeu
    du temps, du score, (de la map?),
    qui doit jouer
    """
    def __init__(self):
        self.plateau = {
                       "a1":"tourG-b", "b1":"cavalierG-b", "c1":"fouG-b", "d1":"dame-b",
                       "e1":"roi-b", "f1":"fouD-b", "g1":"cavalierD-b", "h1":"tourD-b",
 
                       "a2":"pion-b", "b2":"pion-b", "c2":"pion-b", "d2":"pion-b",
                       "e2":"pion-b", "f2":"pion-b", "g2":"pion-b", "h2":"pion-b",
 
                       "a3":None, "b3":None, "c3":None, "d3":None,
                       "e3":None, "f3":None, "g3":None, "h3":None,
 
                       "a4":None, "b4":None, "c4":None, "d4":None,
                       "e4":None, "f4":None, "g4":None, "h4":None,
 
                       "a5":None, "b5":None, "c5":None, "d5":None,
                       "e5":None, "f5":None, "g5":None, "h5":None,
 
                       "a6":None, "b6":None, "c6":None, "d6":None,
                       "e6":None, "f6":None, "g6":None, "h6":None,
 
                       "a7":"pion-n", "b7":"pion-n", "c7":"pion-n", "d7":"pion-n",
                       "e7":"pion-n", "f7":"pion-n", "g7":"pion-n", "h7":"pion-n",
 
                       "a8":"tourG-n", "b8":"cavalierG-n", "c8":"fouG-n", "d8":"dame-n",
                       "e8":"roi-n", "f8":"fouD-n", "g8":"cavalierD-n", "h8":"tourD-n"
                       }
        self.case = "a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8 \
                     e1e2e3e4e5e6e7e8f1f2f3f4f5f6f7f8g1g2g3g4g5g6g7g8h1h2h3h4h5h6h7h8" #Toutes les cases de l'échiquier
        self.score = 0
        self.etatPartie = True

    def updatePlateau(self):
        """Met à jour le plateau"""
        self.newPlateau = self.bougerPiece()
        self.plateau = self.newPlateau
 
 
#Classe (2)
class Piece(Mere):
    """
    Classe definissant où sont les pièces
    sur l'échiquiquier du plateauActuel:
    
    Méthodes:(3)
     - verifCoup()
     - bougerPiece()
     - get_plateau()
    """
    def __init__(self):
        """Initialise la classe <Mere>"""
        Mere.__init__(self)
 
 
    def verifCoup(self):
        """
        Verifie si le coup que le
        joueur souhaite faire est
        possible ou non
        """
        self.variable = True
        return self.variable #Essai. Ne tapez pas ;p
    
    def bougerPiece(self):
        """
        Applique les coordonées changées par le joueur
        sur le plateauActuel
        """ 
        self.numCase, self.goToCase = self.selectPiece()             #On récupère les coord de la piece sélectionnée + celui de son déplacement
        self.verificationCoup = self.verifCoup()
        if self.verificationCoup == True:
            self.plateau[self.goToCase] = self.plateau[self.numCase] #La variable de 'goToCase' devient la variable de 'numCase'
            self.plateau[self.numCase]  = None                       #Il n'y a donc plus rien dans la case précédante. *None*
            self.updatePlateau()                                     #On appelle automatiquement la méthode *updatePlateau()* de la classe <Mere>
        
    def get_plateau(self):
        """
        Donne les positions des pièces sur l'achiquier
        """
        return self.plateau
     
 
#classe (3) 
class Joueur(Mere):
    """
    Tout ce qui se rapporte aux
    actions du/des joueur/s ainsi que
    de l'I.A.:
 
    Méthodes:(2)
     - property(_get_nom, _set_nom)
     - selectPiece()
    """
    def __init__(self, _nom = "Joueur_1"):
        """
        Initialise le nom du joueur ainsi
        que la classe principale
        """
        Mere.__init__(self)
        self._nom = _nom
 
    def _get_nom(self):        #Voir proprety 
        """
        Donne le nom du joueur
        actuel
        """
        return self._nom
    
    def _set_nom(self, newNom): #Voir proprety 
        """
        Attribut un nouveau nom
        au joueur
        """
        self._nom = newNom
 
    def selectPiece(self):
        """
        Sélectionne une pièce de
        l'échiquier à bouger
        """
        while(True):
            self.numCase = input("""Entrez les coordonées du pion à déplacer\n 
                                    sous forme d'une lettre suivit d'un chiffre. Exemple:\n    
                                    a1, c2, h3\n=>""")
            
            if len(self.numCase) == 2 and self.numCase in self.case:
                self.goToCase = input("""Entrez les coordonées de la case\n
                                      où vous voulez bouger votre pièce\n=>""")
                
                if len(self.goToCase) == 2 and self.goToCase in self.case:
                    return self.numCase, self.goToCase #Si le num est correct, on retourne coord de la pièce+son déplacement.
                    break                              #Sans oublier de breaker pour éviter les loop infinies
            else:
                continue
            
                  
    nom = property(_get_nom, _set_nom)      #De la classe <Joueur>
    
if __name__ == "__main__":
    Piece()
    Joueur()


Voici donc mon code source.

P.S: Il y a aussi un bug bizarre lors de la sélection des coordonnées. Je dois les rentrer deux fois de suite, pourquoi donc ?
P.P.S: J'utilise Python 3.x

Je vous remercie à l'avance. :)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
29 août 2010 à 11:14:02

Je sais pas si j'ai bien compris, mais tout d'abord je dois te dire que c'est courageux de faire un jeu d'échecs, je ne m'y suis jamais osé.

Pourquoi hériter de la classe Mere si tu n'utilises pas ses méthodes dans d'autres classes


Si tu veux bénéficier des attributs d'une classe dans une autre classe, il suffit de l'instancier.

>>> class A:
...     def __init__(self):
...         self.plateau=3
... 
>>> class B:
...     def get_plateau(self):
...         a=A()
...         print(a.plateau)
... 
>>> b=B()
>>> b.get_plateau()
3


Citation

Mon problème: J'appelle une méthode située dans la classe <Pièce> depuis la classe <Joueur>. Impossible de faire fonctionner ce petit jeu.



Alors là par contre tu peux hériter une classe.

class Joueur(Piece)
  • Partager sur Facebook
  • Partager sur Twitter
29 août 2010 à 12:46:25

Salut, je pense que tes classes sont un peu trop "imbriquées" les unes dans les autres et que c'est pour ça que tu as du mal à faire fonctionner ton jeu.

Par exemple ta méthode updatePlateau dans ta classe Mere fait appel à la méthode bougerPiece dans la classe Piece, qui a son tour appelle updatePlateau une 2ème fois
. C'est inutile je trouve et...déroutant quand on essaye de débugger.

Pour gagner en clarté je te conseillerais de revoir l'organisation des classes :

- 1 classe "Piece" qui contient uniquement les éléments nécessaires pour décrire la pièce (nom, coordonnées, possibilités de déplacement).
- 1 classe "Plateau" ou "Joueur" qui s'occupe d'initialiser le plateau, et ensuite de déplacer les pièces à chaque coup. Cette classe pourra appeler autant d'instances de la classe "Piece" qu'il n'en faudra pour remplir le plateau.


Enfin c'est un avis :-°


Pour ton erreur regarde les lignes 5 et 10 dans ton programme echecMain :
p = Piece()
print(P.get_plateau()) #On veut voir le résultat

l'instance de classe "Piece" s'appelle "p" et pas "P" ! Si tu appelles une méthode de "P" tu n'auras qu'une erreur.
  • Partager sur Facebook
  • Partager sur Twitter
29 août 2010 à 16:38:15

Bonjour,


J'ai vu un truc que tu as dû mettre longtemps à écrire...
self.case = "a1a2a3a4a5a6a7a8b1b2b3b4b5b6b7b8c1c2c3c4c5c6c7c8d1d2d3d4d5d6d7d8 \
                     e1e2e3e4e5e6e7e8f1f2f3f4f5f6f7f8g1g2g3g4g5g6g7g8h1h2h3h4h5h6h7h8" #Toutes les cases de l'échiquier


Tu peux remplacer tout ça par quelque chose beaucoup plus simple, la méthode keys() des dicos.
self.case = ''.join(self.plateau.keys())


Tu peux également supprimer ton fichier echecMain.py en mettant ceci à la fin de echecClasse.py.

if __name__ == '__main__':

    #Mes variables.
    j = Joueur()
    p = Piece()

    #Le programme principal
    j.selectPiece()
    j.updatePlateau()
    print(p.get_plateau()) #On veut voir le résultat



PS: Codes non testés...
  • Partager sur Facebook
  • Partager sur Twitter
29 août 2010 à 23:59:23

Salut.
J'ai pas lu ton code, juste le commentaire suivant qui semble indiquer l'architecture de ton programme :
##
#Nombre de classes: (3)
#  - Mere
#  | - __init__()
#  | - updatePlateau()
#  |
#  - Piece(Mere)
#  | - __init__()
#  | - verifCoup()
#  | - bougerPiece()
#  | - get_plateau() 
#  |
#  - Joueur(Mere)
#  |  - __init__()
#  |  - property(_get_nom, _set_nom)
#  |  - selectPiece()


Plusieurs choses m'ont sauté aux yeux.

Pourquoi ne pas avoir créé une classe Plateau ?

Aux échecs, tu manipules des pièces sur un plateau. Ce dernier joue un rôle majeur lorsque tu veux déplacer une pièce : on se sert du plateau (de son état), par exemple, pour vérifier que l'on ne cherche pas à faire un mouvement illégal (bouger une pièce sur une case sur laquelle se trouve une autre pièce de la même couleur, bouger une pièce en dehors du plateau...). Il me semble que lorsque tu voudras implémenter les règles des échecs, tu auras nécessairement besoin de tenir compte de l'état du plateau : avoir un objet qui décrit cet état (et permet de le manipuler) me semble indispensable.

Au lieu de cela, je vois que la classe Piece possède une méthode get_plateau().

Ça semble mettre en évidence un défaut de conception. Si cette méthode est présente dans ta classe Piece, cela signifie que tes pièces encapsulent ton plateau. C'est contre-intuitif : ce n'est pas la pièce qui "a un" plateau, mais plutôt le plateau qui "contient des" pièces (la relation "a un" est grosso-modo la traduction de l'encapsulation).
Par quoi se caractérise une pièce aux échecs ? Sa manière de bouger (on y reviendra un peu plus tard). Est-ce qu'une pièce a besoin de connaître le plateau pour savoir "comment" elle doit bouger ? Non. On sait, par exemple, qu'un cavalier bougera toujours de deux cases dans une direction A, puis d'une case dans une direction B perpendiculaire à A. Ainsi, en donnant une paire de coordonnées à ta pièce, elle est capable de retourner toutes les positions "possibles" (sans parler de la légalité du mouvement) qu'elle peut atteindre en un coup. Est-ce à la pièce de vérifier la légalité de son mouvement ? Non. La pièce est l'élément le plus "petit" aux échecs, elle n'a pas à se préoccuper de ce qui l'entoure pour savoir comment elle doit agir : c'est le rôle de ce qui se trouve "au-dessus" de la pièce de décider que tel mouvement "possible" n'est pas légal parce qu'une autre pièce de même couleur se situe sur la case d'arrivée, ou qu'il atterrit en dehors du plateau.
Ainsi, ta classe Pièce n'a pas besoin d'avoir connaissance du plateau sur lequel elle se trouve (c'est l'inverse), ni des autres pièces qui se trouvent sur ce plateau ("l'état du cavalier en E6 n'a absolument aucune incidence sur l'état du pion en B5").
Donc, pour le coup, les méthodes actuelles de ta classe pièce sont toutes les trois inappropriées : elles devraient appartenir à un objet "de plus haut niveau", qui gère les pièces.

Piece et Joueur dérivent tous deux de la même classe Mere .

Pourquoi ? A quoi te sert de dériver ces deux classes de la même classe mère ?
Ni l'une ni l'autre ne précise le comportement de la classe mère : l'héritage ne sert à rien ici. Mais puisqu'on parle d'héritage, voici un petit exemple qui me semble pertinent, et qui pourrait te mettre sur une bonne piste :

# - Piece
# | - __init()__
# | - get_mouvements_possibles(x, y) 
# |       Retourne une liste des coordonnées (x,y) 
# |       que la pièce peut atteindre en un coup
# |
# | - ... autres méthodes communes à toutes les pièces
# |
# - Pion(Piece)
# | - __init__()
# | - get_mouvements_possibles(x, y)  [surcharge]
# |
# - Roi(Piece)
# | - __init__()
# | - get_mouvements_possibles(x, y)  [surcharge]
# |
#      ... etc pour chaque type de pièce ...
#


Là, on décrit une vraie relation d'héritage : un Pion "est une" Pièce, un Roi "est une" Pièce..
Cet héritage est justifié par le fait que chaque classe fille surcharge à sa manière la méthode get_mouvements_possibles(x, y) . En effet, chaque pièce bouge différemment, donc la méthode doit avoir un comportement différent suivant que l'on l'invoque sur une Tour ou sur un Fou...
Cependant, si la seule méthode implémentée par tes pièces est celle-ci, alors il n'y a pas besoin de définir une classe mère Piece (parce qu'on est en Python, et que le polymorphisme d'héritage n'a pas de raison d'exister dans un langage dynamique) : tant que toutes les pièces définissent la même interface publique (les mêmes méthodes), tu peux les utiliser toutes de la même manière...
Là où l'héritage devient indispensable en Python, c'est si toutes les Pièces avaient quelquechose en commun qu'elles feraient toutes de la même manière (donc un comportement qui n'aurait pas besoin d'être surchargé par les classes filles, et que l'héritage leur permettrait d'obtenir sans avoir à le réimplémenter à chaque fois).

Voilà... Je te laisse gamberger sur ces quelques remarques. Je t'encourage à modifier, chercher, réfléchir à comment améliorer ton design : son but est de rendre ton code plus facile à écrire/maintenir/faire évoluer, c'est quasiment la raison d'être de la POO.
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
30 août 2010 à 19:36:20

Bonjour,

Merci pour toutes vos réponses, c'est simpas. Ta réponse ma beaucoup intrigué et m'a bien fait comprendre que la POO n'est pas si facile que ça à saisir.

Pour en venir au jeu d'échec, il faut dire que je suis un peut perdu :( .
J'ai essayé de faire ce que tu m'as conseillé, mais il y aura des classes qui devront connaître des informations contenues dans d'autres classes (notamment dans <Plateau> qui devra connaître les coordonnées de toutes mes pièces). Je ne sais pas si telle-méthode devrait plutôt être dans cette classe-ci ou plutôt dans celle là.

Suis-je sur la bonne voie ?
Je ne sais jamais quoi mettre dans mes __init__, il y aurait il une astuce ?
Les deux nouvelles classes crées sont elles judicieuses ? (<Plateau>, <Juridique>)
Faut il utiliser un dico pour définir le plateau ?
Faut il définir un plateau ou au contraire ne pas en définir ? (On utilise uniquement les coordonnées des pièces)

Mon code:
# - Plateau
# | - __init()__
# | - get_etatCase()
# |
# - Juridique
# | - __init()__
# | - legalite(nomDeLaPiece, coordonnee)
# |
# - Piece
# | - __init()__
# | - g_mouv_possible(x, y) 
# |       Retourne une liste des coordonnées (x,y) 
# |       que la pièce peut atteindre en un coup
# |       dans la classe plateau
# |
# | - ... autres méthodes communes à toutes les pièces
# |
# - Pion(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
# - Tour(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
#      ... etc pour chaque type de pièce. La suite viendra une fois les deux finient ...
#

class Plateau(object):
    """Verifie l'état du plateau et les pièces présentes""" 
    def __init__(self):
        self.partieEnCours = True
        self.nbrPiece = 16
        #Je pense mettre ici mon plateau sous forme d'un dico. Mais il faut
        #bien que mon dico connaisse les coord de mes pièces :/

    def get_etatCase(self):
        """
        Retourne toutes les pièces présentes
        sur chacunes des cases de l'échiquier
        """
        return self.liste_etateCase #Retourne ce que contient les cases
        
    
#Fin de la classe   <Plateau>
#Début de la classe <Juridique>

class Juridique(object):
    """Au 'dessus' de la classe <Piece>. Gère les mouvements"""  
    def __init__(self):
        pass

    def legalite(self, nomDeLaPiece, coordonnee):
        """Est-ce un mouvement légale/possible ?"""
        pass
    
#Fin de la classe   <Juridique>
#Début de la classe <Piece>
    
class Piece(object): #Utile car le 'roi' est une pièce spéciale
    """Classe mère pour toutes les pièces du jeu"""   
    def __init__(self):
        pass
    
    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles"""
        pass
    
#Héritage des pièces de la classe <Piece> sur <Pion>
    
class Pion(Piece):
    """Rôle du 'pion'sur l'echequier"""
    def __init__(self, x = 1, y = 2):
        self.x = x #Icij'instensirai automatiquement ...
        self.y = y # ... mes variables en début de jeu

    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles d'un pion"""
        self.liste_coordPossible = []
        
        if y == 2:
            self.liste_coordPossible.append([x,y+1]) #On ajoute la case d'après.(+1)
            self.liste_coordPossible.append([x,y+2]) #Et l'autre case possible. (+2)          
        else:
            self.liste_coordPossible.append([x,y+1]) #Sinon on ajoute la case d'après.(+1)
            
        return self.liste_coordPossible #On retourne cette liste de coordonée possible


Toutes remarques/suggestions sont les bienvenues.
P.S: Là où il y a des 'pass' je ne sais encore quoi mettre.

Merci
  • Partager sur Facebook
  • Partager sur Twitter
30 août 2010 à 22:37:20

Citation : realmagma

Pour en venir au jeu d'échec, il faut dire que je suis un peut perdu :( .



Le jeu d'échec, je connais moyen. Mais en ne parlant que du script, si tu veux continuer sur cette structure là (qui n'est pas mal), tu peux par exemple :

- créer les pièces dans la méthode __init__ de la classe Plateau (tu peux par exemple créer une liste d'objets "Piece")
- définir les coordonnées de tes pièces (et leurs valeurs initiales) depuis la méthode __init__ de la classe "Piece" (il est inutile de définir les coordonnées de départ dans les classes dérivées de "Piece" SAUF si tu veux absolument qu'à chaque type de pièce correspondent des coordonnées spéciales - je t'ai dis je suis mauvais à ce jeu)
- déplacer toutes tes pièces depuis la classe Plateau par une méthode qui reste à écrire

Comme ça tu centralise le déplacement de toutes tes pièces, et tu peux appeler une liste des valeurs de toutes tes pièces pour n'importe-quelle variable, comme les coordonnées (pour chaque variable, ça peut se faire en une ligne).
Tu évites donc de créer un dictionnaire, qui tu as raison est chiant car tu devrais le mettre à jour pour chaque déplacement de chaque pièce...

Je te laisse tester et faire la suite ^^
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
30 août 2010 à 23:39:25

Je te conseille de regarder des fichiers sources, du genre pychess ou autres, ça te permettra d'avoir un aperçu de la difficulté.

  • Partager sur Facebook
  • Partager sur Twitter
31 août 2010 à 11:37:02

J'ai pensé à ceci: (Créer des instances dans la classe <Plateau>. J'obtiens directement les coordonnées des mes pièces. Le problème se trouve au niveau des mises à jour des coordonnées. Je cherche un moyen de contourner ce problème, je vous préviens dès que c'est bon :) )

class Plateau(object):
    """Verifie l'état du plateau et les pièces présentes""" 
    def __init__(self):
        """Initialisation des variables utiles + instances"""
        self.partieEnCours = True
        self.nbrPiece = 16

        pionA = Pion(1, 2) #(a;2)
        pionB = Pion(2, 2) #(b;2)
        #De même jusqu'à 'H' et pour chacune des pièces
        #Cela m'évite de créer un dico et à le mettre à jour

    def get_etatCase(self):
        """
        Retourne toutes les pièces présentes
        sur chacunes des cases de l'échiquier
        """
        return self.liste_etateCase #Retourne ce que contient les cases
        
    
#Fin de la classe   <Plateau>
#Début de la classe <Juridique>

class Juridique(Plateau): #Hérite de <plateau>


Citation : fred1599

Je te conseille de regarder des fichiers sources, du genre pychess ou autres, ça te permettra d'avoir un aperçu de la difficulté.



J'ai téléchargé le jeu, mais il n'y a aucunes sources :lol: .
Les avez-vous trouvé ?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
31 août 2010 à 11:42:34

Citation

J'ai téléchargé le jeu, mais il n'y a aucunes sources



Si si

A mon avis tu as eu peur de les regarder :)

  • Partager sur Facebook
  • Partager sur Twitter
31 août 2010 à 18:38:56

Hum, en effet ça fait très très très peur.
Je n'ai même pas pu compter le nombre de fichiers, tellement qu'il y en a.

Mon objectif: Pourvoir déplacer des pièces virtuelles sur l'échiquier suivant la pièce choisit. (Un cavalier se déplace en 'L', un pion se déplace qu'en vertical...) :p

Une fois fait, je m'attaquerai aux déplacements illégaux. (Je me répète, une fois cela fait) :lol:
  • Partager sur Facebook
  • Partager sur Twitter
1 septembre 2010 à 18:20:12

Citation : realmagma


Merci pour toutes vos réponses, c'est simpas. Ta réponse ma beaucoup intrigué et m'a bien fait comprendre que la POO n'est pas si facile que ça à saisir.



La POO, si, c'est simple : une classe définit des attributs responsables de son état et des méthodes responsables de son comportement. Tu ajoutes à ça quelques règles simples comme l'héritage et c'est parti, tu programmes objet.

Ce qui est (très) difficile et avant tout une question d'expérience, c'est designer correctement un programme orienté objet : faire en sorte que la manière dont tu écris et organises tes objets ait du sens et soit cohérent, facile à comprendre, à maintenir et à faire évoluer.

A ce propos il n'y a pas de design parfait, il n'y a que des motifs que l'on reconnait avec l'habitude, et dont on sait qu'ils fonctionnent (mais il est encore trop tôt pour que tu te penches sur les design patterns... tout leur intérêt éclate au grand jour quand tu te rends compte combien tu galérais avant de les utiliser, mais pour ça, eh bien il faut galérer et reconnaitre ses premiers motifs avant de les utiliser :D ).

Citation : realmagma


Pour en venir au jeu d'échec, il faut dire que je suis un peut perdu :( .



C'est normal, et plutôt bon signe : tu veux bien faire, mais, par manque d'expérience, tu as du mal à assembler toutes les notions qu'on t'envoie à la tronche et tu te retrouves bloqué sans avoir UNE marche à suivre idéale en tête. On passe tous par là (même avec de l'expérience, ça arrive encore... juste moins souvent).

Citation : realmagma


J'ai essayé de faire ce que tu m'as conseillé, mais il y aura des classes qui devront connaître des informations contenues dans d'autres classes (notamment dans <Plateau> qui devra connaître les coordonnées de toutes mes pièces).



Bien sûr. Ce que je dis, c'est simplement que ce n'est pas la responsabilité de la pièce de savoir si tel mouvement qu'elle peut faire est "légal" ou non : tout ce que la pièce devrait connaitre, c'est son type (roi, fou, tour...), sa couleur (blanc ou noir), et la manière dont elle peut bouger (x cases en horizontal et vertical pour une tour, 1 case en avant pour le pion, x cases dans n'importe quelle direction pour la reine). En gros, la pièce doit être "stupide" : si elle est en (x,y), l'information qu'elle doit pouvoir donner, c'est uniquement les cases qui lui sont accessibles "dans l'absolu" depuis sa position (mais qui ne le sont peut-être pas dans le cadre de la partie parce qu'une autre pièce lui barre le chemin, par exemple). Après, elle peut passer cette information à n'importe qui le lui demande (par sa méthode get_mouvements_possibles), c'est juste elle qui fait le calcul.

Citation : realmagma


Je ne sais pas si telle-méthode devrait plutôt être dans cette classe-ci ou plutôt dans celle là.



Pense en termes de responsabilités.
Si tu as une classe "chien", est-ce sa responsabilité de "rentrer le chien dans la maison", ou bien plutôt de "Courser le facteur" ?
Dans le premier cas, non, c'est la responsabilité de son maître (c'est lui qui décide de rentrer le chien ou pas), pas du chien. Dans le second cas, oui, c'est "ce que fait" un chien : il course le facteur et il le bouffe.

Citation : realmagma


Suis-je sur la bonne voie ?
Je ne sais jamais quoi mettre dans mes __init__, il y aurait il une astuce ?


Dans la méthode __init__, tu initialises les attributs qui décrivent l'état de ton objet... Pour une pièce, par exemple, tu initialiserais la couleur, le nom...

Citation : realmagma


Les deux nouvelles classes crées sont elles judicieuses ? (<Plateau>, <Juridique>)


Oui, ( :lol: pour le Juridique, j'aurais appelé ça "Jeu" puisque c'est ce qui contient les règles, mais l'idée est la bonne.).

Citation : realmagma


Faut il utiliser un dico pour définir le plateau ?



Ça, c'est un choix, un détail d'implémentation : c'est à toi de le faire.

Citation : realmagma


Faut il définir un plateau ou au contraire ne pas en définir ? (On utilise uniquement les coordonnées des pièces)



Personnellement j'en utiliserais un, de manière à accéder rapidement aux coordonnées relatives des pièces, et je m'en servirais quand je vérifierais la légalité des mouvements : le plateau servirait à "stocker" les pièces.
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
2 septembre 2010 à 17:24:30

Bonjour,

Merci Nohar pour ces explications que j'apprécie et m'aide beaucoup :)

Citation : Nohar

une classe définit des attributs responsables de son état et des méthodes responsables de son comportement.


Une petite question: Je ne comprends pas trop ce que tu veux dire dans "responsable de son état". Aurais-tu un exemple quelconque à me donner s'il te plaît ?

Il y a du nouveau :D

En admettant que le joueur place un pion sur l'échiquier grâce à deux coordonnées (x;y), mon programme est capable de voir les déplacement possibles de celui-ci:

from echecClasse import *

j = Piece()
j.g_mouvPossible(9, 9) #Que peut t'il faire en (9;9) ?
#-----------------------Il peut aller en (9;10)

j.g_mouvPossible(2, 2)
#-----------------------Il peut aller en (2;3) ou en (2;4)


# - Plateau
# | - __init()__
# | - get_etatCase()
# |
# - Juridique
# | - __init()__
# | - legalite(nomDeLaPiece, coordonnee)
# | - choicirPion()
# |
# - Piece
# | - __init()__
# | - g_mouv_possible(x, y) 
# |       Retourne une liste des coordonnées (x,y) 
# |       que la pièce peut atteindre en un coup
# |       dans la classe plateau
# |
# | - ... autres méthodes communes à toutes les pièces
# |
# - Pion(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
# - Tour(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
#      ... etc pour chaque type de pièce. La suite viendra une fois les deux finient ...
#

class Plateau(object):
    """Verifie l'état du plateau et les pièces présentes""" 
    def __init__(self):
        self.partieEnCours = True
        self.nbrPiece = 16

    def get_etatCase(self):
        """
        Retourne toutes les pièces présentes
        sur chacunes des cases de l'échiquier
        """
        return self.liste_etateCase #Retourne ce que contient les cases
        
    
#Fin de la classe   <Plateau>
#Début de la classe <Juridique>

class Juridique(Plateau):
    """
    Au 'dessus' de la classe <Piece>.Gère les mouvements"""  
    def __init__(self):
        """
        Initialise diférentes variables
        telle qu'une liste de booléens

        """
        pass
       
    def legalite(self, nomDeLaPiece, coordonnee):
        """Est-ce un mouvement légale/possible ?"""
        pass

    def choisirPion(self):
        """Selectionne un pièce pour le joueur"""
        #Retourne les coordonnées demandées
        pass
    
#Fin de la classe   <Juridique>
#Début de la classe <Piece>
    
class Piece(object): #Utile car le 'roi' est une pièce spéciale
    """Classe mère pour toutes les pièces du jeu"""   
    def __init__(self):
        """
        Instantation des pièces
        ainsi que leur coordonnée
        
        """
        self.x, self.y = 0, 0
        self.pionA = Pion(self.x+1, self.y+2) #(a;2)
        self.pionB = Pion(self.x+2, self.y+2) #(b;2)
    
    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles"""
        self.mouvPossible = self.pionA.g_mouv_possible(x, y)
        print(self.mouvPossible)
        
    
#Héritage des pièces de la classe <Piece> sur <Pion>
    
class Pion(Piece):
    """Rôle du 'pion'sur l'echequier"""
    def __init__(self, x , y, couleur = True):
        """
        Pour l'instant gère la couleur du pion
        True = Blanc; False = Noir

        """
        self.couleur = couleur 
        
    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles d'un pion"""
        self.liste_coordPossible = [] #On a besoin d'une liste
        
        if y == 2:
            self.liste_coordPossible.append([x,y+1]) #On ajoute la case d'après.(+1)
            self.liste_coordPossible.append([x,y+2]) #Et l'autre case possible. (+2)          
        else:
            self.liste_coordPossible.append([x,y+1]) #Sinon on ajoute la case d'après.(+1)
           
        return self.liste_coordPossible #On retourne cette liste de coordonée possible
  • Partager sur Facebook
  • Partager sur Twitter
2 septembre 2010 à 23:29:29

Pas mal, mais la lourdeur que je t'avais signalée est de nouveau là : ta classe Piece créée des objets de classe Pion qui...sont dérivés de la classe Piece.

La méthode __init__ de Piece se contente de créer 2 pions. Ce que la méthode __init__ de Plateau saurait très bien faire, avec l'avantage :

- de centraliser la création de ton plateau...dans la classe Plateau (le nombre de pièces et la partie en cours y sont déjà initialisés).

- d'établir un lien clair de dépendance entre Piece et Pion, qui serait alors une classe dérivée de Piece et point final. Crois moi après 2H de débogage tu en auras marre de te demander quelle classe fait quoi à quelle autre classe. Des liens clairs et simples sont donc bienvenus ;)
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 10:43:22

Citation : realmagma


Une petite question: Je ne comprends pas trop ce que tu veux dire dans "responsable de son état". Aurais-tu un exemple quelconque à me donner s'il te plaît ?



Les attributs d'un objet décrivent son état, et ses méthodes ce que l'objet "fait" en fonction de son état. Un exemple simple:

class Bisounours(object):
    def __init__(self):
        self.etat = 'à peine initialisé'

    def dire_etat(self):
        print('Je suis {0}.'.format(self.etat))

    def recevoir_bisou(self):
        print('Oh, super !')
        self.etat = 'content'

    def recevoir_coup_de_pied_aux_fesses(self):
        print('Aïe !')
        self.etat = 'blessé'

    def recevoir_cadeau(self):
        print("C'est pour moi ? merci !")
        self.etat = 'touché'

    def recevoir_balle_dans_la_tete(self):
        print('>_<...')
        self.etat = 'mort'


>>> b = bisounours.Bisounours()
>>> b.dire_etat()
Je suis à  peine initialisé.
>>> b.recevoir_bisou()
Oh, super !
>>> b.dire_etat()
Je suis content.
>>> b.recevoir_coup_de_pied_aux_fesses()
Aïe !
>>> b.dire_etat()
Je suis blessé.
>>> b.recevoir_cadeau()
C'est pour moi ? merci !
>>> b.dire_etat()
Je suis touché.
>>> b.recevoir_balle_dans_la_tete()
>_<...
>>> b.dire_etat()
Je suis mort.
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
3 septembre 2010 à 11:17:06

Citation : Lexileduval

Pas mal, mais la lourdeur que je t'avais signalée est de nouveau là : ta classe Piece créée des objets de classe Pion qui...sont dérivés de la classe Piece.

La méthode __init__ de Piece se contente de créer 2 pions. Ce que la méthode __init__ de Plateau saurait très bien faire, avec l'avantage :

- de centraliser la création de ton plateau...dans la classe Plateau (le nombre de pièces et la partie en cours y sont déjà initialisés).

- d'établir un lien clair de dépendance entre Piece et Pion, qui serait alors une classe dérivée de Piece et point final. Crois moi après 2H de débogage tu en auras marre de te demander quelle classe fait quoi à quelle autre classe. Des liens clairs et simples sont donc bienvenus ;)



Je comprends clairement ce que tu me dis mais le problème c'est que si je fais comme ça, ma classe <Plateau> ne pourra pas connaître la valeur renvoyée par ma classe <Pion>. Il n'y aura pas de lien. Admettons que j'en crée un (Déjà comment faire? Polymorphisme/héritage?) ce sera aussi le 'buic-buic' (bazar) que maintenant.

Dites-moi si je me trompe hein, n'hésitez surtout pas, bien au contraire ;) .
:euh: Help ?
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 11:37:34

D'une manière générale, une classe mère n'a pas à connaitre ses classes filles, c'est l'inverse.

Une classe mère a un comportement "de base", et c'est tout. Ses classes filles modifient/précisent le comportement de base : elles peuvent se substituer à la classe mère, elles feront le même boulot qu'elle, mais différemment.

En reprenant l'exemple précédent, je définis deux classes qui héritent de Bisounours :

class BisounoursSM(Bisounours):
    def __init__(self):
        Bisounours.__init__(self)

    def recevoir_coup_de_pied_aux_fesses(self):
        print('Oh oui, encore !')
        self.etat = 'content'

class BisounoursEmo(Bisounours):
    def __init__(self):
        Bisounours.__init__(self)

    def recevoir_bisou(self):
        print("Peuh ! Garde ça pour toi!")
        self.etat = 'indifférent'

    def recevoir_balle_dans_la_tete(self):
        print('Enfin ! Adieu monde ingrat!')
        Bisounours.recevoir_balle_dans_la_tete(self)


>>> b = bisounours.Bisounours()
>>> s = bisounours.BisounoursSM()
>>> e = bisounours.BisounoursEmo()
>>> b.recevoir_bisou()
Oh, super !
>>> e.recevoir_bisou()
Peuh ! Garde ça pour toi!
>>> b.dire_etat()
Je suis content.
>>> e.dire_etat()
Je suis indifférent.
>>> e.recevoir_coup_de_pied_aux_fesses()
Aïe !
>>> s.recevoir_coup_de_pied_aux_fesses()
Oh oui, encore !
>>> e.dire_etat()
Je suis blessé.
>>> s.dire_etat()
Je suis content.
>>> e.recevoir_balle_dans_la_tete()
Enfin ! Adieu monde ingrat!
>_<...
>>> e.dire_etat()
Je suis mort.


Comme tu le vois, les deux classes filles peuvent se substituer au bisounours : ils font la même chose, juste de manière différente. Le plus important, c'est qu'À AUCUN MOMENT je n'ai dû modifier la classe mère pour prendre en compte les classes filles.

Dans ton cas, si Pion, Cavalier, etc. dérivent de Pièce, alors tu les utiliseras exactement comme des Pièces dans le reste du code. Ils agiront juste différemment, en fonction de leur classe : le code qui les utilise se fout de "comment" la pièce agit (comme un pion ? comme une tour ?), tout ce qu'il veut c'est que le boulot qu'il demande à la pièce soit fait.

EDIT : Dans ton code, les classes Piece et Pion sont définies comme si Piece était plus importante, avait plus de responsabilité que Pion. Ce n'est pas le cas. L'héritage ne traduit pas une hiérarchie dans les responsabilités mais plutôt dans la "généralisation" : les classes dérivées ont les mêmes responsabilités que leur classe mère, elles doivent faire exactement le même boulot, ni plus, ni moins, elles ont juste la possibilité d'être plus spécifiques que leur classe mère (donc moins "générales").
La relation qui introduit une hiérarchie dans les responsabilités serait plutôt l'encapsulation : si une classe en encapsule une autre, c'est pour pouvoir lui déléguer le boulot qu'elle ne sait pas faire. En réalité ce n'est pas toujours une relation hiérarchique, mais plutôt une relation "client/fournisseur de service" : la classe encapsulée fournit un service à la classe qui l'encapsule. Quand la deuxième a besoin de faire un boulot qu'elle ne sait pas faire, elle demande à la première de le faire, (se moque complètement de la manière dont c'est fait), et récupère le résultat pour l'utiliser.
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
3 septembre 2010 à 14:44:06

Tu m'éclaircis des zones d'ombres, là où la lumière ne pouvait passer. Je t'encourage à continuer, je sens que ça vient :) .

Pour faire bref, il ne faut pas considérer l'héritage comme un sorte de hiérarchisation des classes. Une classe Mère est un Moule pour les classes Filles. Les classes Filles permettent uniquement de préciser ce que font les méthodes de la classe Mère; une sorte de sous-traitance :D .
(Encore une fois, dis moi si je me trompe ou si j'ai mal résumé ce que tu essayes de me faire comprendre)

J'ai cherché dans les tutoriels sur Python dans ce site ainsi que sur internet mais mes recherches m'ont plus embrouillés l'esprit qu'autre chose.

Qu'est-ce que le polymorphisme ?
  • -polymorphisme d'inclusion
  • -polymorphisme ad-hoc
  • -polymorphisme paramétrique



Il y a t'il un lien avec l'héritage ?
Quelle différence il y a t'il entre Héritage et polymorphisme ?

Pour en revenir au échec, Lexileduval m'a suggéré d'instancier mes objets dans la classe <Plateau>. Ses arguments tiennent la route, mais il m'est impossible de faire fonctionner ma méthode 'g_mouvPossible' de ma classe <Pion>. Il y a un problème. Il faut un lien entre ces deux classes... (Je veux dire par là qu'il faut bien que les informations circulent entre les différentes classes)

Je suis perdu :( , et je n'aime pas être perdu & ne pas comprendre & de ne pas y arriver.
Je veux apprendre, je dirais même plus, j'ai soif d'apprendre. :D

P.S: Le passage avec Lexileduval est à comprendre 'tes' pour... Lexiduval :D


En espérant ne rien avoir oublié (même si ce n'est pas le cas). Je vous remercie encore une fois
Realmagma.
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 15:16:34

L'idée qu'il essayait de faire passer (je pense, possible que je me trompe, et ça m’étonnerait pas), c'est que ta classe plateau, lors de son initialisation, initialise ses pièces aussi. C'est pas une relation d'héritage, mais d'encapsulation (je pense que c'est ce concept qu'essayait d'expliquer NoHaR dans son edit, mais là je me trompe peut-être aussi. Je suis pas trop à l'aise avec ça non plus). Vu que tes pièces ont été instanciées dans ta classe Plateau, lorsque tu utilises plateau, tu peux appeler les méthodes de tes pièces (mouvement_possible, etc.).

Je suis sans doute pas très clair, et n'étant pas super à l'aise avec la POO, il est possible que je dise des erreurs. Mais c'est comme ça que j'aurai vu le truc personnellement.
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 16:15:20

Citation : Kazy

L'idée qu'il essayait de faire passer (je pense, possible que je me trompe, et ça m’étonnerait pas), c'est que ta classe plateau, lors de son initialisation, initialise ses pièces aussi. C'est pas une relation d'héritage, mais d'encapsulation (je pense que c'est ce concept qu'essayait d'expliquer NoHaR dans son edit, mais là je me trompe peut-être aussi. Je suis pas trop à l'aise avec ça non plus). Vu que tes pièces ont été instanciées dans ta classe Plateau, lorsque tu utilises plateau, tu peux appeler les méthodes de tes pièces (mouvement_possible, etc.).

Je suis sans doute pas très clair, et n'étant pas super à l'aise avec la POO, il est possible que je dise des erreurs. Mais c'est comme ça que j'aurai vu le truc personnellement.



En faite le problème vient surtout du fait que je n'arrive pas à faire circuler une information d'une classe à une autre sans utiliser l'héritage. Cependant, j'ai essayé d'instancier mes objets dans ma classe <Plateau>.
Voici le résultat:

# - Plateau
# | - __init()__
# | - get_etatCase()
# |
# - Juridique
# | - __init()__
# | - legalite()
# | - choisirPion()
# | - choisirDestination()
# | - bouger()
# |
# - Piece
# | - __init()__
# | - g_mouv_possible(x, y) 
# |       Retourne une liste des coordonnées (x,y) 
# |       que la pièce peut atteindre en un coup
# |       dans la classe plateau
# |
# | - ... autres méthodes communes à toutes les pièces
# |
# - Pion(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
# - Tour(Piece)
# | - __init__()
# | - g_mouv_possible(x, y)  [surcharge]
# |
#      ... etc pour chaque type de pièce. La suite viendra une fois les deux finient ...
#

class Plateau(object):
    """Verifie l'état du plateau et les pièces présentes""" 
    def __init__(self):
        """
        Instancie les pièces de l'échiquier
        ainsi que les informations du plateau

        """
        self.partieEnCours = True
        self.nbrPiece = 16

        self.x = 0
        self.y = 0
        
        self.pionA = Pion(self.x+1, self.y+2) #(a;2)
        self.pionB = Pion(self.x+2, self.y+2) #(b;2)
        
    def get_etatCase(self):
        """
        Retourne toutes les pièces présentes
        sur chacunes des cases de l'échiquier
        """
        return self.liste_etateCase #Retourne ce que contient les cases
        
    
#Fin de la classe   <Plateau>
#Début de la classe <Juridique>

class Juridique(Plateau):
    """
    Au 'dessus' de la classe <Piece>.Gère les mouvements"""  
    def __init__(self):
        """
        Initialise diférentes variables
        telle qu'une liste de booléens

        """
        Plateau.__init__(self)
        pass
       
    def legalite(self, nomDeLaPiece, coordonnee):
        """Est-ce un mouvement légale/possible ?"""
        #Si le déplacement est possible
        #return True
        pass

    def choisirPion(self):
        """Selectionne un pièce pour le joueur"""
        self.caseX = input("Choisissez une abscisse")
        self.caseY = input("Choisissez une ordonnée")
        return self.caseX, self.caseY

    def choisirDestination(self):
        """Ou voulons-nous bouger la pièce ?"""
        self.destinationX = input("Choisissez une abscisse de destination")
        self.destinationY = input("Choisissez une ordonnée de destination")
        #Appel de la fonction légalité
        #Si True alors on modifie les position
        #On modifie donc le plateau

    def bouger(self):
        """
        Permet de bouger une pièce:
        1) Appel la fonction choisirPion()
        2) Appel de la fonction choisirDestination()
        2,A) - Appel de la fonction legalite()
        2,B) - Le déplacement s'effectue
          """
        self.choisirPion()
        self.choisirDestination()

        
#Fin de la classe   <Juridique>
#Début de la classe <Piece>
    
class Piece(object): #Utile car le 'roi' est une pièce spéciale
    """Classe mère pour toutes les pièces du jeu"""   
    def __init__(self):
        """
        Ne sert à rien
        pour le moment
        
        """
        pass
    
    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles"""
        self.mouvPossible = self.pionA.g_mouv_possible(x, y)
        
    
#Héritage des pièces de la classe <Piece> sur <Pion>
    
class Pion(Piece):
    """Rôle du 'pion'sur l'echiquier"""
    def __init__(self, x , y, couleur = True):
        """
        Pour l'instant gère la couleur du pion
        True = Blanc; False = Noir

        """
        self.couleur = couleur 
        
    def g_mouv_possible(self, x, y):
        """Permet de voir les déplacements possibles d'un pion"""
        self.liste_coordPossible = [] #On a besoin d'une liste
        
        if y == 2:
            self.liste_coordPossible.append([x,y+1]) #On ajoute la case d'après.(+1)
            self.liste_coordPossible.append([x,y+2]) #Et l'autre case possible. (+2)          
        else:
            self.liste_coordPossible.append([x,y+1]) #Sinon on ajoute la case d'après.(+1)
          
        return self.liste_coordPossible #On retourne cette liste de coordonée possible


Qu'en pensez-vous?
Pourriez-vous répondre aux questions de mon précédant poste aussi ? :p
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 16:19:07

Citation : realmagma

Je t'encourage à continuer, je sens que ça vient :) .



:euh: Je savais pas que je faisais ce genre d'effets...

Citation : realmagma


Pour faire bref, il ne faut pas considérer l'héritage comme un sorte de hiérarchisation des classes.



Voilà.

Citation : realmagma


Une classe Mère est un Moule pour les classes Filles. Les classes Filles permettent uniquement de préciser ce que font les méthodes de la classe Mère;



Oui. Tu peux dériver un "sifflet" en "sifflet à clochettes", ça reste un sifflet et il continue de s'utiliser pareil, sauf qu'en plus il a des clochettes.
Pour vérifier si tu as dérivé une classe correctement ou pas il suffit de lui appliquer le principe de substitution. Si tu es capable d'utiliser la classe fille au lieu de la classe mère dans le code (en changeant juste la création de l'objet), et que celui-ci continue de fonctionner normalement, c'est que l'héritage est réussi. Si, quand tu remplaces une classe par l'une de ses classes dérivées dans un code, le programme ne fonctionne plus, alors c'est qu'il y a un problème.

Citation : realmagma


une sorte de sous-traitance :D .



Non, ça c'est l'encapsulation (le principe de déléguer).

Citation : realmagma


Qu'est-ce que le polymorphisme ?


Le fait d'avoir plusieurs objets différents qui s'utilisent de la même manière. Ou de définir une fonction qui s'utilisera de la même manière avec des types d'objets différents.

Par exemple, je peux faire une fonction "ajouter", qui est polymorphe (parce que l'opérateur + est polymorphe), et l'utiliser indifféremment avec des chaines de caractères, des entiers ou des flottants, des listes:
>>> def ajouter(a, b):
...     return a+b
... 
>>> ajouter("Hello", " World")
'Hello World'
>>> ajouter(3, 4)
7
>>> ajouter(2.5643, 42.5678)
45.132100000000001
>>> ajouter(['a', 'b', 'c'], ['d'])
['a', 'b', 'c', 'd']


Citation : realmagma

polymorphisme d'inclusion



Une notion plutôt abstraite qui s'exprime par le polymorphisme d'héritage (concrètement, le principe de substitution des classes filles à leur classe mère est une application du polymorphisme d'héritage), qui lui-même n'est pas une notion pertinente en Python puisque c'est un langage dynamique.

Citation : realmagma

polymorphisme ad-hoc



C'est le fait de surcharger une fonction pour l'utiliser avec des objets de types différents est une expression du polymorphisme ad-hoc. Quand tu surcharges, dans une classe fille, une méthode héritée de sa classe mère, c'est du polymorphisme ad-hoc.

Citation : realmagma

polymorphisme paramétrique



Encore un type de polymorphisme qui ne s'utilise pas en Python.
Ça se trouve surtout en C++ (ou en D) avec le mécanisme des templates, ou en Java avec les generics.

Citation : realmagma

Quelle différence il y a t'il entre Héritage et polymorphisme ?



Quand tu dérives une classe, la classe dérivée (fille) hérite de toutes les méthodes de sa classe de base (mère) : c'est l'héritage.
Dans le reste du code, tu peux utiliser la classe fille exactement de la même manière que tu utilises sa classe mère : c'est du polymorphisme.

Citation : realmagma


Pour en revenir au échec, Lexileduval m'a suggéré d'instancier mes objets dans la classe <Plateau>. Ses arguments tiennent la route, mais il m'est impossible de faire fonctionner ma méthode 'g_mouvPossible' de ma classe <Pion>. Il y a un problème.



Comment ça, c'est impossible ?
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
3 septembre 2010 à 16:43:34

Citation : Nohar

Comment ça, c'est impossible ?



Je m'auto-cite vu que j'ai écris mon message quelques secondes avant le tient: :D


Citation : realmagma

En faite le problème vient surtout du fait que je n'arrive pas à faire circuler une information d'une classe à une autre sans utiliser l'héritage.



Il y a la solution 'variables globales' mais bon je n'ai pas bien en utiliser.

P.S:

Citation : Nohar & realmagma

R: "Je t'encourage à continuer, je sens que ça vient."
N: "Je savais pas que je faisais ce genre d'effets..."


==> :lol:
  • Partager sur Facebook
  • Partager sur Twitter
3 septembre 2010 à 16:46:01

Comme l'a dit Kazy tout à l'heure, si tu instancies tes pièces dans ton plateau (que tu les stockes dedans, donc que le plateau encapsule les pièces), alors il peut tout à fait accéder à leurs informations...

  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
Anonyme
3 septembre 2010 à 17:35:13

Citation

Comme l'a dit Kazy tout à l'heure, si tu instancies tes pièces dans ton plateau (que tu les stockes dedans, donc que le plateau encapsule les pièces), alors il peut tout à fait accéder à leurs informations...



Voir mon 1er post :p
  • Partager sur Facebook
  • Partager sur Twitter
4 septembre 2010 à 10:16:17

Bon, pour t'aider (parce que je sens que tu galères un peu sur le design), je vais te faire un fichier-type, avec un design possible. Tu n'auras plus qu'à coder le contenu des méthodes (le plus important).

J'éditerai plus tard.

Edit :
Bon... en fait, je pense que j'avais parlé un peu vite pour les pièces (le fait de les rendre "stupides") :
En réfléchissant à la manière dont le jeu pourra fonctionner, je me suis aperçu qu'il n'y avait que 2 façons de gérer les règles subtiles du déplacement des pièces :
- soit la pièce est "stupide", fait le minimum syndical (vérifie grosso-modo qu'elle peut géométriquement accomplir un déplacement en un coup ou non), et le reste (légalité du mouvement) est géré par le plateau ou la classe qui contient les règles du jeu.
- soit la pièce est "intelligente", se comporte en véritable soldat, et sait ce qu'elle peut faire (donc elle le vérifie elle-même).

Dans le premier cas, cela permet, niveau design, de créer une hiérarchie entre les pièces et le plateau, mais la gestion (le renforcement) des règles devient beaucoup plus moche (obligé de coller un branchement conditionnel dans une méthode énorme, ce qui tue le polymorphisme à ce niveau et crée un design extrêmement rigide). Je me suis donc tiré une balle dans le pied.

Dans le second cas, le plateau et les pièces sont couplés un peu plus fortement, mais les règles de déplacement / d'attaque sont mieux encapsulées (plus faciles à gérer), le polymorphisme est plus présent dans les classes "de plus haut niveau" (les classes de plus haut niveau sont moins prise de tête à coder), et le jeu plus facile à maintenir.

Au temps pour moi, donc, je ne vais pas suivre mon propre conseil : chaque pièce contiendra ses règles de déplacement, et devra pour ça avoir une connaissance de son environnement (le plateau). :p

Edit2:
Voilà.
J'ai implémenté quelques méthodes de bas niveau, et laissé le squelette pour les principales autres méthodes.

L'organisation conseillée est la suivante:
Au début de la partie, tu crées un objet de la classe Jeu (qui va lui-même instancer un Echiquier, qui va lui-même instancer des Pieces et les placer au bon endroit).
Pour chaque coup, tu sélectionnes une pièce au moyen de la méthode selectionner_piece de la classe Jeu (non implémentée : elle devra vérifier que la pièce appartient bien au joueur courant...), puis tu joues une case (non implémenté : il faudra vérifier que la case est vide ou bien qu'elle contient une pièce adverse). Si la case est vide, tu appelles la méthode mouvement de la classe Echiquier, qui va vérifier que la pièce a le droit de faire ce mouvement (en délégant le travail de vérification à la pièce. Pour ce faire, la pièce a la possibilité, par exemple accéder à son attribut __echiquier, et utiliser sa méthode libre(x, y)...). Si la case contient une pièce adverse, c'est un peu le même principe...

Pour afficher l'échiquier, comme je l'ai fait dans le "main", il suffit de faire print(echiquier) , ça te donnera dans la console un résultat qui ressemble à ceci :

Image utilisateur

Voilà, tu n'as plus qu'à coder tout le reste : en particulier surcharger les méthodes qui vont bien dans les classes dérivées de Piece.
Évidemment, il te faudra sûrement rajouter des méthodes dans tes classes, ou des classes/fonctions supplémentaires pour gérer l'exécution principale du programme, mais je pense que c'est déjà une bonne base.

voici le code :


# -*- coding: utf-8 -*-

from __future__ import print_function

###############################################################################
# fonctions utilitaires et constantes globales

NOIR  = 0
BLANC = 1

VIDE = None

SYMBOLE_PION = '*'
SYMBOLE_TOUR = 'T'
SYMBOLE_CAVALIER = 'C'
SYMBOLE_FOU = 'F'
SYMBOLE_REINE = '&'
SYMBOLE_ROI = '@'

COULEUR_SURLIGNE = '\033[7m'
COULEUR_STOP = '\033[0m'

def surligne(texte):
    """
    Surligne le texte dans la console (affiche en noir sur blanc)
    Fonctionne sur toutes les plate-formes 
    (sous windows, il faut s'assurer que ansi.sys est activé)

    """
    return '{0}{1}{2}'.format(COULEUR_SURLIGNE, texte, COULEUR_STOP)

###############################################################################

class Piece(object):
    """
    Interface commune à toutes les pièces.
    Ne s'utilise pas directement.

    """
    def __init__(self, echiquier, couleur, symbole):
        """
        Construit une pièce.
        echiquier : echiquier sur lequel se trouve la pièce
        couleur : NOIR ou BLANC.
        symbole : caractère symbolisant la pièce.
        
        """
        self.__echiquier = echiquier
        self.__couleur = couleur
        self.__symbole = symbole

    def get_mouvements_possibles(self, pos_x, pos_y): 
        """ 
        Retourne la liste des cases que peut atteindre la pièce sachant qu'elle
        se trouve actuellement en (pos_x, pos_y). 
        
        """ 
        # return []
        raise NotImplementedError(
            "{0}.get_mouvements_possibles".format(self.nom)
            )

    def is_mouvement_possible(self, pos_x, pos_y, dest_x, dest_y):
        """
        Retourne True si la pièce, actuellement (pos_x, pos_y) peut se
        déplacer vers (dest_x, dest_y) en un tour.

        """
        # Il est possible, suivant le type de la pièce, d'utiliser des règles
        # de calcul plus efficaces que de parcourir linéairement la liste des
        # mouvements possibles... 
        return (dest_x, dest_y) in self.get_mouvements_possibles(pos_x, pos_y)

    def get_attaques_possibles(self, pos_x, pos_y):
        """
        Retourne une liste des mouvements d'attaque possible.
        Concrêtement, la pièce peut se déplacer différemment quand elle attaque
        ou quand elle bouge simplement (le pion, par exemple).
        Cette méthode permet donc de retourner les cases sur lesquelles la
        pièce peut attaquer.

        """
        # cas général, la pièce attaque exactement comme elle bouge
        return self.get_mouvements_possibles(pos_x, pos_y)

    def is_attaque_possible(self, pos_x, pos_y, dest_x, dest_y):
        """
        True si la pièce peut attaquer la case dest_x, dest_y depuis pos_x,
        pos_y.
        False sinon.
        """
        return (dest_x, dest_y) in self.get_attaques_possibles(pos_x, pos_y)

    @property
    def nom(self):
        """
        Retourne le nom de la pièce sous forme de chaîne de caractères.

        Par exemple:
        >>> p = Reine()
        >>> p.nom
        'Reine'
        """
        return type(self).__name__

    @property
    def couleur(self):
        return self.__couleur
    
    def __str__(self):
        """
        Retourne une représentation de la pièce et de sa couleur sur un
        caractère (Noir sur blanc si la pièce est noire, blanc sur noir si la
        pièce est blanche).
        """
        return surligne(self.__symbole) if self.__couleur == NOIR \
                else self.__symbole

    def __repr__(self):
        return "Piece({0}, {1})".format(self.nom, self.couleur)

###############################################################################
# classes dérivées de Piece

class Tour(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_TOUR)

class Pion(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_PION)

class Cavalier(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_CAVALIER)

class Fou(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_FOU)

class Reine(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_REINE)

class Roi(Piece):
    def __init__(self, echiquier, couleur):
        Piece.__init__(self, echiquier, couleur, SYMBOLE_ROI)

###############################################################################

POS_PIECES = [Tour, Cavalier, Fou, Roi, Reine, Fou, Cavalier, Tour]

class Echiquier(object):
    """
    Echiquier. 
    Tient à jour l'échiquier de la partie : l'état du jeu
    """
    def __init__(self):
        """ 
        Initialise l'échiquier en plaçant les pièces à leur position initiale.

        """
        self.__plateau = [[VIDE for _ in range(8)] for _ in range(8)]
        # initialisation des pions
        for x in range(8):
            self.__plateau[x][0] = POS_PIECES[x](self, BLANC)
            self.__plateau[x][1] = Pion(self, BLANC)
            self.__plateau[x][6] = Pion(self, NOIR)
            self.__plateau[x][7] = POS_PIECES[7-x](self, NOIR)

        # ...
    
    def get(self, x, y):
        """
        Retourne la pièce à la position x, y

        """
        return self.__plateau[x][y]

    def put(self, piece, x, y):
        """
        Place une pièce à la position x, y

        """
        self.__plateau[x][y] = piece

    def libre(self, x, y):
        """
        retourne True si la case aux coordonnées (x, y) est libre

        """
        return self.piece_at(x, y) is VIDE

    def mouvement(self, pos_x, pos_y, dest_x, dest_y):
        """
        Bouge la pièce aux coordonnées pos_x, pos_y vers la position dest_x,
        dest_y.

        """
        if not self.libre(pos_x, pos_y):
            piece = self.get(pos_x, pos_y)
            if piece.is_mouvement_possible(pos_x, pos_y, dest_x, dest_y):
                self.put(piece, dest_x, dest_y)
                self.put(VIDE, pos_x, pos_y)

    def attaque(self, pos_x, pos_y, dest_x, dest_y):
        """
        Attaque la pièce en position (dest_x, dest_y) avec la pièce en position 
        (pos_x, pos_y)

        """
        raise NotImplementedError('Plateau.attaque')

    def __str__(self):
        """
        Retourne une représentation de l'échiquier affichable dans la console.

        """
        str_plateau = ''
        for yc in range(23,-1,-1):
            y = yc // 3
            if yc % 3 == 1:
                str_plateau += ' ' + str(y+1) + '  '
            else:
                str_plateau += '    '

            for xc in range(48):
                x = xc // 6
                if xc % 6 == 2 and yc % 3 == 1:
                    piece = self.__plateau[x][y]
                    if piece is not None:
                        str_plateau += str(piece)
                    else:
                        str_plateau += surligne(' ') if (x + y) % 2 else ' '
                else:
                    str_plateau += surligne(' ') if (x + y) % 2 else ' '
            str_plateau += '\n'
        str_plateau += '\n      A     B     C     D     E     F     G     H\n'
        return str_plateau

###############################################################################

class Jeu(object):
    """
    Classe permettant de gérer une partie.

    """
    def __init__(self):
        """
        initialise une partie.

        """
        self.selection_x = -1
        self.selection_y = -1
        self.tour_actuel = BLANC
        self.echiquier = Echiquier()
        # ...

    def selectionner_piece(self, pos_x, pos_y):
        """
        Sélectionne la pièce avec laquelle jouer.
        Vérifie qu'elle appartient bien au joueur dont c'est le tour de
        jouer...

        """
        raise NotImplementedError("jeu.selectionner_piece")

    def jouer_case(self, pos_x, pos_y):
        """
        Joue la case (pos_x, pos_y) avec la pièce sélectionnée.

        """
        raise NotImplementedError("jeu.jouer_case")

    def verifier_echec_mat(self):
        """ 
        Vérifie si le joueur actuel est en position d'echec et mat.
        """
        raise NotImplementedError("jeu.verifier_echec_mat")


if __name__ == '__main__':
    ech = Echiquier()
    print(ech)

  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
4 septembre 2010 à 17:17:39

Citation : realmagma

Pour en revenir au échec, Lexileduval m'a suggéré d'instancier mes objets dans la classe <Plateau>. Ses arguments tiennent la route, mais il m'est impossible de faire fonctionner ma méthode 'g_mouvPossible' de ma classe <Pion>. Il y a un problème. Il faut un lien entre ces deux classes... (Je veux dire par là qu'il faut bien que les informations circulent entre les différentes classes)



Désolé, pas eu trop le temps de répondre. Mais je vois qu'on t'a bien mieux expliqué l'idée d'encapsulation qu'effectivement je voulais te faire passer.

Bonne chance pour la suite de ton projet ^^
  • Partager sur Facebook
  • Partager sur Twitter
4 septembre 2010 à 21:44:34

Bonjour,

Je tiens encore une fois à vous remercier de m'aider, que serais-je sans vous ? ;) ...Merci beaucoup !

Je commence à mieux comprendre comment fonctionne le principe d'encapsulation ainsi que l'utilisation des classes, de l'héritage (même s'il reste quelques zones d'ombre dû au manque d'expérience. C'est pour cela que je vais étudier ton code à fond demain matin. Mais d'abord dodo ;) )

J'aurais juste une dernière question que j'aimerais te poser (mais aussi à tout ceux qui passeront sur ce topic):
- Pour implémenter ces classes, ces méthodes, il ta bien fallu réfléchir dans un premier temps. (avant de te lancer corps et âmes dans ce projet :) ).
Mais Qu' as-tu utilisé ?

  • - Le 'langage' UML
  • - Les design-Pattern
  • - Quelque chose d'autre


Peux-tu me décrire (si tu peux/veux) ta façon de penser, ta façon de confectionner ce projet.
- As-tu réalisé des schémas ? Si oui, peux-tu mes les envoyer grâce à un screenShoot ou seulement me les décrire ?
- Que me conseils-tu ?

Ça fait beaucoup de questions et j'en suis conscient. Je tiens aussi à préciser qu'a aucuns moments je ne veux abuser de ta générosité pour que tu (mais aussi 'Vous') me fasses mon code. J'ai soif d'apprendre et de comprendre; et grâce à des experts comme toi (à comprendre par: Des personnes maîtrisant très bien (parfaitement?) le langage et ses concepts) que se sera possible. Je vous en remercie encore une fois.

P.S: Toutes suggestions/critiques seront les bienvenues.
P.P.S: Attendez-vous (sans doute) à d'autres questions par la suite. (Désolé, je ne suis qu'un simple débutant) :magicien:

Realmagma.
  • Partager sur Facebook
  • Partager sur Twitter
4 septembre 2010 à 22:25:14

Heuu, pour ce code, j'avoue que je l'ai fait plutôt directement...

Pour les deux 'outils' que tu as cités :
Les diagrammes UML sont avant tout un moyen de communiquer. C'est vrai que lorsque je réfléchis "sur le papier", en général je dessine une sorte de diagramme des classes simplifié, avec un crayon à papier et une gomme (pour pouvoir effacer/redessiner), histoire d'avoir sous les yeux une idée de l'architecture générale, mais pour un truc aussi "simple", j'ai plutôt le diagramme dans la tête.
Les design patterns ne s'utilisent en général pas dès le début de la conception : ils sont là pour résoudre un problème lorsqu'il se présente, mais on n'utilise pas un design pattern avant de savoir s'il va être utile ou non (à part dans certains cas, comme pour les sites web ou les programmes qui disposent d'une GUI avancée, où l'on peut raisonnablement se lancer vers une conception avec le MVC —Model-View-Controller— ou MVD —Model-View-Delegate—).

Pour ce code, j'ai commencé par identifier ce que je pouvais regrouper par 'familles', de manière à créer des abstractions.

Ici, la seule "couche d'abstraction" consiste à créer une classe Piece, dont toutes les pièces vont dériver. De cette manière, je sais que je pourrai demander de faire ce que je voudrai à une pièce, sans avoir à déterminer s'il s'agit d'un roi ou d'un cavalier : elle le fera, point.
Pour obtenir cette couche d'abstraction, je me suis servi de l'héritage, par habitude.

J'ai donc créé le "squelette" (sans préciser les arguments des méthodes ni les implémenter... ou plutôt en remplaçant l'implémentation par un raise NotImplementedError , de manière à ne pas oublier par la suite de les implémenter, si elles sont nécessaires, avant de lancer le programme.) de la classe Piece, avec des noms de méthodes explicites.

Ensuite, j'ai créé le squelette de la classe Echiquier : pour moi, l'échiquier est un moyen de regrouper les pièces : de créer le "lien" entre les pièces. C'est pour ça que c'est l'Echiquier qui initialise les pièces. Étant donné qu'il s'agit d'un objet plus "gros" (plus haut niveau), je lui ai confié la responsabilité de méthodes moins précises ("bouger"/"attaquer" au lieu de "vérifier que tel mouvement est possible"/"vérifier que telle attaque est possible"). L'échiquier servira donc à coordonner les mouvements des pièces entre elles, sans se demander comment doit bouger chaque type de pièce : ce comportement est encapsulé.

Enfin, la classe Jeu permet de faire le travail de plus haut niveau encore que l'Echiquier : gérer les règles du jeu autres que les règles de déplacement ("Y'a-t'il un échec et mat ?", "À quel joueur de jouer ?", etc.).

Avec ces 3 classes, on a une vision globale du modèle.
Cette vision globale, dans un programme simple comme celui-ci (qui n'a pas beaucoup de classes différentes à gérer), me suffit. Je réfléchis ensuite à des scénarios : comment les classes communiquent-elles pour déplacer une pièce, pour attaquer..., qui va/peut/doit faire telle tâche, quelles sont les informations nécessaires, etc., et je vérifie qu'avec le modèle actuel, tout peut se faire "naturellement".

Reste à dériver la classe Piece pour chaque type de pièce : j'ai rajouté dans le constructeur de la classe Piece une référence à l'échiquier parce qu'une pièce donnée aura besoin de vérifier qu'une autre pièce ne lui barre pas la route pour se déplacer, notamment, ce qui permet de laisser l'échiquier superviser ces vérifications en se contentant de déléguer le travail aux pièces.

Après ça, j'ai rajouté le code nécessaire à l'affichage de l'échiquier : quand on code, on aime bien vérifier que ce que l'on vient de faire fonctionne. Il est donc nécessaire de pouvoir afficher les pièces sur l'échiquier (d'une manière ou d'une autre) tôt durant le développement. Ici, c'est un affichage très simple (et un peu crade) dans la console, mais qui sera suffisant pour tester le fonctionnement du modèle. Libre à toi de créer une interface plus sexy par la suite.

Pour terminer, j'ai implémenté quelques méthodes, pour te donner une idée du "niveau" d'implémentation nécessaire dans chaque classe.

Il te restera probablement une classe "interface" à créer, de manière à ce que l'on puisse effectivement jouer avec ce programme.



  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
25 février 2017 à 20:02:04

Bonsoir j ai  un jeu d échecs à compléter , j ai besoin d aide.
  • Partager sur Facebook
  • Partager sur Twitter
25 février 2017 à 20:14:30

Ouvre ton propre sujet en expliquant ton problème ou les points que tu n'arrives pas à implémenter, et en présentant un minimum ton travail (en utilisant le bouton </> pour insérer le code).
  • Partager sur Facebook
  • Partager sur Twitter
Précepte: Le mieux est l'ennemi du bien