Exercice en Python : Pierre-Papier-Ciseaux

Vous pouvez copier-coller la solution proposée directement dans votre éditeur de programmes Python. Veillez toutefois à retaper vous-même certains programmes afin de vous exercer pour quand vous devrez taper vos programmes personnels.

Le programme ci-dessous est extrait/inspiré du site www.apprendre-en-ligne.net/pj/ppc/Chapitre3.pdf

Connaissez-vous le jeu Pierre, papier, ciseaux ? Cela se joue à deux joueurs. Chacun compte 1, 2, 3 et tend la main en simulant un de ces trois éléments.

  • → Pour Pierre, ce sera le poing fermé;
  • → Pour Papier, ce sera la paume ouverte;
  • → Et pour Ciseaux, ce seront l'index et le majeur tendus.

Qui gagne ???

  • Pierre gagne contre les ciseaux car couper une pierre avec des ciseaux casse les ciseaux;
  • Papier gagne contre la pierre car le papier peut envelopper la pierre;
  • Ciseaux gagne contre le papier car les ciseaux peuvent couper le papier.

 Exercice  : Le programme doit simuler ce jeu Pierre-Papier-Ciseaux, l'utilisateur joue contre l'ordinateur.

L'ordinateur doit demander au joueur quel "coup" il joue sous la forme d'un chiffre (robustesse) : 1 pour Pierre, 2 pour Papier et 3 pour Ciseaux. L'ordinateur tire ensuite au hasard son coup et affiche les résultats, à savoir qui remporte le point pour ce coup. Les points sont accumulés et, le premier qui arrive au total de x points a gagné, la partie s'arrête.

De plus, l'ordinateur doit afficher les coups "en clair", au lieu de 1, l'ordinateur doit afficher "Pierre"... L'ordinateur doit également afficher les cas d'égalité et, dans ce cas et naturellement, ne comptabiliser aucun point.

Voir une solution

Remarque : cette solution tient compte du fait que l'utilisateur ne peut entrer que les chiffres 1, 2 ou 3 pour jouer. Toutefois, ce programme n'est pas protégé s'il/elle tape d'autres caractères que des chiffres, ce programme n'est donc pas suffisamment robuste.

Le programme utilise deux fonctions signalées par la mot réservé "def". Il faut commencer la lecture de ce programme par le programme principal qui se trouve après les fonctions et qui commence par : "ton_score = 0".

# Importation de la bibliothèque pour le tirage au sort
from random import randint

def ecrire(nombre):
     if nombre == 1:
          print("Pierre", end = " ")
     elif nombre == 2:
          print("Papier", end = " ")
     else:
          print("Ciseaux", end = " ")

def augmenter_score (mon_coup, ton_coup):
     global mon_score, ton_score # Pour que ces variables puissent "remonter" vers le programme principal
     if mon_coup == 1 and ton_coup == 2: # Ordinateur → Pierre - Utilisateur → Papier
          ton_score += 1
     if mon_coup == 1 and ton_coup == 3: # Pierre - Ciseaux
          mon_score += 1
     if mon_coup == 2 and ton_coup == 1: # Papier - Pierre
          mon_score += 1
     if mon_coup == 2 and ton_coup == 3: # Papier - Ciseaux
          ton_score += 1
     if mon_coup == 3 and ton_coup == 1: # Ciseaux - Pierre
          ton_score += 1
     if mon_coup == 3 and ton_coup == 2: # Ciseaux - Papier
          mon_score += 1
     if mon_coup == ton_coup: # Égalité
          print("Égalité")

ton_score = 0
mon_score = 0
fin = 5
print ("*** Pierre – Papier – Ciseaux : le premier à", fin, "aura gagné. ***")
while mon_score < fin and ton_score < fin:
     ton_coup = int(input("1 = Pierre, 2 = Papier, 3 = Ciseaux : que joues-tu ? "))
     while ton_coup < 1 or ton_coup > 3:
          ton_coup = int(input("Erreur, une valeur entre 1 et 3 stp : "))
     print("Tu montres :", end=" ")
     ecrire(ton_coup)
     mon_coup = randint(1,3)
     print(" - Je montre :", end=" ")
     ecrire(mon_coup)
     print() # Pour passer une ligne
     augmenter_score(mon_coup, ton_coup)
     print ("Toi :", ton_score, "…… Moi :", mon_score)
print("Le jeu est terminé")
input() # à ajouter si vous faites exécuter le programme directement en Python

Cacher cette solution

 Complément 1  : Truquez maintenant votre programme pour que ce soit l'ordinateur qui gagne à tous les coups... (oh que c'est vilain, mais ça peut arriver... Les jeux de hasard électroniques sont vérifiés afin de ne pas contenir ce genre d'astuces).

Voir une solution

Au moment où l'ordinateur tire son coup au hasard (ligne "mon_coup = randint(1,3)"), il faut faire en sorte que le coup de l'ordinateur ne soit plus tiré au hasard mais qu'il soit toujours gagnant, càd :

  • Si l'utilisateur joue 1, l'ordinateur doit jouer 2;
  • Si l'utilisateur joue 2, l'ordinateur doit jouer 3;
  • Si l'utilisateur joue 3, l'ordinateur doit jouer 1;

Quelle expression mathématique directe (pas une cascade de SI) pourrait-on mettre en place pour que le jeu de l'ordinateur réponde à ce principe ?

Quand on arrive à 3, il faut recommencer à 0. Cela peut se faire grâce au reste de la division entière par 3 : ton_coup % 3

Il suffit alors d'ajouter 1 à la valeur obtenue pour que l'ordinateur gagne à tous les coups.

Il faut donc remplacer la ligne "mon_coup = randint(1,3)" par "mon_coup = (ton_coup % 3) + 1". Les ( ) garantissent la priorité de l'opérateur %.

Essayez pour voir ce que cela donne...

Cacher cette solution

 Complément 2  : Si vous êtes gentil·le, comment feriez-vous pour que ce soit l'utilisateur qui gagne à tous les coups ?

Voir une solution

Le coup de l'ordinateur doit cette fois être inférieur de 1 au coup de l'utilisateur mais en bouclant si on obtient 0, càd :

  • Si l'utilisateur joue 1, l'ordinateur doit jouer 3;
  • Si l'utilisateur joue 2, l'ordinateur doit jouer 1;
  • Si l'utilisateur joue 3, l'ordinateur doit jouer 2;

Quelle expression mathématique directe (pas une cascade de SI) pourrait-on mettre en place pour que le jeu de l'ordinateur réponde à ce principe ?

En informatique, il est souvent intéressant de "penser à l'envers" (out of the box). Plutôt que d'enlever 1, ne pourrait-on ajouter 2 ?

  • a- l'utilisateur donne 1 + 2 = 3 → OK;
  • b- l'utilisateur donne 2 + 2 = 4 → Pas OK, il faut revenir à 1;
  • c- l'utilisateur donne 3 + 2 = 5 → Pas OK, il faut revenir à 2.

A priori, le fait d'utiliser le reste de la division entière permet d'obtenir la bonne valeur dans les cas b et c mais pas dans le cas a, on obtiendrait 0.

Un système serait d'ajouter 2 en deux fois 1 et, entre les deux additions, prendre le reste de la division entière par 3, càd :

  • a- (1 + 1) % 3 = 2 + 1 = 3 → OK;
  • b- (2 + 1) % 3 = 0 + 1 = 1 → OK;
  • c- (3 + 1) % 3 = 1 + 1 = 2 → OK.

Pour que l'utilisateur gagne à tous les coups, on peut donc remplacer la ligne "mon_coup = randint(1,3)" par "mon_coup = ((ton_coup + 1) % 3) + 1".

Essayez pour voir ce que cela donne...

Il y a sans doute d'autres astuces, pouvez-vous en trouver une autre ?

Cacher cette solution

 Complément 3  : Heureusement, de tels scores montrent de façon évidente qu'il y a un trucage dans le jeu. Les concepteurs malhonnêtes introduisent des systèmes permettant d'être plus discrets, comme par exemple le fait que ce soit l'ordinateur qui gagne deux coups sur trois ou trois coups sur cinq. Comment feriez-vous pour introduire un tel système dans le programme ?

Bonne continuation et restez honnêtes !!!

Retour