Le module random contient beaucoup de fonctions permettant de gérer l'aléatoire.
Il existe notamment la fonction randint() qui permet de générer un integer aléatoire entre une valeur de départ incluse et une valeur de fin incluse.
Cette fonction doit recevoir deux entrées : la première est la valeur de départ autorisée, et la deuxième la valeur finale autorisée. Ainsi en fournissant 5 et 8, on génère un nombre aléatoire entre 5 et 8 pouvant valoir 5, 6, 7 ou 8.
Si on veut simuler un d6, un dé à 6 faces, il suffit de taper ceci (notez bien qu'on importe randint() depuis le module, pas tout le module) :
>>> from random import randint
>>> randint(1, 6)
4>>> randint(1, 6)
1>>> randint(1, 6)
6>>> randint(1, 6)
2
B - Exemple d'un programme créant des notes au hasard
Ci-dessous, un programme générant 10 notes aléatoires entre 0 et 20 (notez bien qu'ici, nous importons uniquement le module random) :
1
2
3
4
5
6
7
importrandom# Importation du modulenotes=[0,0,0,0,0,0,0,0,0,0]# Tableau de dix entiersforiinrange(10):# Pour i variant de 0 à 9notes[i]=random.randint(0,20)# Case i contient un nbr entre 0 et 20print(notes)# Affiche lorsque la boucle est finie
qu'avec randint(2, 5), la valeur finale est incluse dans les choix possibles : 2, 3, 4 ou 5.
qu'avec range(5), la valeur "finale" 5 n'est pas incluse : nous allons générer progressivement 0, 1, 2, 3, 4 mais PAS 5.
A vous de le savoir, cela a été défini comme cela.
01° Taper ceci dans la console pour comprendre comment réaliser un tirage aléatoire entre 5 et 10 : le résultat sera toujours soit 5, soit 6, soit 7, soit 8, soit 9, soit 10.
Remarque : attention, ici le 10 est une valeur incluse dans les choix possible. Ce n'est pas le cas avec range, où la valeur "finale" est exclue.
Questions
Que doit-on taper dans la console pour obtenir un résultat simulant un dé à 6 faces ?
Réaliser une fonction de() recevant un paramètre nb qui est le nombre de faces d'un dé et qui renvoie un nombre entier aléatoire entre 1 et nb.
1
2
3
4
5
6
7
8
9
10
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat :: param nb(int) :: un entier >= 1 :: return (int) :: un entier compris entre 1 et nb. """pass
PRECONDITION : lors de l'appel de la fonction, il faudra envoyer un nombre nb qui soit un entier positif strictement supérieur à 1.
...CORRECTION...
>>> random.randint(1, 6)
1
2
3
4
5
6
7
8
9
10
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat :: param nb(int) :: un entier >= 1 :: return (int) :: un entier compris entre 1 et nb. """returnrandom.randint(1,nb)
Ligne 4 : évaluation de la condition de poursuite.
SI son évaluation donne True, on réalise le bloc des lignes 5-6 puis on revient à la ligne 4.
SINON, on quitte et part en ligne 8.
Si on lance le programme :
>>> %Run boucleTQ.py
5120
9
On voit qu'on atteint 5120 euros au bout de 9 mois. Un compte bancaire qui rapporte 100% tous les mois, c'est rentable mais malheureusement totalement imaginaire.
Traduction et déroulé
L1-2 : initialisation à 10 et 0 des variables.
L4 : évaluation à True de la condition car gain vaut 10.
L5-6 : gain passe à 20 et nb_mois à 1.
L4 : évaluation à True de la condition car gain vaut 20.
L5-6 : gain passe à 40 et nb_mois à 2.
...
L4 : évaluation à True de la condition car gain vaut 2560.
L5-6 : gain passe à 5120 et nb_mois à 9.
L4 : évaluation à False de la condition car gain vaut 5120.
L8-9 : affichage des variables
Commençons par un exemple simple : réaliser l'équivalent d'une boucle FOR partant de 0 jusqu'à atteindre 40, de 10 en 10 : 0 puis 10 puis 20 puis 30 puis 40.
02° Placer le programme sur Python Tutor. On notera qu'il existe une connexion automatique depuis le menu Executer de Thonny. Observer son fonctionnement. Pourquoi peut-on dire qu'une boucle TANT QUE est un mélange de boucle POUR et d'instruction conditionnelle ?
1
2
3
4
5
6
7
x=0whilex<40:print(x)x=x+10print("Fin du TANT QUE")
CLIQUEZ ICI POUR VOIR LE DEROULEMENT SEQUENTIEL
x :
x < 40 ? :
...CORRECTION...
Avant chaque nouveau tour de boucle, on évalue la condition, un peu comme avec un IF.
Si la condition renvoie VRAI, on réalise l'action puis on renvoie au début de la boucle où on va évaluer une nouvelle fois la condition pour voir si on recommence, ou pas.
Il s'agit donc d'une sorte de "boucle conditionnelle".
03° Réaliser un programme utiliser un while pour afficher les nombres 15, 35, 55, 75, 95... jusqu'à atteindre 20015 pour le dernier affichage. De 20 en 20 donc.
...CORRECTION...
1
2
3
4
5
6
7
x=15whilex<20016:print(x)x=x+20print("Fin du TANT QUE")
04° Quelle est l'erreur présente ici, qui provoque une boucle infinie ?
1
2
3
4
5
6
7
x=0whilex<31:print(x)x=x+10print("FIN DU TANT QUE")
...CORRECTION...
Il y a une mauvaise indentation de la ligne 5 : l'incrémentation de la variable x n'est plus à l'intérieur de la boucle.
On ne va exécuter cette ligne qu'après être sorti de la boucle.
Le problème ? La condition pour continuer dépend de x, or on ne change jamais sa valeur lors de l'exécution de la boucle.
x vaut 0 au début et garde cette valeur... à l'infini.
Comme vous pouvez le voir, cette structure est plus complexe à utiliser qu'une boucle FOR :
Ligne 1 : il faut d'abord initialiser la variable de boucle x.
Ligne 3 : il faut préciser fournir la condition de poursuite.
Question : pourquoi vouloir utiliser un while alors qu'on peut utiliser un for qui a l'air plus simple à utiliser ?
1
2
3
4
5
x=0whilex<31:print(x)x=x+10
1
2
forxinrange(0,31,10):print(x)
Réponse : Un TANT QUE permet de faire plus de choses qu'une boucle POUR.
2.2 Boucle bornée ou boucle non bornée
Si on connait à l'avance le nombre de fois où on va devoir agir, on peut utiliser une boucle POUR.
La boucle POUR est une boucle bornée.
Si on ne peut pas savoir à l'avance le nombre de fois où on va devoir agir, on utilise une boucle TANT QUE. C'est la condition de poursuite fournie qui va permettre de faire le choix de continuer ou pas.
Si l'action peut potentiellement se dérouler à l'infini, il faut utiliser une boucle TANT QUE.
C'est pour cela que la boucle TANT est une boucle non bornée.
Exemple 1
Un exemple concret avec le remplissage de votre sac pour aller au lycée :
1
2
3
4
5
while vous_avez_encore_des_choses_a_prendre:prendre_objet_suivant()mettre_cet_objet_dans_votre_sac()partir_au_lycée()
Vous allez donc faire les lignes 1-{2-3} en boucle tant que vos affaires ne sont pas totalement faites.
Exemple 2
Un autre exemple : le passage en cuisine pour se nourrir avec l'utilisation du prédicat j_ai_faim() qui renvoie True si vous avez encore faim.
Vous allez donc faire les lignes 3-{4-5-6} en boucle à chaque fois que la fonction prédicat j_ai_faim() renvoie encore et encore True.
05-A° Mettre l'exemple ci-dessous en mémoire. Lancer le programme.
Quelques explications : à chaque tour de boucle
Ligne 10 : on tire 3 fois un dé à 6 faces et on calcule le total obtenu.
Ligne 11 : on affiche ce total (entre 3 (on tire 3 "1" sur les dés par malchance) et 18 (on tire 3 "6" sur les dés par chance).
La condition de poursuite de la boucle est d'avoir obtenu un total inférieur à 16 sur les dés : tant que le total est inférieur à 16, on lance les dés.
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)total=0whiletotal<16:total=de(6) +de(6) +de(6)
print(total)print(f"La valeur obtenue avec les 3 trois dés est {total}")
Questions
Aurait-on pu faire pareil avec une boucle bornée ?
Pourquoi avoir placé 0 initialement dans la variable total sur la ligne 7 ? Quelle est l'intérêt de cette première "fausse" valeur intiale ?
...CORRECTION...
La boucle bornée n'est pas possible ici à cause du côté aléatoire : on ne sait absolument pas combien de lancer de dés il va falloir faire pour obtenir 16, 17 ou 18 et ainsi sortir de la boucle.
On place 0 car on est ainsi certain de pouvoir réaliser au moins une fois la boucle.
05-B° Erreur courante n°1. Quelqu'un a mal copié le programme précédent et a oublié de mettre la ligne 7.
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)whiletotal<16:total=de(6) +de(6) +de(6)
print(total)print(f"La valeur obtenue avec les 3 trois dés est {total}")
Question : Expliquer l'erreur d'exécution qui apparaît alors.
while total < 16:
NameError: name 'total' is not defined
...CORRECTION...
Lorsqu'on arrive en ligne 9, on demande à l'interpréteur d'évaluer l'expression total<16. Or, la variable total n'existe pas en mémoire puisqu'on ne l'a pas initialisée sur la ligne 7 manquante.
L'erreur typique vient du fait qu'en lisant vite, on voit qu'il y a une affectation de total en ligne 10. Oui, mais cela veut bien dire qu'on ne peut pas l'évaluer en ligne 9.
05-C° Erreur courant n°2. Quelqu'un a encore mal copié le programme précédent.
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)totol=0whiletotal<16:total=de(6) +de(6) +de(6)
print(total)print(f"La valeur obtenue avec les 3 trois dés est {total}")
Question : Expliquer l'erreur d'exécution qui apparaît alors.
while total < 16:
NameError: name 'total' is not defined
...CORRECTION...
Même principe : total n'existe pas car ligne 7 on crée la variable totol... Pour un humain, cela pourrait passer. Pas pour l'ordinateur qui n'a aucune compréhension de ce que vous voulez faire.
2.3 Première évaluation de la condition
Les variables nécessaires à l'évaluation du TANT QUE doivent toutes être définies avant la ligne du TANT QUE.
Si la condition est basée sur une variable qui est calculée dans le bloc de la boucle elle-même, comment faire ? Initialiser cette variable avec une "fausse première valeur" de façon à rentrer dans la boucle au départ.
Exemple : en ligne 7, on place 0 dans total, ce qui permet de valider la condition de poursuite : le total est inférieur à 16.
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)total=0whiletotal<16:total=de(6) +de(6) +de(6)
print(total)print(f"La valeur obtenue avec les 3 trois dés est {total}")
Un exemple sur le thème économique :
06-A° Imaginons que vous ayez une somme d'argent à investir. Tous les mois, votre placement vous rapporte 2% d'intérêt par mois (cela veut dire que la somme d'argent est multipliée par 1,02 tous les mois).
Nous voudrions savoir au bout de combien de mois vous allez avoir 2000 euros ou plus sur ce compte.
Question : comment écrire la condition_de_poursuite de l'action laisser_l_argent_sur_le_compte_un_mois_de_plus() ?
Donc, il faut continuer à laisser l'argent tant qu'il y a moins de 2000 euros sur le compte. Il faut donc choisir la proposition C :
whileargent<2000:
laisser_l_argent_sur_le_compte_un_mois_de_plus()
06-B° Voici un algorithme de calcul de la somme d'argent au fil des mois :
On crée un compteur de mois valant 0 initialement
On définit la somme initiale d'argent sur le compte (1200 euros).
TANT QUE l'argent sur le compte est inférieur à 2000 euros :
On multiplie par 1.02 l'argent sur le compte (l'intérêt est de 2 % sur l'exemple)
On incrémente de 1 le compteur de mois
Une fois sorti de la boucle, on affiche la valeur du compteur qui correspond au nombre de mois nécessaires pour atteindre au moins 2000 euros sur le compte.
Questions :
Comprendre le principe de l'algorithme
Calculer (sans faire de programme) la somme sur le compte après 3 mois (3 tours de boucle donc) en plaçant 1200 euros initialement. On prendra un taux d'intérêt de 2 % par mois. Réalisez les calculs en utilisant la console de Python comme une simple calculatrice.
...CORRECTION...
On commence avec un compteur nb_mois faisant référence à 0 et une variable argent faisant référence à 1200 euros.
On fait un premier tour de boucle puisque argent vaut moins que 2000 euros.
On incrémente nb_mois qui passe à 1
La nouvelle somme d'argent vaut 1200*1.02, soit 1224.0 euros.
On affiche la somme sur le compte, 1224.0 euros.
Le programme attend que l'utilisateur valide en appuyant sur ENTREE
On fait un deuxième tour de boucle puisque argent vaut moins que 2000 euros.
On incrémente nb_mois qui passe à 2
La nouvelle somme d'argent vaut 1224.0*1.02, soit 1248.48 euros.
On affiche la somme sur le compte, 1248.48 euros.
Le programme attend que l'utilisateur valide en appuyant sur ENTREE
On fait un troisième tour de boucle puisque argent vaut moins que 2000 euros.
On incrémente nb_mois qui passe à 2
La nouvelle somme d'argent vaut 1248.48.0*1.02, soit 1273.4496000000001 euros.
On affiche la somme sur le compte, 1273.4496000000001 euros.
Le programme attend que l'utilisateur valide en appuyant sur ENTREE
06-C° Lancer le programme ci-dessous pour voir comment il fonctionne. Il faut taper sur ENTREE pour confirmer la poursuite du déroulement.
1
2
3
4
5
6
7
8
9
10
nb_mois=0argent=1200whileargent<2000:nb_mois=nb_mois+1argent=argent*1.02print(f"Il y a maintenant {argent} euros sur le compte")input()print(f"Pour atteindre cette somme, il a fallu {nb_mois} mois.")
Explication de la ligne 8 : la fonction native input() permet simplement ici d'attendre l'appui sur la touche ENTREE pour permettre la suite de l'exécution. Le programme ne repartira donc en ligne 4 qu'après votre appui sur ENTREE.
Question finale
Trouver les lignes du programme qui correspondent aux actions suivantes :
Ligne ? : On crée un compteur de mois valant 0 initialement
Ligne ? : On définit la somme initiale sur le compte.
Ligne ? : Tant que l'argent sur le compte est inférieur à 2000 euros :
Ligne ? : On calcule la nouvelle somme d'argent sur le compte à la fin du mois
Ligne ? : On affiche la somme d'argent sur le compte
Ligne ? : On incrémente le compteur de mois de 1
Ligne ? : On crée une pause en demandant à l'utilisateur d'appuyer sur ENTREE
Ligne ? : Une fois sorti de la boucle, on affiche la valeur du compteur qui correspond au nombre de mois nécessaire pour avoir 2000 euros sur le compte.
...CORRECTION...
Ligne 1 : On crée un compteur de mois valant 0 initialement
Ligne 2 : On définit la somme d'argent initialement sur le compte.
Ligne 4 : Tant que l'argent sur le compte est inférieur à 2000 euros :
Ligne 6 : On calcule la nouvelle somme d'argent sur le compte à la fin du mois
Ligne 7 : On affiche la somme d'argent sur le compte
Ligne 5 : On incrémente le compteur de mois de 1
Ligne 8 : On crée une pause en demandant à l'utilisateur d'appuyer sur ENTREE
Ligne 10 : Une fois sorti de la boucle, on affiche la valeur du compteur qui correspond au nombre de mois nécessaire pour avoir 2000 euros sur le compte.
2 % par mois c'est beaucoup ou pas ?
Pour répondre à la question, vous devriez vous rendre compte maintenant, qu'il suffit de multiplier la somme initiale par 1.02 pendant 12 mois :
Et cela donne presque 1.27, soit un taux d'intérêt de 27 % par an !
Note : plutôt que de taper les multiplications, vous pouvez également juste demander à Python de calculer 1.02 puissance 12.
>>> 1.02 ** 12
1.2682417945625455
2.4 TANT QUE : condition d'arrêt ou de poursuite
False est la valeur complémentaire de True. On passe de l'une à l'autre à l'aide du mot-clé not.
A - Lien entre poursuite et arrêt
Pour résoudre un problème avec un TANT QUE, on utilise une condition de poursuite.
1
2
whilepoursuite:faire_des_chose()
Lorsqu'on réfléchit, on tombe parfois plutôt sur une condition d'arrêt de la boucle... Comment faire alors ? C'est facile :
Si poursuite est True, c'est que arret est False.
Si poursuite est False, c'est que arret est True.
L'inverse est vrai également :
Si arret est True, c'est que poursuite est False.
Si arret est False, c'est que poursuite est True.
On peut donc trouver la condition de poursuite en inversant la condition d'arrêt avec not.
Ecrire arret est équivalent à écrire notpoursuite.
Ecrire poursuite est équivalent à écrire notarret.
B - Deux écritures de la boucle TANT QUE
Pour décrire un même TANT QUE, on a donc deux possibilités d'écriture en fonction de ce qui vient d'abord à l'esprit :
Si vous connaissez la condition de poursuite de la boucle :
1
2
whilepoursuite: faire_des_chose()
Si vous connaissez la condition d'arret de la boucle :
1
2
whilenotarret: faire_des_chose()
C - Exemple
si on sait qu'on doit continuer tant qu'on a moins de 2000 euros sur le compte :
1
whileargent<2000:
cela veut dire qu'on doit s'arrêter dès qu'on a 2000 ou plus :
1
whilenotargent>=2000:
Parfois on trouve facilement les deux conditions (arrêter ou continuer). Parfois, seule l'une des deux conditions est facile à trouver.
C'est pour cela que connaitre la technique du not est important.
D - Cas typique qui cause des erreurs
L'opérateur complémentaire de > est <=
L'opérateur complémentaire de >= est <
07° La condition de poursuite de la boucle était d'avoir obtenu un résultat r strictement inférieur à 16 sur les dés.
La condition d'arrêt est donc d'avoir un résultat de 16 ou plus avec nos dés.
Action : reécrire le TANT QUE en utilisant plutôt la condition d'arrêt. Tester pour vérifier que le programme a exactement le même fonctionnement.
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)r=0whiler<16:r=de(6) +de(6) +de(6)
print(r)print(f"La valeur obtenue avec les 3 trois dés est {r}")
...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12
13
importrandomdefde(nb):"""Lance un dé à nb faces et renvoie le résultat"""returnrandom.randint(1,nb)r=0whilenotr>=16:r=de(6) +de(6) +de(6)
print(r)print(f"La valeur obtenue avec les 3 trois dés est {r}")
08° On veut créer un jeu vidéo.
Le joueur commence sur un premier niveau de jeu relativement facile pour se faire la main.
Lorsqu'il a acquis 500 points, il passera au niveau 2.
Lorsqu'il aura acquis 1000 points, il passera au niveau 3.
Question : Quel est le programme imaginaire (A ou B) adapté à la situation décrite ?
importturtleastrtimportrandomasrddefstylo()->trt.Turtle:"""Crée, déplace et oriente un crayon aléatoirement"""x=rd.randint(-200,200)# on tire une valeur aléatoire en xy=rd.randint(-200,200)# on tire une valeur aléatoire en yangle=rd.randint(-45,45)# on tire un angle aléatoirefeutre=trt.Turtle()# on crée une tortuefeutre.penup()# On lève la pointefeutre.setpos(x,y)# On déplace le crayonfeutre.setheading(angle)# On le tourne dans le bon sensfeutre.pendown()# On abaisse la pointereturnfeutre# Instructions du programme principals=stylo()# on crée un stylo-tortuewhiles.xcor()<300:s.forward(1)
Questions
Remarque ligne 24 : xcor() est une méthode des objets Turtle permettant de récupérer la coordonnée x de l'objet.
Quelles sont les valeurs initiales possibles des valeurs initiales en x et de l'angle de base du "stylo" stocké dans la variable s ?
Avec la valeur initiale de x imposée en ligne 7, pourquoi est-on certain de parvenir à rentrer au moins une fois dans la boucle TANT QUE ?
Avec l'angle créé en ligne 9 (entre -45° et +45°), est-on certain de percuter le "mur" de droite ?
...CORRECTION...
Ligne 7 : x=rd.randint(-200,200) : on voit que x va avoir aléatoirement une valeur entre -200 et +200, donc nécessairement inférieure à 300. Comme la condition pour rentrer dans la boucle est (ligne 23) d'être inférieure à 300, nous sommes certain de rentrer au moins une fois.
Pour l'angle, on a (ligne 9) un angle entre -45° et +45° : on va donc percuter le mur (on ne risque pas d'avoir un stylo qui remonte avec un angle de +90° ou qui redescend avec un angle de -90°).
10° Remplacer la ligne 9 par ceci : l'angle est maintenant aléatoire entre 90° et 180°. La tortue va donc se diriger quelque part à gauche, mais jamais vers la droite.
9
angle=rd.randint(90,180)# on tire un angle aléatoire
Question
Pourquoi obtient-on une boucle TANT QUE infinie ?
...CORRECTION...
On ne pourra jamais percuter le mur de droite car la tortue part à gauche...
Nous ne sortirons donc jamais de la boucle car la condition restera VRAI à jamais.
D'où le terme "BOUCLE INFINIE" : le programme va faire la séquence L23-L24-L23-L24-L23... sans jamais pouvoir s'interrompre.
Voyons maintenant le principe de la boucle infinie.
2.5 - Boucle infinie
Pour créer une boucle qui ne s'arrête jamais, on choisit une condition de poursuite qui ne pourra jamais être évaluée à False. Commme quoi ? C'est simple : True !
1
2
3
whileTrue:faire_un_truc()faire_un_autre_truc()
La condition de poursuite étant juste True, on va donc effectuer L2-L3 à l'infini.
Ce bouclage peut être voulu (comme sur un système embarqué qui ne doit jamais arrêter son programme) ou involontaire. Si c'est involontaire, c'est le drame car on ne pourra pas prendre la main à moins de stopper le programme.
11° Mettre le programme en mémoire.
Lancer la fonctionlancer_animation() depuis la console.
Question 1 : Localiser la boucle et vérifier que l'animation ne s'arrête jamais. Ca peut être long :o)
Question 2 : Quelles sont les fonctions qui sont chargées de gérer ces tâches ?
effacer le dessin de cercle réalisé par l'un des crayons
déplacer le crayon de la balle
redessiner la balle à cet endroit
Question 3 (optionnelle) : Si vous vous sentez assez à l'aise, prenez le temps de comprendre vraiment comment le programme fonctionne.
"""Description des fonctions d'interface proposées--> Fonctions fournissant une réponse :+ application_graphique(dim) : Créé et renvoie la référence d'une application Turtle+ bordure(dim) : Crée et renvoie un objet-Turle ayant dessiné la bordure+ balle(couleur, epaisseur, dim) : Crée et renvoie un objet-Turtle qui servira à tracer le cercle--> Fonctions sans réponse (modification de l'état par effet de bord)+ deplacer(feutre, x, y, angle) : Déplace en (x,y) et réoriente en fonction de angle le feutre+ effacer(feutre) : Efface le dessin réalisé avec ce feutre+ dessiner(feutre) : Dessine un cercle avec le feutre+ gerer_collision_mur(feutre) : Modifie l'angle d'orientation après cas de contact avec une paroi verticale+ gerer_collision_sol(feutre) : Modifie l'angle d'orientation après cas de contact avec une paroi horizontale+ gerer_deplacement(feutre) : déplace et oriente le crayon servant à dessiner la balle, en simulant un déplacement élémentaire+ deplacer_balle(feutre) : Bouge le dessin associé à ce feutre+ lancer_animation() : Crée l'application graphique, les balles et lance l'animation"""# Partie Importationimportturtleastrtimportrandomasrd# Partie Fonctions d'interfacedefapplication_graphique(dim:int)->trt.Screen:"""Créé et renvoie la référence d'une application Turtle"""fenetre=trt.Screen()# création d'un objet-application fenetre.setup(dim*3,dim*3)fenetre.tracer(0)# NEW: affiche uniquement par updatereturnfenetredefbordure(dim:int)->trt.Turtle:"""Crée et renvoie un objet-Turle ayant dessiné la bordure"""feutre=trt.Turtle()# Création de l'objet-Turtlefeutre.color('black')feutre.pensize(10)deplacer(feutre,-dim,-dim,0)foretapeinrange(4):feutre.forward(2*dim)feutre.left(90)returnfeutredefballe(couleur:str,epaisseur:int,dim:int)->trt.Turtle:"""Crée et renvoie un objet-Turtle qui servira à tracer une balle"""feutre=trt.Turtle()feutre.color(couleur)feutre.pensize(epaisseur)feutre.speed(0)x0=rd.randint(-dim,dim)y0=rd.randint(-dim,dim)angle=rd.randint(0,359)deplacer(feutre,x0,y0,angle)feutre.hideturtle()# on n'affiche que les dessins, pas la tortuereturnfeutredefdeplacer(feutre:trt.Turtle,x:int,y:int,angle:int)->None:"""Déplace en (x,y) et réoriente en fonction de angle le feutre"""feutre.penup()# On lève la pointefeutre.setpos(x,y)# On déplace le crayonfeutre.setheading(angle)# On le tourne dans le bon sensfeutre.pendown()# On abaisse la pointedefeffacer(feutre:trt.Turtle)->None:"""Efface le dessin réalisé avec ce feutre"""feutre.clear()
defdessiner(feutre:trt.Turtle)->None:"""Dessine un cercle avec le feutre"""feutre.circle(5,360)defgerer_collision_mur(feutre:trt.Turtle)->None:"""Modifie l'angle d'orientation après contact avec une paroi verticale"""angle=feutre.heading()angle=180-anglefeutre.setheading(angle)defgerer_collision_sol(feutre:trt.Turtle)->None:"""Modifie l'angle d'orientation après contact avec une paroi horizontale"""angle=feutre.heading()angle=-anglefeutre.setheading(angle)defgerer_deplacement(feutre:trt.Turtle)->None:"""déplace et oriente le crayon servant à dessiner la balle, en simulant un déplacement élémentaire"""# Gestion de la coordonné x et des risques de collisions avec les mursx=feutre.xcor()ifx>200orx<-200:gerer_collision_mur(feutre)# Gestion de la coordonné y et des risques de collisions avec plafond / sol y=feutre.ycor()ify>200ory<-200:gerer_collision_sol(feutre)# Déplacement une fois que le feutre a été tourné dans une nouvelle directionfeutre.forward(1)defdeplacer_balle(feutre:trt.Turtle)->None:"""Bouge le dessin associé à ce feutre"""effacer(feutre)# on efface le dessin précédent réalisé avec ce feutregerer_deplacement(feutre)# on modifie les données de la balledessiner(feutre)# on met à jour l'affichage de la balledeflancer_animation():"""Crée l'application graphique, les balles et lance l'animation"""dim=200# De base, on veut un terrain de jeu de 200 x 200app=application_graphique(dim)# création de l'application graphiqueb=bordure(dim)# création de la bordures1=balle("red",3,dim)# création des balless2=balle("blue",3,dim)s3=balle('green',3,dim)s4=balle('magenta',3,dim)s5=balle('orange',3,dim)whileTrue:# BOUCLE INFINIE !deplacer_balle(s1)# déplace la balle 1deplacer_balle(s2)deplacer_balle(s3)deplacer_balle(s4)deplacer_balle(s5)app.update()# NEW : on affiche le ou les dessins juste à ce moment
Nous allons réaliser un jeu demandant en boucle à l'utilisateur de trouver un nombre compris entre 0 et 100, tant qu'il ne trouve pas la bonne réponse.
Nous allons donc réaliser une interface entre notre programme et l'utilisateur humain et nous utiliserons la console, c'est plus facile que de construire une interface graphique.
ORDINATEUR vers UTILISATEUR en utilisant la console :
la fonction native print() affiche des choses dans la console.
UTILISATEUR vers ORDINATEUR en utilisant la console :
la fonction native input() renvoie ce que l'utilisateur tape sur le clavier. L'utilisateur valide sa réponse en appuyant sur ENTREE.
Bref, petit complément.
(RAPPELS) 3.1 Fonction input() pour récupérer les entrées clavier
(RAPPELS) 3.1.1 Principe fondamental
La fonction native input() récupère la réponse reçue au clavier sous forme d'un string. Pour valider la réponse, il faut appuyer sur entrée ↲ (sur l'exemple, on répond Bonjour).
Le problème vient du fait que TOUT ce qu'on récupère depuis le clavier est interprété comme un string. D'où cette situation bizarre si on ne fait pas attention :
Hors Programme : et en cas d'impossibilité de conversion ?
Cela ne tombera pas le jour de l'examen ou dans un DS, mais c'est pratique et nous allons l'utiliser aujourd'hui. Rien à retenir, il suffit de voir un peu commment cela fonctionne.
Il est possible que le string récupéré ne soit pas interprétable en tant qu'entier. Dans ce cas, récupérer un entier devient impossible et cela lève une exception :
>>> reponse = int(input("Veuillez fournir une note : "))
Veuillez fournir une note : cinq↲ValueError: invalid literal for int() with base 10: 'trois'
Le seul moyen de palier à ceci est d'utiliser quelques notions hors programme en NSI. Je choisis ici d'utiliser une structure permettant de tenter d'exécuter un premier bloc d'instructions. Si Python lève une exception pendant la tentative, plutôt que d'interrompre brutalement le programme, il va alors simplement aller dans le second bloc, que le développeur a écrit dans le but de gérer l'exception qu'on vient de détecter. Il s'agit de try except.
1
2
3
4
5
6
7
8
9
defrecuperer_une_note():reponse=""# "Fausse réponse" pour rentre dans le whilewhiletype(reponse)!=int:# TQ reponse n'est pas un entierrep_clavier=input("Note : ")# récupère le choix de l'utilisateurtry:# Essaye de faire ceci :reponse=int(rep_clavier)# transforme rep_clavier en intexcept:# Si cela déclenche une Exceptionpass# on ne fait rien, on passe...returnreponse
Voici une utilisation possible en mode interactif (une fois la fonction mise en mémoire bien entendu)
13° Tester ce programme qui parvient à transformer la donnée fournie par l'utilisateur en integer en utilisant la fonction native int().
1
2
3
4
5
6
7
nom=input("Quel est votre nom ? ")age=input("Quel est votre age ? ")age=int(age)print(f"Bonjour {nom}")print(f"Lorsque vous serez deux fois plus agé, vous aurez {age*2} ans !")
Et un exemple d'utilisation :
Quel est votre nom ? BobENTREEQuel est votre age ? 17ENTREEBonjour BobLorsque vous serez deux fois plus agé, vous aurez 34 ans !
Commençons notre jeu de recherche d'un nombre aléatoire compris entre 0 et 100.
14° Regarder le programme fourni qui permet de jouer et dans lequel il faut trouver un nombre POSITIF. Il manque uniquement la "fausse" valeur initiale à placer dans la variable proposition en ligne 17.
Question : Expliquer pourquoi il n'est pas malin de mettre 0 sur cette ligne 17 cette fois.
# 1 - Importationimportrandom# 2 - Déclaration des constantesMINI=0MAXI=100# 3 - Déclaration des fonctionsdefaffichage(m,p):ifm!=p:print("Raté")else:print("Bien vu !")# 4 - Programme principalmystere=random.randint(MINI,MAXI)proposition=???whileproposition!=mystere:proposition=input("Quel est le nombre mystère à votre avis ? ")proposition=int(proposition)affichage(mystere, proposition)print("FIN")
...CORRECTION...
Mettre 0 n'est pas malin car il est possible que la bonne réponse soit 0 : ce nombre est aléatoirement possible.
Il faudrait placer un nombre impossible à obtenir, comme -1 par exemple : on précise que le nombre à trouver doit être positif.
✎ 15° Expliquer comment fonctionne la boucle proposée sur les lignes 19-22 :
Utilise-t-on directement une condition de poursuite ou indirectement une condition d'arrêt ?
# 1 - Importationimportrandom# 2 - Déclaration des constantesMINI=0MAXI=100# 3 - Déclaration des fonctionsdefaffichage(m,p):ifm!=p:print("Raté")else:print("Bien vu !")# 4 - Programme principalmystere=random.randint(MINI,MAXI)proposition=-1whilenotproposition==mystere:proposition=input("Quel est le nombre mystère à votre avis ? ")proposition=int(proposition)affichage(mystere,proposition)print("FIN")
✌ 16° Modifier la fonction affichage() pour obtenir un jeu plus facile :
on donne des indices sur la valeur du nombre si la valeur n'est pas bonne. Voir l'exemple ci-dessous.
le jeu ne s'arrête pas sur une erreur lorsqu'on tape quelque chose qui ne peut pas être interprété comme un entier.
Finaliser correctement le programme en rajoutant la documentation (sous forme rapide) à la fonction affichage().
Quel est le nombre mystère à votre avis ? 50↲
Le nombre mystère est plus petit
Quel est le nombre mystère à votre avis ? 25 ↲
Le nombre mystère est plus grand
Quel est le nombre mystère à votre avis ? 37 ↲
Le nombre mystère est plus petit
Quel est le nombre mystère à votre avis ? 31 ↲
Le nombre mystère est plus petit
Quel est le nombre mystère à votre avis ? 28 ↲
Le nombre mystère est plus grand
Quel est le nombre mystère à votre avis ? 29 ↲
Le nombre mystère est plus grand
Quel est le nombre mystère à votre avis ? 30 ↲
Bien vu !
FIN
La boucle bornée POUR est programmable naturellement en Python avec un for.
La boucle non bornée TANT QUE est programmable naturellement avec un while.
Néanmoins, on peut remplacer les for par des while.
Ce n'est pas "naturel", une boucle bornée devrait plutôt être implémenté avec un for mais on peut l'implémenter avec while.
Deux fonctions qui de l'extérieur donneront la même chose :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defsomme_v1(n:int)->int:"""Renvoie la somme de 1 + 2 + 3... jusqu'à n."""reponse=0forxinrange(n+1):# on a donc x < n+1reponse=reponse+xreturnreponsedefsomme_v2(n:int)->int:"""Renvoie la somme de 1 + 2 + 3... jusqu'à n."""reponse=0x=0whilex<n+1:reponse=reponse+xx=x+1returnreponse
La version while est plus complexe puisqu'on doit écrire ce que la syntaxe for réalise en sous-main :
Ligne 11 : on doit initialiser la variable de boucle x alors que c'est fait automatiquement dans l'autre version.
Ligne 12 : on doit transformer l'implicite xinrange(n+1) en condition de poursuite explicite x<n+1.
Ligne 14 : on doit incrémenter la variable de boucle alors que c'est fait automatiquemnet avec l'autre version.
17° Tester les deux versions pour vérifier qu'elles font bien la même chose.
On devrait bien calculer ici 1 + 2 + 3 + 4 + 5 = 15.
>>> somme_v1(5)
15>>> somme_v2(5)
15
4.2 TANT QUE : transformer (parfois) un TANT QUE en POUR
A - Cas des fonctions
Puisqu'on sort définitivement de la fonction une fois qu'on rencontre un return, on peut interrompre un POUR avant sa fin réelle si la boucle est à l'intérieur d'une fonction,.
On peut donc parfois programmer une boucle non bornée en utilisant un for associé à return plutôt que d'utiliser directement un while.
Exemple : deux versions d'une fonction qui renvoie le premier mot de plus de 5 lettres trouvés dans une phrase.
On notera qu'on utilise l'espace comme caractère de séparation dans la phrase pour créer le tableau des mots.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defpremier_mot_v1(phrase:'str NON VIDE')->'int|None':"""Renvoie le premier mot de plus de 5 lettres de la phrase"""t=phrase.split(" ")
i=0whilelen(t[i]) <5:i=i+1ifi<len(t):returnt[i]
defpremier_mot_v2(phrase:'str NON VIDE')->'int|None':"""Renvoie le premier mot de plus de 5 lettres de la phrase"""t=phrase.split(" ")
foriinrange(len(t)):iflen(t[i]) >=5:returnt[i]
B - Est-ce malin de faire cela ?
Tout dépend du point de vue.
AVANTAGE du for+return : en terme de programmation, on doit noter moins de choses en utilisant un for associé à return.
DESAVANTAGE du for+return : cela crée une fonction qui possède deux sorties : n'oubliez pas que si Python ne rencontre pas le return de la ligne 8, on sortira de la fonction comme si vous aviez tapé
C - Pas de remplacement pour une boucle infinie
Pour remplacer le while par un for, il faut qu'il existe un nombre maximale de fois où on réalise la boucle. Si ce nombre n'existe pas, c'est à dire qu'il est possible de tourner à l'infini sans jamais sortir, il est bien entendu impossible de remplacer le TANT par un POUR équivalent.
D - Hors d'une fonction ? (Ne pas utiliser en NSI)
Ceci ne doit pas être utilisé dans une copie de NSI. Algorithmiquement, si vous devez faire une action en boucle à interrompre sous condition, on prend un boucle TANT QUE. Point.
Il existe un mot-clé permettant de sortir instantanément d'une boucle : break.
On peut donc utiliser break à la place de return si on est en dehors d'une fonction.
A titre d'exemple, voici un programme qui permet de stocker dans mot le premier de 5 lettres ou plus, s'il existe.
1
2
3
4
5
6
7
phrase="Phrase permettant de réaliser un exemple"mot=Nonet=phrase.split(" ")
foriinrange(len(t)):iflen(t[i]) >=5:mot=t[i]
break
✎ 18° Tester les deux versions du programme en faisant les appels pour une même phrase.
>>> premier_mot_v1("Bon, alors ça marche ou pas ce truc ?")
'alors'>>> premier_mot_v2("Bon, alors ça marche ou pas ce truc ?")
'alors'
Question
Noter un commentaire pour chaque ligne de ces fonctions : les commentaires devront expliquer en français ce que vont chaque ligne de la fonction.
Si vous bloquez, pensez à me contacter.
4.3 Conclusion : BORNEE ou NON BORNEE
Avant de programmer une boucle, il convient de se demander si on a affaire à une boucle bornée ou une boucle non bornée car cela évitera de partir dans la mauvaise direction.
Boucle bornée : for
Boucle non bornée sans nombre maximum de tours prévisible : while
Boucle non bornée avec un nombre maximum prévisible : while ou for+return
✎ 19° Compléter la condition du programme ci-dessous : il génére en boucle le lancer de 3 dés à 6 faces. Vous devez faire relancer tant que la somme des trois dés n'est pas supérieure ou égale à 13.
1
2
3
4
5
6
7
8
9
10
11
importrandomsomme=0whileCONDITION:de1=random.randint(1,6)de2=random.randint(1,6)de3=random.randint(1,6)somme=de1+de2+de3print(f"Les dés donnent {de1} - {de2} - {de3} pour un total de {somme}")
Votre petite soeur ou votre petit frère, ou un cousin ou une cousine, apprend que vous êtes en informatique au lycée. Malheur ! Il n'arrête pas de vous demander de lui fabriquer un jeu. Pour calmer ses ardeurs, vous décidez de lui concevoir un jeu de multiplication : la fonction tire deux nombres au hasard et demande le résultat de la multiplication tant que le résultat n'est pas bon. La fonction renvoie le nombre de tentatives, un au minimum donc.
✎ 20° Compléter la fonction multiplication() pour qu'elle fonctionne correctement.
Il faudra juste trouver la bonne condition à mettre sur le while à la place de True.
importrandomasrddefmultiplication(max_a,max_b):"""Fonction qui tire au hasard deux nombres et demande à l'utilisateur le résultat de leur multiplication, tant que le résultat n'est pas bon ! ::param max_a(int) :: la valeur maximale du premier tirage ::param max_b(int) :: la valeur maximale du deuxième tirage ::return (int) :: renvoie le nombre essai de tentatives """a=rd.randint(1,max_a)b=rd.randint(1,max_b)m=0# m contiendra la valeur que l'utilisateur pense être a*bessai=0whileTrue:m=input(f"Que vaut {a} x {b} = ? : ")m=int(m)essai=essai+1returnessaiprint("Donne le résultat des multiplications suivantes.")nbr=0forxinrange(10):nbr=multiplication(10,10)+nbrprint(f"{nbr} tentatives pour trouver 10 multiplications !")
✎ 21° Créer un programme qui demande "Voulez-vous continuer ?". Tant qu'on ne tape pas "N", le programme doit afficher un message puis reposer la question.