Dans ce chapitre nous abordons des programmes un peu plus complexes.
Tirage au sort de cartes d'un jeu de 32 cartes.
Le code à saisir :
# nom programme : tirage_carte.py from random import * enseignes = ['pique', 'coeur', 'carreau', 'trèfle'] rang = ['A', 'R', 'D', 'V', 10, 9, 8 , 7] reponse = "O" while reponse in "oO": carte_e = choice (enseignes) carte_r = choice (rang) print('carte tirée : ' , carte_r ,' de ' , carte_e) reponse = input("continuez ? O/N : " )
Étudions le code maintenant.
Ce programme est construit à partir de deux listes. La première liste comprend 4 éléments (les 4 enseignes ou "couleurs")
et la seconde comprend 8 éléments. On utilise donc un jeu de 32 cartes.
Notez que la deuxième liste est ce que j'appelle "mixte" avec des chaines et des nombres.
Observez bien le test qui permet de sortir de la boucle while. En effet l'utilisateur qui veut continuer le tirage peut très bien saisir O ou o.
Dans les deux cas la condition est vraie donc il y a nouveau passage dans la boucle.
Le bloc d'instructions de la boucle comprend 4 lignes, 4 lignes qui doivent tous être indentées par rapport à l'instruction "while".
Ça serait bien que après 5 pioches du joueur, on visualise son jeu.
Nous créons un nouveau fichier et copions le code ci-dessus dans ce dernier qui sera nommé "tirage_carte_bis.py"
Le code de la version améliorée :
# nom programme : tirage_carte_bis.py from random import * enseignes = ['pique', 'coeur', 'carreau', 'trèfle'] rang = ['A', 'R', 'D', 'V', 10, 9, 8 , 7] # jeu stocke dans une liste mon_jeu_liste=[] for i in range(5): carte_e = choice (enseignes) carte_r = choice (rang) #print('carte tirée : ' , carte_r ,' de ' , carte_e) carte_tiree = str(carte_r) + " de " + carte_e mon_jeu_liste.append(carte_tiree) print("mon jeu : ") for i in range(5): print(mon_jeu_liste[i], end=" ")
Le rendu :
mon jeu : 8 de carreau 10 de coeur D de pique D de carreau 9 de pique
La main du joueur est affichée sur une seule ligne alors que cinq instructions "print" on été exécutées ...
Commentons ce code :
Comme le nombre de pioches est connu (5), il faut mieux utiliser la structure "for ... " que la structure "while ...".
À chaque passage dans la boucle il y a tirage au sort d'une carte.
Notez l'emploi de la fonction str() dans l'instruction "carte_tiree = str(carte_r) + " de " + carte_e".
C'est pas pour faire joli ... La variable carte_r peut contenir un entier (7 ou 8 ou 9 ou 10).
Il faut donc convertir cette valeur numérique en string avant de la concaténer avec le contenu de la variable carte_e (qui est de type str)
sinon vous aurez une erreur ...
Le nom de la carte tirée devient un nouvel élément de la liste "mon_jeu_liste".
Le jeu est affiché avec une structure for ... contenant une instruction print().
Notez le paramètrage de la fonction print() ; l'argument end = " " interdit le saut de ligne qui survient normalement après un print.
Ce programme vous montre aussi comment créer une liste vide : liste = [].
Nous verrons plus tard les solutions.
L'IDLE ou l'interpréteur peut vous avertir d'erreurs.
Dans les deux cas, le résultat est le même : il y a ce qu'on appelle une "plantage". La commande ou le programme ne s'éxécute pas.
>>> d = 0 >>> 1/d Traceback (most recent call last): File "", line 1, in ZeroDivisionError: integer division or modulo by zero
C’est la dernière ligne qui doit être lue avec attention. Cette dernière rappelle que la division par zéro est interdite.
Il faut aussi faire attention aux index des listes et se rappeler que la numérotation des listes commence à 0.
>>> liste = [2, 5, 10] >>> liste[2] 10 >>> liste[3] Traceback (most recent call last): File "", line 1, in IndexError: list index out of range
L’exception IndexError indique ici que l’on est allé trop loin dans la numérotation.
Il faut aussi se rappeler que l’on ne peut pas faire d’opérations avec des variables qui n’ont pas été préalablement définies.
>>> x + 2 Traceback (most recent call last): File "", line 1, in NameError: name 'x' is not defined
L’exception NameError nous dit bien que la variable x n’a pas été déclarée.
Pendant guerre les nazis disposaient d'une machine de cryptage appelé ENIGMA.
La clé de cryptage changeait chaque jour.
Les britanniques, à l'initiative du chercheur Alan Turing, réalisèrent un super calculateur électro-mécanique surnommé la "bombe"
pour casser le code avec un certain succès.
Ci-dessous à gauche la fameuse "bombe" et à droite la machine enigma.
En informatique chaque lettre, symbole a un code ASCII. Les logiciels manipulent en fait les codes ASCII des symboles et lettres et jamais ces symboles et lettres.
Les codes 0 à 31 ne codent pas des caractères mais des symboles.
Les codes 65 à 90 représentent les majuscules. Les codes 97 à 122 représentent les minuscules.
La fonction ord(C) renvoie le code ASCII de la lettre C. La fonction chr(n) renvoie la lettre ou symbole associé au code ASCII n
Le code tient en 2 instructions !
# du code ASCII à la lettre correspondante for i in range(65,123): print("code ASCII: ", i, "lettre : " , chr(i))
Lors de son exécution, ce programme retourne 58 lignes avec affichages d'abord des lettres majuscules puis des lettres minuscules.
Produisez le programme qui affiche les caractères correspondant aux codes ASCII de 1 à 64.
Vous constaterez que Python est incapable d'afficher les symboles correspondant aux codes ASCII de 1 à 33.
Le code :
# nom programme code_ascii_ter.py #affiche pour chaque lettre majuscule de l'alphabet latin # de son code ASCII majuscules = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" longueur = len(majuscules) for i in range(longueur): lettre = majuscules[i] #extraction dans la chaine 'majuscules' du caractère de rang i code =ord(lettre) print(lettre, " a pour code ascii : " , code)
La chaine "majuscules" contient 26 lettres. La variable i va successivement prendre les valeurs de 0 à 25 (car longueur vaut 26).
La boucle va être exécutée 26 fois.
A chaque passage dans la boucle on extrait une lettre de la chaine (celle de position i) et on génère son code ASCII grâce à la fonction ord().
Imaginons un cryptage qui consiste à remplacer chaque caractère par son code ASCII.
#cryptage d'un message basé sur le code ASCII #nom programme : cryptage_ascii.py message_clair =input("tapez un message à crypter : ") message_crypte ="" longueur = len(message_clair) for i in range(longueur): lettre= message_clair[i] codage = str(ord(lettre)) message_crypte = message_crypte + codage print ("message cryptée à envoyer") print(message_crypte)
Enregistrez ce fichier sous le nom "cryptage_ascii.py
Message en clair : "attaquez colline delta a 6 am"
Ce qui donne le message crypté suivant :
971161169711311710111432991111081081051101013210010110811697329732543297109
Il faudrait maintenant imaginer le programme de décryptage ou à défaut une table de décryptage ...
Grâce à cette table le destinaire du message pourra le décrypter.
Le code :
#nom programme : table_substitution.py # production table qui permet de traduire un message crypté en ascii alphabet ="abcdefghijklmopqrstuvwxyz 0123456789" longueur = len(alphabet) print('nombre de caractères :' , longueur) table ="" print ("table de substitution") for i in range(longueur): caractere = alphabet[i] #récupérer le caractère correspondant à i dans la chaine 'alphabet' code = ord(caractere) #code ascii de ce caractère cle = str(code) + " = " + caractere +" ; " #clé de décryptage table =table +cle #mise à jour table print (table)
Il ne faut pas hésiter à commenter les lignes de code.
Le rendu du code :
nombre de caractères : 36 table de substitution : 97 = a ; 98 = b ; 99 = c ; 100 = d ; 101 = e ; 102 = f ; 103 = g ; 104 = h ; 105 = i ; 106 = j ; 107 = k ; 108 = l ; 109 = m ; 111 = o ; 112 = p ; 113 = q ; 114 = r ; 115 = s ; 116 = t ; 117 = u ; 118 = v ; 119 = w ; 120 = x ; 121 = y ; 122 = z ; 32 = ; 48 = 0 ; 49 = 1 ; 50 = 2 ; 51 = 3 ; 52 = 4 ; 53 = 5 ; 54 = 6 ; 55 = 7 ; 56 = 8 ; 57 = 9 ;
Remarque : vous constatez que même les chiffres lorsqu'il sont considérés comme des strings ont un code ASCII.
À vous de travailler un peu.
Vous devez décrypter ce message reçu par chaque chef d'unité :
99101115115101122321081013210210111732973249493297109321081013249493210011
732109111105115324949
Le cryptage de chaque caractère par son code ASCII est relativement simple à réaliser mais en contrepartie il est aussi simple à "casser".
Nous allons maintenant aborder une autre technique dite "leet speak".
Le principe est d'utiliser des caractères graphiquement voisins des caractères usuels, par exemple "5" au lieu de "S", "7" au lieu de "T" ,etc.
Le code :
#nom programme cryptage_leet_speak.py message_clair =input("tapez le message en clair : ") message_code = message_clair.replace('s','5') message_code1 = message_code.replace('t','7') message_code2 =message_code1.replace('g','9') message_code3 =message_code2.replace('h','4') message_code4 =message_code3.replace('i', '1') message_code5 = message_code4.replace('u','||') message_code6 =message_code5.replace('e','!') print("message à envoyer : " , message_code6)
Tapez la fameuse maxime de Boileau :
"Tout ce qui se concoit bien s enonce clairement et les mots viennent a la bouche aisement. "
Oui il y a des fautes d'orthographe. Mais il faut éviter les lettres accentuées et apostrophes dans le message en clair.
Ce programme est basé sur la méthode replace() que l'on peut appliquer à une chaine de caractères.
Remarque : lettre "u" remplacée par "||" (double pype) et "e" par " !" (point d'exclamation)
Ce programme "marche" mais sur un plan algorithmique, il est nul ! Il a été écrit rapidement.
Or en programmation il ne faut confondre vitesse et précipitation.
Ce programme est mauvais car d'abord il est trop long et sa maintenance est difficile. Il y a trop de variables utilisées : message_code, ... message_code6.
Cette version améliorée repose sur l'utilisation d'une liste de sous-listes.
#nom programme : cryptage_leet_speak_plus.py message_clair = input("tapez le message en clair : ") cle = [ ['s','5'], ['t','7'], ['g', '9'], ['h', '4'], ['u','||'], ['e','!'] ] for s in cle: ancien =s[0] nouveau = s[1] message_code = message_code.replace(ancien, nouveau) print('message codé : ' , message_code)
Tapez le message en clair : sans methode la pensee erre l action tatonne
message codé : 5an5 m!74od! la p!n5!! !rr! l ac7ion 7a7onn!
Bof … ; pas terrible comme cryptage, ça reste compréhensible.
La liste de listes nommée "clé" comprend 5 sous-listes ; il pourrait y en avoir beaucoup plus.
Donc la boucle "for s in cle" va être exécutée 5 fois.
À chaque passage une des sous-listes est manipulée.
Le programme est désormais bien écrit. Pour complexifier le cryptage il suffirait de rajouter des sous-listes à la liste "cle".
Pour vérifier que vous avez bien compris le codage ci-dessus, vous allez écrire la routine de décryptage.
Dans le même fichier vous aurez le module de cryptage et le module de décryptage.
Le rendu que vous devez obtenir :
Tapez le message en clair mais sans accentuer les "e" : "sans methode la pensee erre et l action tatonne" .
message codé : 5an5 m!74od! la p!n5!! !rr! !7 l ac7ion 7a7onn!
message décryptée : sans methode la pensee erre et l action tatonne
Il serait souhaitable que toutes les voyelles soient cryptées.
Le codage dit de César était une technique de cryptage utilisée sous l'antiquité. Le principe est simple : décaler les lettres de l'alphabet
de une ou plusieurs positions. Par exemple en décalant vers la droite de 2 positions : a devient c, b devient d, ... y devient a et z devient b.
On peut imaginer aussi un décalage vers la gauche d'un pas de 1 : a devient z, b devient a
Le principe est donc simple. Selon un calendrier, connu seulement de l'émetteur et du destinataire, la clé de cryptage peut changer :
décalage droite de 2 puis la semaine suivante décalage droite de 4.
# nom du programme : cryptage_cesar.py alphabet ="abcefghijklmnopqrstuvwxyz" alphabet_2d =alphabet[2:] + alphabet[:2] print(alphabet) print(alphabet_2d) table1 = alphabet + " " table2 = alphabet_2d + " " message_code ="" message_clair = input("tapez le message en clair : ") longueur = len(message_clair) for i in range(longueur): clair=message_clair[i] print (clair) position = table1.index(clair) print (position) code = table2[position] print (code) message_code = message_code + code print('message crypté : ' , message_code)
abcdefghijklmnopqrstuvwxyz cdefghijklmnopqrstuvwxyzab tapez le message en clair : ave cesar a 0 c v 20 x .. message crypté : cxg fguct
Le rendu nous indique que "a" est remplacé par "c" ; "v" par "x" ; etc.
alphabet_2d = alphabet[2:] + alphabet[:2] : dans la chaine "alphabet_2d" les lettres "a" et "b" sont mise en bout de chaine.
Donc on a bien fait un décalage à droite de 2 !
Il faut aussi gérer le caractère " " (espace) ; il faut donc le rajouter en bout des chaines "table1" et "table2".
Simulons le premier passage dans la boucle :
i = 1 message_clair[1] retourne "a" position.index("a") retourne 0 table2[0] retourne "c" message_code = "" + "c"
L'utilisateur doit saisir la règle de décalage : 2 ou 3 ou 4 ou 5.
Le programme s'occupe du reste :
Le rendu, si vous saisissez 3 de décalage :
tapez le décalage 2/3/4 : 3 tapez le message en clair : ave cesar message crypté : dyh fhvdu >>>
Prévoyez un contrôle de saisie dans d (décalage) doit être un élément de la liste [2,3,4].
Astuce programmatique :
... d =1 while d not in [2,3,4]: d =input("tapez le décalage 2/3/4 : ") d =int(d) #saisie avec input toujours de type str ..
L'instruction input() dans une boucle while ...
Ne pas oublier de convertir la saisie effectuée en un entier.
Dans le chapitre suivant : le module turtle (la tortue).