Pendu en python

Bonjour, je cherche à programmer un pendu en python avec les débuts de codes suivantes:

def jeu():
    mot = "test"
    dev=[]
    n = str(len(mot))
    print("Le mot contient " + n + " lettres.")
    m = input("Quelle lettre ?")
    if m in mot:
        m = (mot.index(m))
        print(mot[m])
        dev.append(m + 1)
        print(dev)
        jeu()
    else:
        print("La lettre " + m + " n'est pas dans le mot.")
        jeu()

jeu()

Le problème se trouve dans le "m = (mot.index(m))" car il ne gère pas les lettres doubles, par exemple , si on rentre le mot "test", cette ligne retournera qu'il s'agit de la première lettre alors qu'il s'agit aussi de la quatrième 

Comment puis-je régler ce problème ?

Merci d'avance.

8 réponses

  1. Bonjour 

    • tu peux faire une boucle qui va regarder chaque lettre du mot
    • tu peux faire une boucle qui travaille sur la copie du mot, tu y mets tes lignes 7 à 12, puis dans la copie tu places ce qui reste du mot après l'index de la première occurrence 
    • tu peux utiliser les regex

    1. Bonjour,

      Moi, j'avais fait un truc du même genre avec enumerate:

      display = ['_' for k in range(len(mot))]
      
      while(not end):
          print(display)
          m= input('\nUne lettre : ')
          found = False
      
          if(m in mot):
              found = True
              inds = [ind for ind, value in enumerate(mot) if value == m]
              for ind in inds: #if m in mot ne suffit pas: il faut traiter les occurences multiples comme 2 e dans lycee
                  if(display[ind] == '_'):
                      display[ind] = m
                  break
      
          if(not found):
              print("La lettre " + m + " n'est pas dans le mot.")
      1. Ce n'est pas une bonne idée d'assigner la valeur et la position d'une lettre à la même variable.
        D'accord avec phil pour enumerate.
        Ce n'est pas grave ici, mais un jeu récursif n'est pas conseillé. On pourrait déborder la pile.

        1. Alors oui la méthode de Phil est très belle.

          Mais, s'agissant d'un exercice plutôt simple, sur lequel Acnologiaf peine un peu, je pense que c'est encore hors de sa portée.

          Si j'étais prof et que l'on me ramène une solution que l'élève n'aurais pas pu faire lui-même, je lui mettrais une mauvaise note.

          Concernant le côté récursif, avec en principe 26 récursions maximum (sauf si le joueur propose 2 fois la même lettre), il ne devrait pas y avoir de dépassement de pile.

          Par contre, il va falloir veiller à sortir du mode récursif quand le mot est trouvé, et donc faire en sorte que le jeu sache que c'est gagné.

          Ce qui n'est pas le cas pour le moment


          1. Bonjour,

            Oui, c'est vrai que c'est peut-être hors de sa portée

            Pour ce qui est de sortir de la boucle, je faisais ceci:

                if(mot == ''.join(display)):
                    print('\nGagné en {} essais!'.format(nb_essais))
                    end = True

            Idem si le nb d'essais atteignait le nb maxi permis ( if(not found): ..... )

            1. C'est au message de PierrotLeFou que je réagissais.

              D'abord.

              Ce n'est pas une bonne idée d'assigner la valeur et la position d'une lettre à la même variable.

              C'est très juste donc je n'ai rien à redire.

              Puis

              D'accord avec phil pour enumerate

              Sauf que ça n'est pas de son niveau, et là j'explique pourquoi, j'essaye de ne pas donner ce genre de réponse.

              Et enfin 

              Ce n'est pas grave ici, mais un jeu récursif n'est pas conseillé. On pourrait déborder la pile.

              Je n'avais même pas fait attention au fait que la fonction était récursive, là bien vu (si je peux dire).

              Mais contrairement à ce qu'il dit, c'est grave puisque le programme de Acnologiaf n'en sort jamais.


              1. Modérateur

                Bonjour,

                Tout d'abord j'aimerais signaler que le pendu est un exercice qu'on voit régulièrement sur le forum donc une petite recherche préalable est toujours une bonne chose.

                Préliminaires

                Comme python est sensible à la casse (minuscule / majuscule), il est recommandé d'adopter une convention (par exemple ne travailler qu'avec des minuscules ou des majuscules). Dans ce qui suit je suppose qu'on a choisi de travailler avec des minuscules. Une fois cette convention choisie, on peut de manière systématiquement convertir les lettres proposées en minuscules, ce qui évite d'imposer cette contrainte au joueur.

                Pour faire cette conversion, on peut utiliser la méthode lower (pour les minuscules) ou upper (pour les majuscules) :

                mot = "SeCreT"
                print(mot.lower())  # Affiche secret
                print(mot.upper())  # Affiche SECRET

                Retour à la question initiale

                Je suppose ici que mot et propositions suivent la même convention (que des minuscules ou que des majuscules).

                La manière la plus simple est de mémoriser les lettres proposées (variable propositions), puis au moment d'écrire un mot (variable mot), d'itérer sur chaque lettre de mot. Si la lettre courante a été proposée, on l'affiche, sinon on écrit _.

                def offusquer_mot(mot: str, propositions: set) -> str:
                    s = ""
                    for a in mot:
                        if a in propositions:
                            s += a
                        else:
                            s += "_"
                    return s

                ... qu'on écrirait plutôt ainsi si on connaît bien python :

                def offusquer_mot(mot: str, propositions: set) -> str:
                    return "".join(a if a in propositions else "_" for a in mot)

                Qu'est ce qu'un set ?

                Le type set correspond à un ensemble d'éléments distincts.

                Dans la fonction précédente, on pourrait parfaitement ce contenter d'une liste (type list), cela n'empêche pas d'utiliser l'opérateur d'appartenance (in). Cependant, un set est plus efficace et plus pratique. En effet, on peut facilement vérifier si l'ensemble des proposition inclue toutes les lettres impliquées dans le mot à découvrir, ce que ne permettra pas une liste.

                def tester_victoire(mot: str, propositions: set) -> bool:
                    return set(mot) <= propositions

                Si on rassemble tous ces éléments, voici à quoi peut ressembler le jeu :

                def offusquer_mot(mot: str, propositions: set) -> str:
                    s = ""
                    for a in mot:
                        if a in propositions:
                            s += a
                        else:
                            s += "_"
                    return s
                
                def offusquer_mot(mot: str, propositions: set) -> str:
                    return "".join(a if a in propositions else "_" for a in mot)
                
                def tester_victoire(mot: str, propositions: set) -> bool:
                    return set(mot) <= propositions
                
                def saisir_lettre(message) -> str:
                    while True:
                        proposition = input(message)
                        if len(proposition) != 1:
                            print("Vous ne devez saisir qu'un caractère")
                        elif not ('a' <= proposition.lower() <= 'z'):
                            print(f"'{proposition}' n'est pas une lettre")
                        else:
                            break
                    return proposition
                
                def jeu(mot: str, nombre_tours: int = 10) -> bool:
                    propositions = set()
                    mot = mot.lower()
                    for tour in range(1, nombre_tours + 1):
                        proposition = saisir_lettre(
                            f"[{tour}/{nombre_tours}] Saisir une lettre minuscule ? "
                        )
                        propositions.add(proposition.lower())
                        print(offusquer_mot(mot, propositions))
                        if tester_victoire(mot, propositions):
                            print(f"Victoire, le mot était \"{mot}\"")
                            return True
                    print(f"Défaite, le mot était \"{mot}\"")
                    return False
                
                jeu("test")

                Bonne chance

                1. Bonjour,

                  Une version en mode console mais avec un petit graphique:

                  import os
                  import random
                  import colorama
                  colorama.init()
                  
                  POS = lambda x,y: '\033[{};{}H'.format(x,y) #"escape sequence" pour placer le curseur sur ligne/colonne données
                  failures_nb, tries_nb = 0, 0
                  end, found = False, True
                  display = []
                  sketch = {0:[5,50,'_'], 1:[5,52,'_'], 2:[5,51,'|'], 3:[4,51,'|'], 4:[3,51,'|'], 5:[2,51,'_'], 6:[2,52,'_'],
                      7:[2,53,'_'], 8:[3,52,'/'], 9:[3,54,'|'], 10:[4,54,'o']}
                  words = ['voiture','ordinateur','poker','stylo','lycee','trousse','spatule', 'souris', 'menthe','seisme']
                  
                  print('\n***** Jeu du pendu *****\n')
                  word = random.choice(words)
                  
                  input('''Choix  d'un mot au hasard ... appuyer sur Entrée pour continuer''')
                  display = ['_' for k in range(len(word))]
                  
                  os.system('cls')
                  print('\n***** Jeu du pendu *****')
                  while(not end):
                      print('{}{}'.format(POS(3,1),' '.join(display)))
                      letter = input('\nUne lettre : ')
                      tries_nb += 1
                      found = False
                  
                      if(letter in word):
                          found = True
                          inds = [ind for ind, value in enumerate(word) if value == letter]
                          for ind in inds: #if letter in word ne suffit pas: il faut traiter les occurences multiples comme 2 e dans lycee
                              if(display[ind] == '_'):
                                  display[ind] = letter
                                  break
                  
                      if(not found):
                          failures_nb +=1 #lettre pas dans le mot: on commence a dessiner le pendu ...
                          for i in range(failures_nb): print(POS(sketch[i][0],sketch[i][1])+sketch[i][2])
                  
                          if(failures_nb == len(sketch)):
                              print('''{}Perdu ! C'était {}'''.format(POS(5,1),word))
                              end = True
                  
                      if(word == ''.join(display)):
                          print('{}{}\n\nGagné en {} essais!'.format(POS(3,1),' '.join(display),tries_nb))
                          end = True