Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercice][Débutant - Intermediaire] Démineur

mini-projet

    7 septembre 2010 à 12:01:28

    Salut les zéros.
    Aujourd'hui, pour vous exercer, je vous ai concocté un petit projet de jeu, très connu, plutôt simple mais formateur : un démineur.

    Rappel des règles du jeu


    Pour ceux qui ne connaitraient pas encore ce petit jeu aussi simple qu'addictif, je vous en rappelle rapidement le principe.
    Vous incarnez un agent de l'équipe de déminage chargé de sécuriser un champ de mines anti-personnelles (eh oui, malheureusement, ces horreurs existent encore à notre époque), en disposant des petits drapeaux « Attention, ne marchez pas ici, ça va vous péter à la tronche » sur chacune d'entre elles.

    Le champ de mines se présente sous la forme d'un tableau à L lignes et C colonnes. Chacune des cases de ce tableau peut contenir une mine ou non. Au début de la partie, toutes les cases sont masquées (il y a de la terre partout).
    La suite des explications prendra en exemple la petite matrice suivante à 5 lignes et 5 colonnes.

    <math>\(\begin{bmatrix}\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\end{bmatrix}\)</math>


    Dans le jeu, vous n'avez que deux actions possibles. Creuser ou poser un drapeau.

    Creuser



    Creuser constitue l'action principale du jeu : c'est la seule action indispensable pour terminer la partie, et elle est irréversible.
    Lorsque vous creusez sur une case du tableau, il n'y a que deux possibilités :
    • La case contient une mine, elle vous pète à la tronche et la partie est terminée
    • La case ne contient pas de mine, et elle vous indique le nombre de cases adjacentes qui contiennent une mine.

    Ici, on travaille en 8-connexité, c'est-à-dire que chaque case a 8 cases adjacentes : au-dessus, en-dessous, à gauche, à droite, au-dessus à gauche, au-dessus à droite, en-dessous à gauche, et en-dessous à droite d'elle.

    Par exemple, je décide de commencer la partie en creusant la case se trouvant à la seconde ligne, seconde colonne de la matrice (donc de coordonnées (1, 1)):

    <math>\(\begin{bmatrix}\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & 1 & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\cdot & \cdot & \cdot & \cdot & \cdot \\\end{bmatrix}\)</math>


    Ouf! Pas de mine, à cette position, je suis toujours en vie, et la case m'indique qu'il se trouve une mine (et une seule) dans son voisinage direct...

    Un petit raccourcis

    Pour éviter que le jeu ne soit trop répétitif, on ajoute une petite règle très pratique. Si la case où l'on a creusé ne contient aucune mine dans son voisinage (une case 0), alors le jeu creuse automatiquement sur toutes les cases autour d'elle, ce qui permet de dégager les grands espaces libres d'un seul coup.

    Par exemple, je tente ma chance, et je creuse maintenant au-dessus à gauche de la case précédente (position (0,0), donc).


    <math>\(\begin{bmatrix}0 & 0 & 0 & 1 & \cdot \\0 & 1 & 1 & 2 & \cdot \\0 & 2 & \cdot & \cdot & \cdot \\0 & 2 & \cdot & \cdot & \cdot \\0 & 1 & \cdot & \cdot & \cdot \\\end{bmatrix}\)</math>


    Cette case ne contenait pas de mine dans son voisinage direct, le jeu a donc creusé automatiquement partout autour d'elle, ce qui, récursivement, a découvert tout un champ libre.

    La fin de la partie

    Pour terminer la partie (et gagner), il faut et il suffit que toutes les cases du tableau ne contenant pas de mine aient été creusées.


    Poser un drapeau



    La seconde action possible dans le jeu, poser un drapeau, n'est absolument pas indispensable pour jouer, mais elle constitue une sécurité pour le joueur (ce qui est extrêmement pratique lorsque l'on joue avec un grand champ de 1000 cases). Lorsque l'on pose un drapeau sur une case, celle-ci affiche un symbole "attention", et on ne peut plus creuser dessus tant que le drapeau s'y trouve. Cela évite donc de se faire péter une mine à la tronche par erreur alors que l'on avait deviné qu'elle était là.
    Par exemple, je suis sûr que la case (2,2) contient une mine, je décide donc de poser un drapeau dessus (symbolisé ici par une arobase).


    <math>\(\begin{bmatrix}0 & 0 & 0 & 1 & \cdot \\0 & 1 & 1 & 2 & \cdot \\0 & 2 & @ & \cdot & \cdot \\0 & 2 & \cdot & \cdot & \cdot \\0 & 1 & \cdot & \cdot & \cdot \\\end{bmatrix}\)</math>


    Ainsi, si j'essayais par erreur de creuser cette case, le programme l'ignorerait. C'est une sécurité.

    Le fait de poser un drapeau doit être RÉVERSIBLE.


    En effet, on peut tout à fait poser un drapeau sur une case parce où l'on soupçonne la présence d'une mine à un moment donné, mais on doit pouvoir aussi retirer ce drapeau par la suite si l'on en a envie.

    Consignes



    Je vous propose de programmer ce jeu en plusieurs phases, de manière à ce que les débutants ne se perdent pas dès le départ, et que vous ayez la possibilité d'apprendre des choses avec ce mini-projet quel que soit votre niveau.

    Phase 1 : votre base de travail.


    Dans un premier temps, il va vous falloir coder le cœur du programme, à savoir la version jouable du jeu la plus simple possible.

    Si vous débutez dans la POO, je vous recommande très chaudement de vous forcer à programmer objet pour ce projet : il est suffisamment simple pour que vous vous en sortiez avec 3 classes.

    Si vous n'avez pas encore appris la POO, qu'à cela ne tienne, vous n'êtes pas obligé d'utiliser ce paradigme pour obtenir une version jouable du jeu, mais c'est peut-être une bonne occasion de s'y coller. ;)

    Pour valider cette première phase, vous devrez obtenir la version la plus simple possible, qui vous servira ensuite de base pour le reste du développement :

    • Déterminer un moyen pertinent de représenter les données avec lesquelles vous devrez interagir. Si je rappelle cette analyse préalable obligatoire (et évidente), c'est simplement parce que cette modélisation va influencer tout le reste du mini-projet. Plus les structures de données que vous utiliserez seront pertinentes, plus le jeu sera facile à programmer.
    • Coder la fonctionnalité creuser de base. Comme je viens de l'expliquer dans les règles du jeu, l'action creuser est la seule indispensable pour finir la partie. C'est donc la seule requise dans cette phase. Il n'est pas non plus obligatoire de coder pour le moment la petite règle supplémentaire qui creuse automatiquement autour des casses nulles : c'est gadget, et vous pouvez tester le fonctionnement de votre programme sans ça.
    • Une interface utilisateur rudimentaire. Dans cette première phase, ne vous focalisez surtout pas sur l'interface. Elle peut être moche, on s'en fout... À vrai dire elle DOIT être moche. Le but, c'est simplement que vous puissiez afficher l'état du champ (la grille du jeu), Y COMPRIS LES MINES, et creuser une case donnée en donnant ses numéro de ligne et numéro de colonne. Cette interface rudimentaire va vous permettre de tester votre jeu, voir si les règles sont bien programmées, qu'il n'y a pas de bug, que tout est sous contrôle. Rien de plus ! Attention cependant à ce que cette interface rudimentaire soit bien séparée du reste du programme (bien encapsulée), parce qu'évidemment, elle va changer dans la suite.
    • Une partie minimaliste. Contentez-vous pour le moment d'un tableau à 5 lignes et 5 colonnes grand maximum, et de quelques mines (2 ou 3) (de toute façon avec l'interface "console print" ce serait trop fastidieux de faire plus). Encore une fois, votre jeu ne doit pas à ce niveau vous offrir un challenge, au contraire, les parties doivent être finies rapidement, de manière à vérifier que vous SAVEZ détecter quand une partie est perdue ou gagnée, sans passer 20 minutes à rentrer des coordonnées au clavier à chaque fois. :D

    Une fois cette phase achevée, vous devriez être capable d'avoir une version "jouable minimale" du démineur, avec les mines qui s'initialisent au hasard dans le champ (mais sont affichées, pour vous aider à débugger), la possibilité de creuser (et donc l'affichage de nombre de mines autour de la case dans laquelle vous venez de creuser) et la détection de la fin de la partie. Une fois que tout ceci fonctionne et est bien en place, vous pouvez passer à la phase 2.

    Phase 2 : La totalité des règles du jeu.



    Dans cette phase, votre modèle du démineur va être complété, avec toutes les règles du jeu. Si votre code a bien été organisé dans la première partie, vous ne devriez avoir que du code à rajouter, et très peu à modifier.
    Il vous faudra donc coder :
    • La fonctionnalité creuser intelligente. Si la case que vous creusez ne contient aucune mine dans son entourage, alors vous creusez automatiquement partout autour d'elle afin de dégager les grands champs vides.
    • La pose de drapeaux. Si un drapeau est posé sur une case, on ne peut plus la creuser, mais on peut retirer le drapeau pour creuser ensuite.
    • Pour l'interface utilisateur. Pas de changement exceptionnel, mais comme vous avez deux actions différentes, je vous conseille (c'est le plus simple) de faire en sorte que l'interface soit « modale » : On appuie sur "D" pour passer en mode drapeau, et sur "C" pour passer en mode creusage. Le reste est identique.

    Une fois que tout ceci fonctionne bien, vous pouvez masquer les mines et avoir un "vrai" jeu. ;)

    Phase 3 : l'interface



    Maintenant, et seulement maintenant que le cœur du jeu est en place avec toutes les règles, vous pouvez penser à l'interface.
    Ici, vous avez le choix des armes. PyQt, PyGTK, tkInter, PyGame... Profitez-en peut-être pour choisir un module ou une bibliothèque d'interface à laquelle vous désirez vous essayer, c'est un bon moyen d'apprendre. ;)
    Personnellement, en préparant ce mini-projet, je me suis souvenu que j'avais toujours voulu essayer ncurses (les interfaces console riches, pour les terminaux Unix), donc j'ai codé mon interface en ncurses avec le module curses de l'API standard de Python (par chance, le jeu s'y prête bien). Voici deux screenshots :

    Image utilisateur Image utilisateur

    Séparez, de préférence, votre code d'interface du moteur du jeu (collez-les dans 2 modules différents).
    Personnellement, avant de soumettre une proposition de correction, je vais refaire un module d'interface utilisant autre chose que ncurses, histoire d'avoir une correction portable. :D

    Phase 4 : Lâchez-vous !



    Maintenant que vous avez un démineur complet avec une belle interface, lâchez-vous, rajoutez des parties en temps limité, un système de score, une liste des highscores, des options modifiables, ou même, que sais-je ? un mode multi-joueur coopératif en ligne ou une IA qui vous donne un indice si vous êtes bloqué... Bref, amusez-vous, et venez nous montrer vos belles idées !

    Bon courage !



    Proposition de correction



    Phases 1 et 2
    Phase 3

    Vos démineurs



    Vos projets, une fois passée la phase 3, seront référencés ici...
    • Partager sur Facebook
    • Partager sur Twitter
    Zeste de Savoir, le site qui en a dans le citron !
      7 septembre 2010 à 13:24:32

      Bonjour Nohar,

      Un mini-projet très bien expliqué avec plusieurs phases pour que les débutants comme moi ne s'y perdent pas, jolie :p .
      Voilà un autre projet intéressant. Je m'y atèle une fois les Threads et les Sockets vus.
      Un exercice qui me fera réviser les classes, utiles pour le jeu d'échec et... le bomber man.

      Realmagma.

      EDIT: Il y a t-il une date butoir ?
      • Partager sur Facebook
      • Partager sur Twitter
        7 septembre 2010 à 13:57:28

        Citation : realmagma

        Il y a t-il une date butoir ?



        Pas spécialement, le sujet est ouvert, codez ce que vous voulez, comme vous pouvez.

        Je posterai une correction de la phase 2 d'ici une semaine, et une ou deux versions possibles pour la phase 3 dans 2 semaines.

        Edit : À vrai dire, c'est en voyant comment tu galérais avec tes projets sur le forum que j'ai eu l'idée de ce mini-projet. Je compte aborder ces problématiques dans le tuto sur Pygame (par où commencer, comment organiser le développement ? etc.), mais celui-ci n'étant pas prêt encore à voir le jour, j'ai pensé qu'un mini-projet facile, plutôt extensible, et surtout, orienté (avec des phases de développement prédéfinies), était un bon moyen pour s'habituer à cette démarche, et peut-être la réutiliser inconsciemment dans d'autres projets plus sérieux.
        C'est en mettant les mains dans le cambouis qu'on prend de bonnes habitudes. Ici, le but n'est pas de travailler un aspect de Python particulier mais plus d'obtenir sans trop de difficultés un programme fini, en suivant des étapes simples. Les deux premières phases sont d'ailleurs volontairement assez courtes à réaliser, de manière à donner le sentiment d'avancer.
        • Partager sur Facebook
        • Partager sur Twitter
        Zeste de Savoir, le site qui en a dans le citron !
        Anonyme
          7 septembre 2010 à 20:31:01

          Salut,

          ton exercice m'intéresse beaucoup. Je n'aurais pas le temps de m'y atteler cette semaine, mais je le ferais dès que j'aurais du temps.

          Ce qui m'a attiré, c'est le fait que tu nous donne une démarche afin de nous guider dans la réflexion et la réalisation avant qu'on fonce tête baissée dans le code, comme un brute.

          Bonne soirée.
          • Partager sur Facebook
          • Partager sur Twitter
            7 septembre 2010 à 20:46:29

            Citation : NoHaR

            Edit : À vrai dire, c'est en voyant comment tu galérais avec tes projets sur le forum que j'ai eu l'idée de ce mini-projet. Je compte aborder ces problématiques dans le tuto sur Pygame (par où commencer, comment organiser le développement ? etc.), mais celui-ci n'étant pas prêt encore à voir le jour, j'ai pensé qu'un mini-projet facile, plutôt extensible, et surtout, orienté (avec des phases de développement prédéfinies), était un bon moyen pour s'habituer à cette démarche, et peut-être la réutiliser inconsciemment dans d'autres projets plus sérieux.



            Et je t'en remercie 1000 fois. Ça me (EDIT: '/nous')fera progresser ;)

            Voilà comment je compte organiser mon code:

            Lien direct


            Fait avec DIA en diagramme UML

            Je posterai mon code en Python demain ou tout à l'heure :) .
            Si vous avez des remarques ou autre, dites-les moi.
            Bonne soirée.

            EDIT n°2: Ce n'est qu'une ébauche et je tiens à préciser que je ne maîtrise pas trop le diagramme UML. C'était juste pour m'aider. :p

            EDIT n°3: (décidément) Je me suis trompé de diagramme (il manque quelques '__init__'. Je vais éditer d'ici peu. Excusez-moi.
            • Partager sur Facebook
            • Partager sur Twitter
              7 septembre 2010 à 20:56:56

              Juste une remarque :
              C'est écrit en rouge en gras dans la phase 1 de ne pas se prendre la tête avec l'affichage et tu fais une classe... Affichage... heuu, lol ? :D

              Pour les 2 autres classes, elles me paraissent être une bonne base de départ. ;)

              Je vais moi-même faire un petit diagramme des classes de ce que j'ai fait pour les phases 1 et 2. C'est un peu overkill pour un projet aussi facile, mais bon, ça permet de guider sur la structure du programme sans soumettre à la tentation de copier-coller du code. :p


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

                Je viens d'éditer mon précédant message.

                Citation : Nohar

                C'est écrit en rouge en gras dans la phase 1 de ne pas se prendre la tête avec l'affichage et tu fais une classe... Affichage... heuu, lol ?



                Hum en effet :p , je l'ai fait par 'instinct'. Mais tu n'as pas dit qu'il ne fallait rien afficher.
                Je te rassure l'affichage sera vraiment moche (enfin si j'arrive à l'afficher et à faire fonctionner mon programme :-° )

                Citation : Nohar


                Pour les 2 autres classes, elles me paraissent être une bonne base de départ. ;)


                Ok, merci :)
                • Partager sur Facebook
                • Partager sur Twitter
                Anonyme
                  7 septembre 2010 à 21:33:31

                  Salut,

                  j'ai tout de même un peu réfléchi à ma manière de traiter les données, et une matrice en tant que plateau me semble une bonne idées. Je pourrais l'utiliser pour stocker mes objets "case", de cette manière (là c'est du moche juste pour l'idée):

                  >>> plateau = [
                  ...        [c1, c2, c3],
                  ...        [c4, c5, c6],
                  ...        [c7, c8, c9],
                  ...       ]
                  


                  Cependant, cela oblige à faire deux boucles for imbriquées, et (même si pour le moment on se moque des performances), cela n'est-il pas un peu trop un bricolage menaçant de ne pas marcher à tout moment ?

                  Je me suis inspiré ce de paragraphe de la doc python pour construire ma réflexion

                  Merci d'avance pour vos réponses.

                  Bonne soirée.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    7 septembre 2010 à 21:46:27

                    Je pensais aussi faire comme ça pour gérer mon plateau. Cependant je ne sais pas si c'est judicieux tant au niveau pratique que performance (même si la deuxième raison m'importe peu)

                    EDIT:


                    l =  [[1, 2, 5, 4, 8, 6, 9, 3, 7] ,
                          [8, 5, 6, 9, 7, 3, 1, 5, 2] ,
                          [3, 7, 9, 2, 1, 5, 4, 8, 6], 
                          [2, 1, 8, 5, 9, 4, 7, 6, 3], 
                          [6, 5, 4, 7, 3, 2, 8, 1, 9], 
                          [7, 9, 3, 1, 6, 8, 2, 4, 5], 
                          [5, 3, 2, 8, 4, 9, 6, 7, 1], 
                          [9, 8, 7, 6, 5, 1, 3, 2, 4], 
                          [4, 6, 1, 3, 2, 7, 5, 9, 8]]
                    >>> l[0][0]
                    1
                    >>> l[1][0]
                    8


                    Question: Doit on faire le plateau de jeu à la main ou bien avec un algorithme qui place automatiquement les numéros des cases en fonction des bombes posées aléatoirement ?
                    • Partager sur Facebook
                    • Partager sur Twitter
                      7 septembre 2010 à 22:09:52

                      Deux boucles for imbriquées, niveau performances, ne vous en faites pas, ça passera quand même, d'autant plus qu'a priori vous ne devriez pas avoir à le parcourir tout le temps.
                      Pour un démineur, la question des performances est vraiment... vraiment très secondaire. ;)

                      Un tableau à une ou deux dimensions, c'est effectivement une structure adaptée (c'est probablement plus intuitif à gérer à 2 dimensions).

                      Si ça vous gène d'utiliser un tableau à 2 dimensions, vous pouvez vous inspirer de ce qui suit :

                      >>> dim2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
                      >>> dim1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
                      >>> y, x = 1, 2
                      >>> largeur = 3
                      >>> dim2[y][x] == dim1[y * largeur + x]
                      True
                      


                      Voilà l'architecture que j'ai utilisée, personnellement, pour les deux premières phases.


                      Image utilisateur


                      Pour l'affichage, je me suis contenté de surcharger les méthodes __str__() pour afficher le champ avec un simple print .


                      Edit:

                      Citation : realmagma

                      Question: Doit on faire le plateau de jeu à la main ou bien avec un algorithme qui place automatiquement les numéros des cases en fonction des bombes posées aléatoirement ?



                      Ça, c'est à toi de décider. (Edit2: m'enfin la seconde option me semble meilleure ;) )
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Zeste de Savoir, le site qui en a dans le citron !
                        8 septembre 2010 à 10:45:04

                        Bonjour,

                        finalement, j'ai changé certaines choses au niveau du diagramme (je ne peux pas faire de 'screen' car je l'ai fait sur papier cette fois ^^ ).

                        Mon problème est au niveau de la génération/création d'une grille chiffrée.
                        Je l'ai mise dans ma classe <Plateau> et pour voir si tout marchait bien je l'ai sortie de mon programme pour la tester individuellement. Mais comme vous vous en doutiez, mon algorithme plante sans que j'arrive à trouver pourquoi.

                        Pourriez-vous éclaircie les idées s'il vous plaît. (Décidément j'ai beaucoup de mal avec les classes moi :( )
                        Ma classe <Plateau>:

                        #-*-*- Fin de <Jeu> || Début de <Plateau> -*-*-#  
                        
                        class Plateau(object):
                            """Permet de tenir à jour le plateau du jeu 'démineur'."""
                            def __init__(self):
                                """
                                Définie les variables nécessaires
                        
                                """
                                self.mapp = []
                                self.longueur = 5 #Pour le nombres de case 5²
                                self.nombreMines = 0
                                
                        
                            def genererGrille(self):
                                """Génère une grille de démineur. Appelle placerBombe()
                                   Retourne la grille ainsi qu'une copie remplie de '*'
                                """
                                for i in range(self.longueur):          #Pour un tableau de 5x5
                                    self.mapp.append([0] * self.longueur)#Remplie de '0' (type = int)
                        
                                #Appel à la fonction: placerBombe (deux fois pour l'essai)
                                self.mapp = self.placerBombe() #On met à
                                self.mapp = self.placerBombe() #jour la map
                                
                                #Appel à la fonction: placerChiffre
                                
                        
                            def placerBombe(self):
                                """Génère des nombres aléatoire
                                   pour placer une bombe et appelle chiffrerMap()
                                """
                                #On pose une bombe 'B' aléatoirement dans la map suivant la longueur de celle-ci ->(self.longueur)
                                self.col, self.ligne, self.bombe = random.randrange(0, self.longueur), random.randrange(0, self.longueur), 'B'
                                #On place la bombe aux coordonnées trouvées aléatoirement
                                self.mapp[self.col][self.ligne] = self.bombe
                        
                                #Appel de la fonction chiffrerMap
                                self.mapp = self.chiffrerMap(self.col, self.ligne, self.mapp)
                        
                                print(self.mapp)
                                return self.mapp
                        
                        
                            def chiffrerMap(self, col, ligne, mapp):
                                """Permet de chiffrer ma map pour
                                   savoir où se trouvent les bombes
                                """
                        
                                #Implémentation de l'algorithme pour chiffrer ma map
                                #[[0, 0, 0, 0, 0],
                                # [0, 0, 0, 0, 0],
                                # [0, 0, B, 0, 0],
                                # [0, 0, 0, 0, 0],
                                # [0, 0, 0, 0, 0]]
                        
                                #Implémentation de l'algo
                                #Si il y a un 'B' dans la grille alors on met un '1' dans les huit cases
                                #qui l'entoure donc dans les cases suivantes (<C>olonne;<L>igne) = '<B>ombe'
                        
                                #Sens d'une aiguille d'une montre. On commence en haut à gauche:
                                #(C-1;L-1) -> (C-1;L) -> (C-1;L+1)
                                #(C;L-1)   -> B:(C;L) -> (C;L+1)
                                #(C+1;L-1) -> (C+1;L) -> (C+1,L+1)
                        
                                #Si une des cases est en contacte avec plusieurs bombes, alors:
                                #compteur += 1, donc on placera un 2/3 au lieu du 1
                                #La case aux coordonnées C;L = compteur
                        
                                #Ligne n°1:
                                self.mapp[self.col-1][self.ligne-1] = 1
                                self.mapp[self.col-1][self.ligne]   = 1
                                self.mapp[self.col-1][self.ligne+1] = 1
                        
                                #Ligne n°2:
                                self.mapp[self.col][self.ligne-1] = 1
                                self.mapp[self.col][self.ligne]   = self.bombe
                                self.mapp[self.col][self.ligne+1] = 1
                        
                                #Ligne n°3:
                                self.mapp[self.col+1][self.ligne-1] = 1
                                self.mapp[self.col+1][self.ligne]   = 1
                                self.mapp[self.col+1][self.ligne+1] = 1
                        
                                return self.mapp
                        


                        Je vous en remercie d'avance.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        Anonyme
                          8 septembre 2010 à 11:20:02

                          Edit : Voici une solution naïve, mais qui fonctionne apparemment bien :


                          #!/usr/bin/env python
                          # -*- coding: UTF-8 -*-
                          
                          from random import randint
                          
                          if __name__ == '__main__':
                              plateau = []
                              longueur = 4
                              r = 0
                              for i in range(longueur):          #Pour un tableau de 5x5
                                  plateau.append([0] * longueur)#Remplie de '0' (type = int)
                          
                              #on place des bombes - peu fiable
                              for i in range(longueur):
                                  for j in range(longueur):
                                      r = randint(1, longueur)
                          
                                      if r == longueur:
                                          plateau[i][j] = "B"
                          
                              #et là on va vérifier les cases - implémentation naive
                              for i in range(longueur):
                                  for j in range(longueur):
                                      nbBombes = 0
                                      if plateau[i][j] != "B":
                                          if j > 0: #gauche
                                              if plateau[i][j-1] == "B":
                                                  nbBombes += 1
                                          if j < longueur-1:#droite
                                              if plateau[i][j+1] == "B":
                                                  nbBombes += 1
                                          if i > 0: #faut voir si on est pas première ligne
                                              #on va regarder toutes les cases de la ligne précédente
                                              if j > 0: #haut gauche
                                                  if plateau[i-1][j-1] == "B":
                                                      nbBombes += 1
                                              #haut
                                              if plateau[i-1][j] == "B":
                                                  nbBombes += 1
                                              if j < longueur-1: #haut droite
                                                  if plateau[i-1][j+1] == "B":
                                                      nbBombes += 1
                                          if i < longueur-1: #si on est pas dernière ligne
                                              if j > 0: #bas gauche
                                                  if plateau[i+1][j-1] == "B":
                                                      nbBombes += 1
                                              #bas
                                              if plateau[i+1][j] == "B":
                                                  nbBombes += 1
                                              if j < longueur-1: #bas droite
                                                  if plateau[i+1][j+1] == "B":
                                                      nbBombes += 1
                                          #maj de la case
                                          plateau[i][j] = nbBombes
                          #debug
                                      print "case " + str(i) + "/" + str(j) + " = " + str(nbBombes)
                              print plateau
                          #fin debug
                          


                          J'ai fait ça vite à des fins de tests, et je pense m'en servir :)

                          Après, comme l'a dit NoHaR, on est pas obligé de le faire dès le début, mais ça peut être utile, ça dépend comment on veut le faire.
                          • Partager sur Facebook
                          • Partager sur Twitter
                            8 septembre 2010 à 11:42:51

                            Salut.
                            Pour "chiffrer" ta grille, comme tu dis, tu peux aussi utiliser un algorithme naïf (et largement suffisant... j'ai testé, ça fonctionne sans problème):
                            pour chaque case, tu comptes effectivement le nombre de mines dans son entourage (donc 2 boucles et tu incrémentes un compteur). ;)

                            Par ailleurs, ça peut être intéressant de remarquer que tu n'es pas obligé de connaitre le nombre de mines autour d'une case donnée avant d'avoir vraiment besoin d'afficher cette case...

                            Après, imagine que plusieurs mines se touchent : tel quel ton algo ne compte les mines qu'une par une. peut-être voudrais-tu faire "+=1" au lieu de "=1" dans ta méthode "chiffrer_grille"... ;)

                            Enfin, fais attention avec les indices : si tu dépasses la longueur du tableau, Python te lèvera un IndexError, et si tu passes un indice négatif, il comptera simplement les cases à partir de la fin (ce qui n'est pas le comportement que tu attends dans ce cas précis).

                            Edit : Si malgré tout ça tu bloques vraiment sur l'initialisation de la grille, reporte ça à plus tard et initialises-en une à la main, pour coder le reste, et revenir sur ton initialisation automatique plus tard. ;)
                            • Partager sur Facebook
                            • Partager sur Twitter
                            Zeste de Savoir, le site qui en a dans le citron !
                              8 septembre 2010 à 13:35:23

                              ... J'ai encore un petit problème :lol:

                              J'ai ré-écrit mon algorithme à l'aide d'une double-boucle, seulement ma mapp (mon plateau de jeu) ne veut pas se modifier; même si je retourne ma mapp en fin de fonction :o .

                              Pourriez-vous me dire si mon algorithme est bon ou pas ?

                              Ma méthode *chiffrerMap* issue de ma classe <Plateau>:
                              class Plateau(object):
                                  """Permet de tenir à jour le plateau du jeu 'démineur'."""
                                  def __init__(self):
                                      """
                                      Définie les variables nécessaires
                              
                                      """
                                      self.mapp = []
                                      self.longueur = 5 #Pour le nombres de case 5²
                                      self.nombreMines = 0
                                      
                              
                                  def genererGrille(self):
                                      """Génère une grille de démineur. Appelle placerBombe()
                                         Retourne la grille ainsi qu'une copie remplie de '*'
                                      """
                                      for i in range(self.longueur):          #Pour un tableau de 5x5
                                          self.mapp.append([0] * self.longueur)#Remplie de '0' (type = int)
                              
                                      #Appel à la fonction: placerBombe (deux fois pour l'essai)
                                      self.mapp = self.placerBombe() #On met à
                                      self.mapp = self.placerBombe() #jour la map
                                      print(self.mapp)
                              
                                  def placerBombe(self):
                                      """Génère des nombres aléatoire
                                         pour placer une bombe et appelle chiffrerMap()
                                      """
                                      #On pose une bombe 'B' aléatoirement dans la ma suivant la longueur de celle-ci ->(self.longueur)
                                      self.col, self.ligne, self.bombe = random.randrange(0, self.longueur), random.randrange(0, self.longueur), 'B'
                                      #On place la bombe aux coordonnées trouvées aléatoirement
                                      self.mapp[self.col][self.ligne] = self.bombe
                              
                                      #Appel de la fonction chiffrerMap
                                      self.mapp = self.chiffrerMap(self.col, self.ligne, self.mapp)
                              
                                      return self.mapp
                              
                              
                                  def chiffrerMap(self, col, ligne, mapp):
                                      """Permet de chiffrer ma map pour
                                         savoir où se trouvent les bombes
                                      """
                                      
                                      #Implémentation de l'algorithme pour chiffrer ma map
                                      #[[0, 0, 0, 0, 0],
                                      # [0, 0, 0, 0, 0],
                                      # [0, 0, B, 0, 0],
                                      # [0, 0, 0, 0, 0],
                                      # [0, 0, 0, 0, 0]]
                                      
                                      #Implémentation de l'algorithme
                                      #On parcours notre mapp à l'aide d'une double boucle
                                      #On ajoute dans une liste à part les 8 case entourant la case actuelle
                                      #On regarde combien il y a de 'B'ombes.
                                      #Ma case prendra la valeur du nombre d'occurences de 'B' dans ma listeDesC...
                              
                                      self.listeDesCasesAutourDeJ = []
                              
                                      for i in self.mapp: #On parcours mon tableau 2D
                                          for j in i: 
                                              #On ajoute les 8 valeurs entourant ma case 'J'
                                              self.listeDesCasesAutourDeJ.append([self.col-1, self.col+1, self.ligne-1, self.ligne+1])
                                              
                                              #On compte le nombre d'occurences de la lettre "B"
                                              self.compteur = self.listeDesCasesAutourDeJ.count("B")
                                              self.mapp[self.col][self.ligne] = self.compteur
                                              
                                              self.listeDesCasesAutourDeJ = [] #On remet à '0'
                                          
                                      return self.mapp #On retourne la grille chiffrée
                              
                              • Partager sur Facebook
                              • Partager sur Twitter
                                8 septembre 2010 à 13:40:03

                                Le problème vient de la ligne 63:

                                #On ajoute les 8 valeurs entourant ma case 'J'
                                self.listeDesCasesAutourDeJ.append([self.col-1, self.col+1, self.ligne-1, self.ligne+1])
                                


                                Tu cherches "B" dans une liste de listes d'entiers...
                                • Partager sur Facebook
                                • Partager sur Twitter
                                Zeste de Savoir, le site qui en a dans le citron !
                                  8 septembre 2010 à 14:15:35

                                  Citation : NoHaR

                                  Le problème vient de la ligne 63:

                                  #On ajoute les 8 valeurs entourant ma case 'J'
                                  self.listeDesCasesAutourDeJ.append([self.col-1, self.col+1, self.ligne-1, self.ligne+1])
                                  



                                  Tu cherches "B" dans une liste de listes d'entiers...



                                  Pas forcément vu qu'il se peut qu'a col-1 (ou ligne+1 ou autre) il y ai un 'B'. On n'en sait rien.
                                  C'est par rapport à ma case 'J'. On regarde ce qu'il y a dans chacune des 8 cases qui l'entoure s'il y a un 'B'. (Le 'B' est définie aléatoirement)
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    8 septembre 2010 à 14:25:10

                                    Je ne pense pas que tu aies bien compris ton code...
                                    essaye de coller ceci au milieu de ta fonction :
                                    print [self.col-1, self.col+1, self.ligne-1, self.ligne+1]
                                    


                                    À quoi t'attends-tu ?
                                    Personnellement, ça m'a l'air d'une belle liste de 4 entiers, et qui n'a absolument aucune chance de contenir un "B". :)
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Zeste de Savoir, le site qui en a dans le citron !
                                      8 septembre 2010 à 14:34:47

                                      En faite je voulais faire ceci:

                                      On admet que ma mapp est celle-ci:

                                      [[0, 0, 0, 0, 0],
                                       [0, 0, 0, 0, 0],
                                       [0, 0, B, 0, 0],
                                       [0, 0, 0, 0, 0],
                                       [0, 0, 0, 0, 0]]


                                      Je parcours ma liste 2D en partant de maMapp[0][0] (en haut à gauche)
                                      Je place les 8 cases qui l'entoure dans une autre-liste donc:

                                      autreListe.append([col-1, col+1 ect..])
                                      


                                      Mon autre liste vaut désormais:

                                      autreListe:

                                      None, None, None
                                      None , J   , 0
                                      None , 0   , 0


                                      => Aucunes Bombes, donc la case 'J' vaut 0

                                      On passe à la suivante, maListe[0][1] et on répète le même procédé pour chaque case.

                                      EDIT: Correction d'un de mes tableaux: J'avais oublié deux 'None'


                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        8 septembre 2010 à 14:41:40

                                        Un truc dans ce goût-là ?

                                        for row in range(lignes):
                                            for col in range(colonnes):
                                                maliste = [] # entourage de maMap[row][col]
                                                for r in range(max(0, row-1), min(row+2, lignes)):
                                                    for c in range(max(0, col-1), min(col+2, colonnes)):
                                                        maliste.append(maMapp[r][c])
                                                # faire un truc avec maliste
                                                # faire un truc avec maMap[row][col]
                                        


                                        Edit : attention, si tu utilises Python 2.x à bien remplacer range par xrange.

                                        ou plus proprement, avec du slicing:
                                        for row in range(lignes):
                                            for col in range(colonnes):
                                                maliste = [] # entourage de maMap[row][col]
                                                for s_row in maMap[max(0, row-1):min(row+2, lignes)]:
                                                    maliste += s_row[max(0, col-1):min(col+2, colonnes)]
                                        


                                        Ou alors avec une comprehension de liste...
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Zeste de Savoir, le site qui en a dans le citron !
                                          8 septembre 2010 à 15:08:05

                                          :waw: , il y a beaucoup trop de boucles 'for' imbriqués pour moi.
                                          Pourquoi fais-tu deux boucles supplémentaires ?
                                          Je ne comprends pas très bien ton code à partir de la troisième boucles 'for'. :euh: (Même si je sais que tu as ré-écrit mon algorithme)

                                          J'ai essayé ceci, mais il n'y a aucun chiffrement. :'(
                                          (J'ai repris le tiens en l'arrangeant pour mon code)

                                          for i in range(self.ligne):
                                                      for j in range(self.col):
                                                          self.newL = []
                                                          for ii in range(max(0, i-1), min(i+2, self.ligne-1)):
                                                              for jj in range(max(0, j-1), min(j+2, self.col-1)):
                                                                  self.newL.append(self.mapp[ii][jj])
                                                          self.compteur = self.newL.count("B")
                                                          self.mapp[ii][jj] = self.compteur
                                          
                                                  return self.mapp
                                          


                                          #[[0, 0, 0, 0, 0],
                                          # [0, 0, 0, 0, 0],
                                          # [0, 0, B, 0, 0],
                                          # [0, 0, 0, 0, 0],
                                          # [0, 0, 0, B, 0]]
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            8 septembre 2010 à 15:15:38

                                            Attention, dans ton code ce ne sont pas self.ligne-1 et self.col-1 les limites, mais le nombre de lignes et de colonnes de ta grille.

                                            La première double-boucle for parcourt tout le tableau.
                                            La seconde double-boucle for parcourt l'entourage de la case courante (les cases adjacentes).
                                            Les min et max sont là pour s'assurer qu'on ne dépasse pas du tableau.


                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                            Zeste de Savoir, le site qui en a dans le citron !
                                              8 septembre 2010 à 15:47:08

                                              Rien à faire, je n'arrive pas à faire ce que je veux :colere2: .
                                              Je vais faire une petite pause car j'y suis depuis ce matin. Il est possible que ça me remette les idées en place; qui sait...

                                              Je poste mon code au cas où:

                                              for i in range(self.ligne):
                                                          for j in range(self.col):
                                                              self.newL = []
                                                              for ii in range(max(0, i-1), min(i+2, self.longueur-1)):
                                                                  for jj in range(max(0, j-1), min(j+2, self.longueur-1)):
                                                                      self.newL.append(self.mapp[ii][jj]) #On capture les 8 cases
                                                              self.compteur = self.newL.count("B") #Combien il y a t'il de bombe
                                                              self.mapp[ii][jj] = self.compteur #Ma case porte son nouveau numéro
                                              
                                                      return self.mapp #On retourne le tout
                                              
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                8 septembre 2010 à 15:54:01

                                                for i in range(self.longueur):
                                                    for j in range(self.longueur):
                                                        newL = [] # pas besoin d'en faire un attribut
                                                        for ii in range(max(0, i-1), min(i+2, self.longueur)):
                                                            for jj in range(max(0, j-1), min(j+2, self.longueur)):
                                                                newL.append(self.mapp[ii][jj])
                                                        if self.mapp[i][j] != "B":
                                                            self.mapp[i][j] = newL.count("B")
                                                
                                                return self.mapp
                                                


                                                Edit : je viens de voir qu'il fallait que tu rajoutes une condition pour éviter de remplacer les "B" par des nombres, vu la structure que tu utilises.
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Zeste de Savoir, le site qui en a dans le citron !
                                                  8 septembre 2010 à 16:20:11

                                                  Ça ne va toujours pas car on ne voit plus les 'B' et il faut au minimum 8 chiffrements
                                                  1,1,1
                                                  1,B,1
                                                  1,1,1


                                                  ... voir plus vu qu'il y a deux 'B'.

                                                  J'ai essayé toutes les possibilités possibles et inimaginable (dont celle-ci) mais aucunes ne veut faire ce que je souhaite
                                                  Je vais re-créer un algorithme en prenant celui-ci comme base.
                                                  Je le posterai ce soir, car j'ai la tête qui va exploser.

                                                  Merci quand même pour tes suggestions qui m'ont (ré-orientés dans 'le droit chemin') :)


                                                  EDIT: J'ai oublié de dire que j'obtenais ceci:
                                                  [[0, 0, 0, 0, 0],
                                                   [0, 0, 0, 0, 0],
                                                   [0, 0, 0, 0, 0],
                                                   [1, 1, 0, 0, 0],
                                                   [1, 0, 0, 0, 0]]
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    8 septembre 2010 à 16:25:24

                                                    J'ai édité mon précédent message...
                                                    Tu aurais pu trouver tout seul.

                                                    Pour info, voici le début de mon code, qui contient entre autres ma réponse au premier point de la phase 1 (y compris l'initialisation de la grille), j'utilise une liste simple pour stocker les cases au lieu d'un tableau à 2 dimensions, mais c'est plus un choix personnel qu'autre chose.
                                                    Les deux conviennent très bien.


                                                    #!/usr/bin/env python
                                                    #-*- coding: utf-8 -*-
                                                    
                                                    from __future__ import print_function
                                                    from random import randint
                                                    
                                                    ###############################################################################
                                                    # Constantes globales
                                                    
                                                    MINE = '*'    # Dessin d'une mine
                                                    FLAG = '!'    # Drapeau
                                                    NOFLAG = '.'  # Case cachée normale
                                                    NOTHING = ' ' # Case vide
                                                    
                                                    class Cell(object):
                                                        """
                                                        Classe modélisant une case du champ de mines.
                                                    
                                                        """
                                                        def __init__(self):
                                                            """
                                                            Initialisation des attributs.
                                                    
                                                            """
                                                            self.mine = False  # La case contient-elle une mine ?
                                                            self.flag = False  # Y-a t'il un drapeau posé sur cette case ?
                                                            self.show = False  # La case est-elle visible ?
                                                            self.value = 0     # Combien de cases adjacentes contiennent une mine ?
                                                    
                                                        def __str__(self):
                                                            """
                                                            Retourne un caractère symbolisant l'état de la case.
                                                    
                                                            """
                                                            if self.show:
                                                                return str(self.value) if self.value else NOTHING
                                                            else:
                                                                return FLAG if self.flag else NOFLAG
                                                    
                                                    class Field(object):
                                                        """
                                                        Classe modélisant un champ de mines.
                                                    
                                                        """
                                                        def __init__(self, rows, cols, n_mines):
                                                            """
                                                            Initialisation du champ de mines.
                                                    
                                                            rows: nombre de lignes
                                                            cols: nombre de colonnes
                                                            n_mines: nombre de mines
                                                    
                                                            """
                                                            self.rows = rows
                                                            self.cols = cols
                                                            self.n_mines = n_mines
                                                            self.hidden = rows * cols  # nombre de cases cachées
                                                            self.flagged = 0           # nombre de cases portant un drapeau
                                                            self.gameover = False
                                                            self.cells = []
                                                            self._init_cells()         # initialisation des cases
                                                            # remplissage aléatoire du champ de mines
                                                            for _ in xrange(n_mines):
                                                                idx = randint(0, rows * cols -1)
                                                                while self.cells[idx].mine:
                                                                    idx = randint(0, rows * cols -1)
                                                                row = idx // cols
                                                                col = idx - row * cols
                                                                self.cells[idx].mine = True
                                                                self._count_mines(row, col)
                                                            
                                                        def _init_cells(self):
                                                            """
                                                            Initialisation des cases.
                                                            Cette fonction pourra être surchargée si une classe fille désire 
                                                            utiliser une autre classe pour les cases. (voir interface)
                                                    
                                                            """
                                                            self.cells = [Cell() for _ in xrange(self.rows * self.cols)]
                                                    
                                                        def _count_mines(self, row, col):
                                                            """
                                                            Incrémente les compteurs de mines autour de la case (row, col)
                                                            row: numéro de ligne de la case.
                                                            col: numéro de colonne de la case.
                                                    
                                                            """
                                                            for r in xrange(max(row-1, 0), min(row+2, self.rows)):
                                                                for c in xrange(max(col-1, 0), min(col+2, self.cols)):
                                                                    self.cells[r * self.cols + c].value += 1
                                                    

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                    Zeste de Savoir, le site qui en a dans le citron !
                                                    Anonyme
                                                      8 septembre 2010 à 17:06:40

                                                      Salut,

                                                      tu veux un zip pour les sources ou on peut les poster directement ici (dans la balise secret)?

                                                      J'ai commencé un petit quelque chose, ça ne sera pas prêt ce soir, mais comme ça au moins j'ai ma réponse pour quand j'aurais fini l'étape 1 :)

                                                      Bonne journée.
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        8 septembre 2010 à 17:10:48

                                                        Je pense que c'est mieux de poster les sources sur le forum (dans une balise secret), ça permet de les lire directement. ;)
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                        Zeste de Savoir, le site qui en a dans le citron !
                                                          8 septembre 2010 à 18:08:28

                                                          NoHaR: C'est possible de faire en sorte que ta version du démineur soit accessible par PuTTy via simplement un protocole telnet ou autre. C'est pratique pour les utilisateurs de Windows qui veulent tester ta version du jeu réalisée avec curses. En tout cas, j'aime beaucoup. Bon boulot !
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            8 septembre 2010 à 18:21:42

                                                            Citation : Karel

                                                            NoHaR: C'est possible de faire en sorte que ta version du démineur soit accessible par PuTTy via simplement un protocole telnet ou autre. C'est pratique pour les utilisateurs de Windows qui veulent tester ta version du jeu réalisée avec curses. En tout cas, j'aime beaucoup. Bon boulot !



                                                            Ah oui remarque, c'est pas bête. :p
                                                            À la base j'en profitais pour y jouer dans un screen, mais j'avais zappé cet avantage de ncurses : ça peut se jouer en client léger. :D

                                                            Je verrai ça quand j'aurai posté "ma" correction complète. J'ai encore du temps avant ça, pour rajouter des fonctionnalités, fignoler un peu le code, etc.

                                                            Je pense que j'essayerai de faire une interface tkinter aussi (jamais essayé non plus :p ), si le temps me le permet, histoire de rester dans l'API Python standard et d'avoir au moins une version totalement portable.
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                            Zeste de Savoir, le site qui en a dans le citron !
                                                            Anonyme
                                                              9 septembre 2010 à 8:52:59

                                                              Bonjour,

                                                              voici la phase 2. Vous pouvez donc jouer en mode console, creuser (avec révélation intelligente), et poser des drapeaux.

                                                              Sans plus tarder, voici le code :

                                                              main.py

                                                              #!/usr/bin/env python
                                                              # -*- coding: UTF-8 -*-
                                                              
                                                              
                                                              from plateau import Plateau
                                                              
                                                              
                                                              class Main:
                                                                  """Classe principale du jeu. Gere les I/O
                                                                  c pour mode creuser, d pour mode drapeau, q pour quitter"""
                                                              
                                                                  def __init__(self):
                                                                      """Initialisation de la partie"""
                                                                      self.__flagMode = False
                                                                      self.__taille = 5
                                                                      self.__arreter = False
                                                                      self.__plateau = Plateau(self.__taille)
                                                                      self.jouer()
                                                              
                                                                  def jouer(self):
                                                                      """Fonction pour jouer, affiche l'invite
                                                                      puis appelle la fonction pour trairer l'entrée"""
                                                              
                                                                      invite = "c: creuser - d : drapeau - q quitter\n{0} > "
                                                              
                                                                      while not self.__arreter:
                                                                          print
                                                                          print self.__plateau
                                                              
                                                                          saisieJoueur = raw_input(
                                                                                  invite.format('drapeau' if self.__flagMode else 'creuser'))
                                                                          self.traiterInput(saisieJoueur.strip().lower())
                                                              
                                                                      self.__plateau.revelerMines()
                                                                      print self.__plateau
                                                              
                                                                  def traiterInput(self, chaine):
                                                                      """Permet de traiter l'entrée utilisateur, et appelle les fonctions au besoin
                                                                      inspiré de la fonction écrite par NoHaR :)"""
                                                              
                                                                      if chaine == "q":
                                                                          raise SystemExit
                                                              
                                                                      if chaine == "c":
                                                                          self.__flagMode = False
                                                                          return 0
                                                              
                                                                      if chaine == "d":
                                                                          self.__flagMode = True
                                                                          return 0
                                                              
                                                                      try:
                                                                          lig, col = [int(num) for num in chaine.split()] #splitter et affecter à la fois
                                                                          #si on est en mode drapeau
                                                                          if self.__flagMode:
                                                                              self.__plateau.traiterDrapeaux(lig, col)
                                                                          #non? alors on creuse
                                                                          else:
                                                                              if not self.creuser(int(lig), int(col)):
                                                                                  self.__arreter = True
                                                                                  print "PERDU"
                                                                              if self.__plateau.getNbCasesSansMines() == 0:
                                                                                  self.__arreter = True
                                                                                  print "VICTOIRE"
                                                                      except (ValueError, TypeError):
                                                                          print 'Entrée invalide : 2 nombres attendus'
                                                                      except IndexError:
                                                                          print 'Entrée invalide : Ligne est colonne compris entre 0 et ' + str(self.__taille)
                                                              
                                                                  def creuser(self, ligne=0, colonne=0):
                                                                      """Fonction qui demande à creuser une certaine case"""
                                                                      if ligne < 0 or colonne < 0:
                                                                          print "Erreur d'une coordonnée. 0 est le minimum"
                                                                          return True
                                                                      elif ligne > self.__taille or colonne > self.__taille:
                                                                          print "Erreur d'une coordonnée. " + str(self.__taille) + " est le maximum"
                                                                          return True
                                                                      else:
                                                                          return self.__plateau.creuserCase(ligne, colonne)
                                                              
                                                              if __name__ == '__main__':
                                                                  partie = Main()
                                                              



                                                              plateau.py
                                                              #!/usr/bin/env python
                                                              # -*- coding: UTF-8 -*-
                                                              
                                                              
                                                              from case import Case
                                                              from random import randint
                                                              
                                                              
                                                              class Plateau:
                                                                  """Classe contenant le plateau de jeu"""
                                                              
                                                                  def __init__(self, taille=5):
                                                                      """Initialisation du plateau de jeu"""
                                                              
                                                                      self.__taille = taille
                                                                      self.__plateau = self.creerPlateau()
                                                              
                                                                      self.__nbBombes = 0
                                                                      while self.__nbBombes == 0:
                                                                          self.__nbBombes = self.placerBombes()
                                                              
                                                                      self.__nbCasesSansMines = (self.__taille*self.__taille)-self.__nbBombes
                                                              
                                                                  def creerPlateau(self):
                                                                      """Creer le plateau en tant que tab a deux dimensions et place les bombes
                                                                      puis retourne le tableau"""
                                                              
                                                                      plateau = []
                                                              
                                                                      for i in xrange(self.__taille):
                                                                          plateau.append([None] * self.__taille) #remplir de 0
                                                                      return plateau
                                                              
                                                                  def placerBombes(self):
                                                                      """Place les bombes de manière aléatoire en fonction de la taille
                                                                      et renvoie le nombre au plateau.
                                                                      Il ne peut pas y avoir plus de <taille plateau> bombes"""
                                                              
                                                                      nb=0
                                                              
                                                                      for i in xrange(self.__taille):
                                                                          for j in xrange(self.__taille):
                                                                              r = randint(1, self.__taille)
                                                                              self.__plateau[i][j] = Case()
                                                              
                                                                              if r == self.__taille and nb < self.__taille: #pas plus de <taille> bombes
                                                                                  self.__plateau[i][j].devenirBombe()
                                                                                  nb += 1
                                                                      return nb
                                                              
                                                                  def compterBombes(self, ligne=0, colonne=0):
                                                                      """Compter les bombes pour une case donnée"""
                                                              
                                                                      l_min = max(0, ligne-1)
                                                                      l_max = min(ligne+2, self.__taille)
                                                                      c_min = max(0, colonne-1)
                                                                      c_max = min(colonne+2, self.__taille)
                                                                      bombes = 0
                                                                      for lig in xrange(l_min, l_max):
                                                                          for col in xrange(c_min, c_max):
                                                                              if self.__plateau[lig][col].getEstUneBombe():
                                                                                  bombes += 1
                                                              
                                                                      self.__plateau[ligne][colonne].setNbBombesAutour(bombes)
                                                              
                                                                  def traiterDrapeaux(self, ligne=0, colonne=0):
                                                                      """Place ou retire un drapeau sur la case donnée"""
                                                                      self.__plateau[ligne][colonne].setEstDreapeau()
                                                              
                                                                  def creuserCase(self, ligne=0, colonne=0):
                                                                      """Creuser une case : si pas encore révélée ou pas un drapeau
                                                                      compter les mines autour de cette case,
                                                                      la rendre visible. Si c'est une mine, on retourne false
                                                                      ------
                                                                      v2 : la fonction permet de creuser intelligemment (et est plus lisible)
                                                                      merci NoHaR :)"""
                                                              
                                                                      case = self.__plateau[ligne][colonne]
                                                              
                                                                      if case.getEstDrapeau():
                                                                          return True
                                                              
                                                                      #si la case n'(est pas déjà creusée
                                                                      if not case.getEstVisible():
                                                                          self.compterBombes(ligne, colonne)
                                                                          case.rendreVisible()
                                                                          #si c'est une bombe : perdu
                                                                          if case.getEstUneBombe():
                                                                              return False
                                                                          #ça n'en est pas une
                                                                          else:
                                                                              self.__nbCasesSansMines -= 1
                                                                              #si la case vaut 0, on creuse les voisines
                                                                              if case.getNbBombesAutour() == 0:
                                                                                  #voir fonction compterBombes pour ces 2 boucles en détail
                                                                                  for lig in xrange( max(0, ligne-1), min(ligne+2, self.__taille) ):
                                                                                      for col in xrange( max(0, colonne-1), min(colonne+2, self.__taille) ):
                                                                                          self.creuserCase(lig, col)
                                                                              return True
                                                                      else:
                                                                          return True
                                                              
                                                                  def getNbCasesSansMines(self):
                                                                      return self.__nbCasesSansMines
                                                              
                                                                  def revelerMines(self):
                                                                      """Revèler les bombes du plateau"""
                                                                      for i in xrange(self.__taille):
                                                                          for j in xrange(self.__taille):
                                                                              if self.__plateau[i][j].getEstUneBombe():
                                                                                  self.__plateau[i][j].rendreVisible()
                                                              
                                                                  def __str__(self):
                                                                      """Pour pouvoir afficher le tableau avec un simple print"""
                                                              
                                                                      affichage = "+" + ("-" * 2 * self.__taille) + "-+"
                                                                      ligne = ""
                                                              
                                                                      for i in xrange(self.__taille):
                                                                          ligne = "| "
                                                                          for j in xrange(self.__taille):
                                                                              ligne = ligne + str(self.__plateau[i][j]) + " "
                                                                          affichage = affichage + "\n" + ligne + "|"
                                                                          ligne = ""
                                                              
                                                                      affichage += "\n+" + ("-" * 2 * self.__taille) + "-+"
                                                              
                                                                      return affichage
                                                              
                                                              
                                                              if __name__ == '__main__':
                                                                  print "Veuillez lancer le script 'main.py'"
                                                              


                                                              case.py
                                                              #!/usr/bin/env python
                                                              # -*- coding: UTF-8 -*-
                                                              
                                                              
                                                              class Case:
                                                                  """Classe gérant les cases du plateau de jeu"""
                                                              
                                                                  def __init__(self):
                                                                      """Initialisation d'une case"""
                                                                      self.__estUneBombe = False
                                                                      self.__estDrapeau = False
                                                                      self.__estVisible = False
                                                                      self.__nbBombesAutour = 0
                                                              
                                                                  def devenirBombe(self):
                                                                      """La case est une bombe"""
                                                                      self.__estUneBombe = True
                                                              
                                                                  def rendreVisible(self):
                                                                      """Rend une case visible"""
                                                                      self.__estVisible = True
                                                              
                                                                  def getEstUneBombe(self):
                                                                      return self.__estUneBombe
                                                              
                                                                  def getEstVisible(self):
                                                                      return self.__estVisible
                                                              
                                                                  def setEstDreapeau(self):
                                                                      if self.__estDrapeau:
                                                                          self.__estDrapeau = False
                                                                      else:
                                                                          self.__estDrapeau = True
                                                              
                                                                  def getEstDrapeau(self):
                                                                      return self.__estDrapeau
                                                              
                                                                  def setNbBombesAutour(self, nb=0):
                                                                      self.__nbBombesAutour = nb
                                                              
                                                                  def getNbBombesAutour(self):
                                                                      return self.__nbBombesAutour
                                                              
                                                                  def __str__(self):
                                                                      """Pour faire un print d'une case"""
                                                                      if self.__estVisible:
                                                                          if self.__estUneBombe:
                                                                              return "x"
                                                                          else:
                                                                              if self.__nbBombesAutour > 0:
                                                                                  return str(self.__nbBombesAutour)
                                                                              else:
                                                                                  return " "
                                                                      elif self.__estDrapeau:
                                                                          return "D"
                                                                      else:
                                                                          return "."
                                                              
                                                              if __name__ == '__main__':
                                                                  print "Veuillez lancer le script 'main.py'"
                                                              


                                                              EDIT : c'est devenu le code de la phase 2 suite à une erreur d'édition

                                                              Bonne journée.
                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              [Exercice][Débutant - Intermediaire] Démineur

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown