Programme plante au bout d'un certain nombre de lancements

Résolu/Fermé
Nabi - 27 févr. 2023 à 23:40
 Nabi - 1 mars 2023 à 13:12

Bonsoir,

Je suis un débutant en code et je fais des programmes pour le fun ou pour les cours. J'ai un petit problème avec une histoire de boucle for mais probablement pas aussi facile à régler qu'au premier abord. J'ai mis toutes les explications détaillées dans mon programme en commentaire (ce qui explique l'absence d'accents)

'''
Je ne sais pas si vous connaissez le jeu des batons de Fort Boyard. Dans le doute le but est simple :
2 joueurs s'affrontent et sont face a face devant 20 batons. Chaque joueur chacun son tour peut decider de prendre 1, 2 ou
3 batons (il est oblige d'en prendre). La partie s'arrete lorsqu'il n'y a plus de batons et le but est de ne pas etre le
dernier a jouer, donc le dernier a retirer un baton.

Il se trouve qu'il existe une strategie imparable qui permet au 1er joueur de gagner dans 100 % des cas s'il la connait :
- Commencer par prendre 3 batons
Puis durant toute la partie :
- Si l'adversaire en prend 3 prennez en 1
- S'il en prend 2 prennez en 2 aussi
- S'il en prend 1 prennez en 3

Il est donc impossible de gagner pour le 2e joueur si le premier connait cette strategie. Mais en jouant avec ma famille
j'ai vite remarque qu'ils ne la connaissaient pas et j'ai eu envie de creer un programme qui obeit a cette règle et s'il
joue en premier gagne tout le temps. Après avoir fait cela je l'ai ameliorer pour maximiser selon moi ses chances de gagner
en etant 2e en lui apprenant a profiter de chaque erreur du premier joueur qui lui serait fatale.

J'ai ensuite voulu tester le pourcentage de fois sur lesquels mon programme gagnerai en etant 2e a jouer. Pour ce faire
j'ai simule les coups d'un joueur par des coups aléatoires (effectivement ce n'est pas la meilleure manière car un joueur
réfléchit mais qu'importe).

C'est la que vient mon problème, j'ai lancé ce programme d'abord 100 ou 200 fois avec un boucle for mais j'ai remarqué
qu'au dela d'environ 230 le programme ne se lançait plus. J'ai d'abord pensé qu'il mettait du temps et j'ai donc ajouté
une barre de progression et attendu mais rien ni fait la barre au dela de 230 essais (environ) n'avance pas d'un iota.

'''


# IMPORTS
import random
from time import sleep
from tqdm import tqdm

# INITIALISATION DES VARIABLES
J1 = []
J2 = []

J1_victories = []
J2_victories = []

R3 = [True]

# DEF DU TOUR FAIT PAR LE PROGRAMME (COMMENT IL DOIT REAGIR)
def tour(Nb_batons, Joueur):
  verification = False
  if Joueur == 1 :
    Liste = J1
    if Nb_batons == (20-J1[0]):
      R2 = True
    else :
      R2 = R3[0]
  else :
    Liste = J2
  if Nb_batons != 0 :
    while verification == False :
      R2 = R3[0]
      if Liste == J2 or R2 == False :
        if len(list(Liste)) == 0:
          batons = 3
        else :
          if Liste[-1] == 1 :
            batons = 3
          elif Liste[-1] == 2:
            batons = 2
          elif Liste[-1] == 3:
            batons = 1
        if batons <= Nb_batons :
          verification = True
      else :
        right_move = True
        
        if len(J2) == 0 and J1[0] != 3:
          right_move = False
          if J1[-1] == 2 :
            batons = 1
          else :
            batons = 2
            
        elif len(J2) > 0 :
          Nb_test = Nb_batons
          V3 = False
          while (Nb_test-1) % 4 != 0 :
            V3 = True
            Nb_test = Nb_batons
            if J2[-1] == 1 and J1[-1] != 3:
              right_move = False
              batons = random.randint(1,3)
              Nb_test -= batons
            elif J2[-1] == 2 and J1[-1] != 2:
              right_move = False
              batons = random.randint(1,3)
              Nb_test -= batons
            elif J2[-1] == 3 and J1[-1] != 1:
              right_move = False
              batons = random.randint(1,3)
              Nb_test -= batons
  
            else :
              batons = 2
              break
              
          if (Nb_test-1) % 4 == 0 and V3 == False:
            batons = 2

        else :
          batons = 2
              
        if right_move == False :
          R3.clear()
          R3.append(False)
        if batons <= Nb_batons :
          verification = True
        else :
          R3.clear()
          R3.append(True)
    return batons

# DEF DU TOUR D'UN UTILISATEUR, ICI SIMULE PAR DES COUPS ALEATOIRES ENTRE 1 ET 3
def tour_utilisateur(Nb_batons):
  verification = False
  if Nb_batons != 0 :
    while verification == False :
      batons = random.randint(1,3)
      if batons <= Nb_batons:
        verification = True
    return batons


# DEF DU JEU EN FONCTION DE QUI COMMENCE
def game():
  Nb_batons = 20
  Tour_joueur = 1
  Joueur = 1
  while Nb_batons > 0 :
    if Joueur == 1 :
      if Tour_joueur % 2 != 0 :
        batons = tour_utilisateur(Nb_batons)
        J1.append(batons)
        Nb_batons -= batons
        Tour_joueur += 1
        if Nb_batons == 0 :
          J2_victories.append(1)
      else :
        batons = tour(Nb_batons, Joueur)
        J2.append(batons)
        Nb_batons -= batons
        Tour_joueur += 1
        if Nb_batons == 0 :
          J1_victories.append(1)
    else :
      if Tour_joueur % 2 != 0 :
        batons = tour(Nb_batons, Joueur)
        J1.append(batons)
        Nb_batons -= batons
        Tour_joueur += 1
        if Nb_batons == 0 :
          J1_victories.append(1)
      else :
        batons = tour_utilisateur(Nb_batons)
        J2.append(batons)
        Nb_batons -= batons
        Tour_joueur += 1
        if Nb_batons == 0 :
          J2_victories.append(1)

# LAUNCH
for i in tqdm(range(300)):
  J1.clear()
  J2.clear()
  J1_win = False
  J2_win = False
  R3.clear()
  R3.append(True)
  game()
Nb_victoires_J1 = sum(J1_victories)
Nb_victoires_J2 = sum(J2_victories)
print('J1 a gagne',Nb_victoires_J1,'fois et J2,',Nb_victoires_J2,'fois')

Si quelqu'un à la patience et la gentillesse d'y rejeter un oeil je le remerci d'avance.
Windows / Chrome 110.0.0.0

7 réponses

Pas évident de trouver l'erreur et je n'avais pas envie de m'essayer ...
Tu peux éliminer une cause de boucle infinie en modifiant la fonction suivante:

# DEF DU TOUR D'UN UTILISATEUR, ICI SIMULE PAR DES COUPS ALEATOIRES ENTRE 1 ET 3
def tour_utilisateur(Nb_batons):
    verification = False
    if Nb_batons != 0 :
        essais = 0
        while verification == False  and essais < 1000:
            essais += 1
            batons = random.randint(1,3)
            if batons <= Nb_batons:
                verification = True
        return batons if essais < 1000 else Nb_batons

0

J'ai modifié cette boucle pour la simplifier:
 
          while (Nb_test-1) % 4 != 0 :
            V3 = True
            Nb_test = Nb_batons
            if J1[-1] + J2[-1] != 4:   # Tu ne veux pas que la somme soit 4 ?
              right_move = False
              batons = random.randint(1,3)
              Nb_test -= batons
 
            else :
              batons = 2
              break
 
J'ai fini par faire un copier-coller de ton code. :)
Avec la modification donnée au début, j'ai pu me rendre au moins une fois jusqu'à 300 et c'est l'ordi qui a gagné. :)
Il se peut qu'on soit malchanceux avec randint() et qu'on ne trouve jamais la bonne réponse.
Il faut se parer contre ce côté aléatoire.

edit: quand on entre dans cette boucle, il est impossible d'en sortir.

Dans tour_utilisateur, je retourne le nombre de batons restants. Ce n'est pas correct.
Et pour donner une chance à l'utilisateur, ce serait mieux de retourner 1 à la place.

0

J'ai remplacé la boucle ci-haut par ceci:

          if (Nb_test-1) % 4 != 0 :
            V3 = True
            Nb_test = Nb_batons
            if J1[-1] + J2[-1] != 4:   # Tu ne veux pas que la somme soit 4 ?
              right_move = False
              batons = [3, 0, 1, 2][Nb_test%4]
              Nb_test -= batons
 
            else :
              batons = 2

Je n'ai pas trouvé où ça boucle, mais je me rend plus souvent jusqu'à 300.

Il y a sans doute encore un bug dans la stratégie qui fait boucler le programme.

Et cela dépend des choix aléatoires de l'utilisateur.

0
yg_be Messages postés 23184 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 15 septembre 2024 Ambassadeur 1 533
28 févr. 2023 à 08:12

bonjour,

quand tu partages du code, merci de préciser le langage,comme expliqué ici: https://codes-sources.commentcamarche.net/faq/11288-poster-un-extrait-de-code

Le programme boucle parfois dans tour(), sans jamais sortir de "while verification == False :"  Tu peux facilement déterminer où il boucle en ajoutant des print().

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question

J'ai fait dans mon propre code les modifications que j'ai suggérées.
Et effectivement, je boucle toujours dans la fonction tour() et dans la boucle mentionnée par yg_be.

Je crois que j'étais fatigué pour le dernier code. Ceci est plus simple:

          if (Nb_test-1) % 4 != 0 :
            V3 = True
            Nb_test = Nb_batons
            if J1[-1] + J2[-1] != 4:   # Tu ne veux pas que la somme soit 4 ?
              right_move = False
              batons = (Nb_test + 3) % 4
              Nb_test -= batons
 
            else :
              batons = 2

On peut simplifier encore:
          if Liste[-1] == 1 :
            batons = 3
... les deux autres tests
          batons = 4 - Liste[-1]
Plus tu vas simplifier, plus le bug finira par devenir évident.

0

J'ai modifié le début de la boucle pour limiter le nombre d'essais:
 
    essais = 0
    while verification == False :
      essais += 1
      if essais > 1000:
        print("Erreur pour", Nb_batons)
        exit()
 
Ça plante toujours quand Nb_batons est égal à 1

On pourrait afficher plus de variables pour comprendre.

0

Bonjour à tous,

Merci de m’avoir aidé j’ai fini par régler le problème et tout roule ;)

J’ai été impressionné de voir la réactivité des réponses.

0