cours07 - sbalev/processing101 GitHub Wiki
Nous avons vaincu les instructions if
simples :
Si ma température dépasse 38, alors aller chez le docteur.
Hourra ! Mais parfois, réaliser simplement une tâche en fonction d'une condition n'est pas suffisant. Par exemple :
Si ma température dépasse 38 OU que je deviens vert, alors aller chez le docteur.
Si je suis piqué par une abeille ET que je suis allergique aux abeilles, alors aller chez le docteur.
Nous aurons très souvent besoin de faire la même chose en programmation :
Si la souris est sur la droite de l'écran ET que la souris est en bas de l'écran, alors dessiner un rectangle dans le coin inférieur droit.
Notre premier instinct serait probablement de traduire le pseudo-code ci-dessus par des instructions if
imbriquées :
if (mouseX > width / 2) {
if (mouseY > height / 2) {
fill(255);
rect(width / 2, height / 2, width / 2, height / 2);
}
}
Nous aurions donc à traverser deux instructions if
afin d'atteindre le code de dessin. Ce code est parfaitement fonctionnel mais il se trouve que l'on peut faire bien plus simple à l'aide de l'opérateur logique ET. Ce dernier s'écrit avec deux "et commerciaux" &&
(attention, un simple &
a aussi un sens en Processing prenez soin d'en utiliser deux).
Le code suivant est équivalent mais utilise l'opérateur logique ET :
if (mouseX > width / 2 && mouseY > height / 2) {
fill(255);
rect(width / 2, height / 2, width / 2, height / 2);
}
Prenons un autre exemple :
Si la souris est dans le tiers gauche OU dans le tiers droit, dessiner un fond blanc.
Bien qu'on puisse faire ceci avec
if (mouseX < width / 3) {
background(255);
} else if (mouseX > 2 * width / 3) {
background(255);
}
c'est beaucoup plus facile d'utiliser l'opérateur logique OU qui s'écrit avec deux barres verticales ||
:
if (mouseX < width / 3 || mouseX > 2 * width / 3) {
background(255);
}
Enfin, le NON logique est écrit avec un point d'exclamation !
.
Si ma température N'est pas supérieure à 38, alors je n'appellerai pas le médecin.
Si je suis piqué par une abeille que je NE suis pas allergique aux abeilles, alors ne pas s'inquiéter.
Par exemple, en Processing :
Si la souris N'est pas pressée, dessiner un cercle, sinon dessiner un carré.
if (!mousePressed) {
ellipse(width / 2, height / 2, 100, 100);
} else {
rect(width / 2, height / 2, 100, 100);
}
L'expression !mousePressed
se lirait "bouton de la souris non pressé". mousePressed
est une variable de Processing qui vaut vrai si le bouton gauche est pressé. C'est une variable booléenne, nous en reparlerons bientôt.
Bien entendu le code ci-dessus est équivalent à :
if (mousePressed) {
rect(width / 2, height / 2, 100, 100);
} else {
ellipse(width / 2, height / 2, 100, 100);
}
Voici les valeurs des opérateurs logiques en fonction de leurs arguments :
x |
y |
x && y |
---|---|---|
false |
quelconque | false |
true |
false |
false |
true |
true |
true |
x |
y |
x || y |
---|---|---|
false |
false |
false |
false |
true |
true |
true |
quelconque | true |
x |
!x |
---|---|
false |
true |
true |
false |
Exercice 7.1. Vrai ou faux ?
- Votre enseignant a deux oreilles et mesure 3 m.
- Votre enseignant a deux oreilles ou mesure 3 m.
- Votre enseignant a deux oreilles et ne mesure pas 3 m.
- Votre enseignant a deux oreilles ou ne mesure pas 3 m.
- Votre enseignant n'a pas deux oreilles et ne mesure pas 3 m.
- Votre enseignant n'a pas deux oreilles ou ne mesure pas 3 m.
Exercice 7.2. Déterminez si les expressions booléennes suivantes sont vraies ou fausses avec x = 5
et y = 6
.
!(x > 6) _______________________________
(x == 6 && x == 5) _______________________________
(x == 6 || x == 5) _______________________________
(x > -1 && y < 10) _______________________________
Exercice 7.3. Bien que la syntaxe soit correcte, l'expression suivante est crétine, pourquoi ?
(x > 10 && x < 5) _____________________________
Exercice 7.4. Écrivez un programme qui implante un rollover (élément graphique qui change d'apparence quand la souris passe dessus). Dans notre cas l'élément en question sera un rectangle qui changera de couleur quand il est survolé par la souris. Voici un peu de code pour démarrer :
int x = 50;
int y = 50;
int w = 100;
int h = 75;
void setup() {
size(200, 200);
}
void draw() {
background(0);
stroke(255);
if (___ && ___ && ___ && ___) {
___
} ___ {
___
}
rect(x, y, w, h);
}
À partir de cet exercice, nous allons essayer de résoudre un problème un peu plus avancé. Nous allons diviser la fenêtre en quatre carrés blancs et faire en sorte que le carré qui contient la souris soit colorié en noir.
Commençons par réfléchir au fonctionnement de notre programme en pseudo-code, en français :
- Créer une fenêtre carrée de 200 x 200 pixels.
- Dessiner un fond blanc.
- Dessiner une ligne verticale et une ligne horizontale pour diviser la fenêtre en quatre quadrants.
- Si la souris est dans le coin supérieur gauche, dessiner un rectangle noir dans ce coin.
- Si la souris est dans le coin supérieur droit, dessiner un rectangle noir dans ce coin.
- Si la souris est dans le coin inférieur gauche, dessiner un rectangle noir dans ce coin.
- Si la souris est dans le coin inférieur droit, dessiner un rectangle noir dans ce coin.
Pour les étapes 3 à 6, nous devons nous poser la question suivante : "Comment savons-nous si la souris est dans un coin donné ?" Pour cela, nous devons développer une conditionnelle particulière. Nous pourrions dire :
Si la coordonnée x de la souris est plus grande que 100 pixels et que la coordonnée y est plus grande que 100 pixels, dessiner un rectangle noir dans le coin inférieur droit.
En tant qu'excellent exercice, et en fonction de l'exercice précédent vous pouvez essayer de transcrire le pseudo-code ci-dessus en code pour Processing. Vous trouverez ci-dessous la solution.
Exemple 7.1. Rollovers multiples
void setup() {
size(200, 200);
}
void draw() {
// étape 1
background(255);
// étape 2
stroke(0);
line(width / 2, 0, width / 2, height);
line(0, height / 2, width, height / 2);
noStroke();
fill(0);
if (mouseX < width / 2 && mouseY < height / 2) {
// étape 3
rect(0, 0, width / 2, height / 2);
} else if (mouseX >= width / 2 && mouseY < height / 2) {
// étape 4
rect(width / 2, 0, width / 2, height / 2);
} else if (mouseX < width / 2 && mouseY >= height / 2) {
// étape 5
rect(0, height / 2, width / 2, height / 2);
} else if (mouseX >= width / 2 && mouseY >= height / 2) {
// étape 6
rect(width / 2, height / 2, width / 2, height / 2);
}
}
À noter qu'on peut remplacer le dernier else if
par un simple else
(si la souris ne se trouve dans aucun des trois premiers quadrants, elle est forcement dans le dernier) :
if (mouseX < width / 2 && mouseY < height / 2) {
rect(0, 0, width / 2, height / 2);
} else if (mouseX >= width / 2 && mouseY < height / 2) {
rect(width / 2, 0, width / 2, height / 2);
} else if (mouseX < width / 2 && mouseY >= height / 2) {
rect(0, height / 2, width / 2, height / 2);
} else {
// quadrant inférieur droit
rect(width / 2, height / 2, width / 2, height / 2);
}
On peut obtenir le même résultat en raisonnant différemment et en utilisant des if
imbriqués :
if (mouseX < width / 2) {
// moitié gauche
if (mouseY < height / 2) {
// moitié supérieure
rect(0, 0, width / 2, height / 2);
} else {
// moitié inférieure
rect(0, height / 2, width / 2, height / 2);
}
} else {
// moitié droite
if (mouseY < height / 2) {
// moitié supérieure
rect(width / 2, 0, width / 2, height / 2);
} else {
// moitié inférieure
rect(width / 2, height / 2, width / 2, height / 2);
}
}
Exercice 7.5. Réécrivez l'exemple 7.1. de façon à ce que la couleur des carrés change de manière progressive du noir au blanc lorsque la souris les quitte. Indice : vous aurez besoin de quatre variables, une pour le niveau de gris de chaque carré.
L'étape suivante après la programmation de rectangles qui se colorent consiste à implanter un bouton ! Après tout un bouton d'interface graphique n'est qu'un rectangle qui change quand on clique dessus. Vous pourriez peut-être penser qu'il est assez décevant dans un environnement informatique de ne pas avoir d'option « insérer un bouton tout fait ici ». En fait cela est possible en utilisant une bibliothèque. Cependant apprendre comment créer des interfaces graphiques est très éclairant.
D'une part cela nous fournit d'excellents exercices sur les conditionnelles et les variables, mais en outre, réutiliser les même vieux boutons que tous les programmes possèdent n'est pas très excitant ! Si les interfaces graphiques vous intéressent, comprendre comment elles sont réalisées est une compétence qu'il vous faut.
Ceci étant dit, nous allons apprendre à utiliser des variables booléennes pour programmer un bouton. Une variable booléenne est une variable qui peut seulement avoir les valeurs vrai ou faux, true
ou false
. Pensez-y comme à un interrupteur qui ne peut être qu'en deux positions : allumé ou éteint. Pressez un bouton et l'interrupteur s'allume. Pressez-le à nouveau et l'interrupteur s'éteint. La variable prédéfinie mousePressed
que nous avons déjà utilisé est une variable booléenne. mousePressed
vaut true
quand le bouton gauche de la souris est pressé, false
sinon.
Ainsi, notre exemple de bouton comportera une variable booléenne dont la valeur initiale sera false
(on part du principe que l'interrupteur est éteint au début) :
boolean button = false;
Notre croquis changera le fond en blanc quand le bouton est pressé et en noir sinon.
if (button) {
background(255);
} else {
background(0);
}
Nous pouvons désormais vérifier si la souris est dans le rectangle du bouton et si son bouton gauche est pressé, en changeant en fonction la valeur de button
à true
ou false
. Voici l'exemple complet :
Exemple 7.2. Pressez ce bouton !
boolean button = false;
int x = 50;
int y = 50;
int w = 100;
int h = 75;
void setup() {
size(200, 200);
}
void draw() {
if (mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h && mousePressed) {
button = true;
} else {
button = false;
}
if (button) {
background(255);
stroke(0);
} else {
background(0);
stroke(255);
}
fill(175);
rect(x, y, w, h);
}
À noter que le premier if-else
de draw()
peut être remplacé par une instruction beaucoup plus succinte et élégante :
button = mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h && mousePressed
Cet exemple, bien que fonctionnel, ne simule pas tout à fait un interrupteur qui allume ou éteint une lumière par exemple. Dès que la souris quitte le bouton, le fond redevient noir.
Pour changer le comportement de notre bouton nous aurons besoin vérifier si la souris est dans le bouton à l'intérieur de mousePressed()
(que nous avons vu au cours 3), et non pas dans draw()
. Rappelez-vous que le code à l'intérieur de mousePressed()
est exécuté uniquement lorsque l'utilisateur clique la souris, alors que le code contenu dans draw()
est exécuté en boucle.
Donc le code de notre interrupteur sera placé dans mousePressed()
de façon à n'être exécuté qu'une fois lorsque la souris sera cliquée. Si la variable button
est à true
nous la placerons à false
et inversement :
if (button) {
button = false;
} else {
button = true;
}
Cependant il y a un moyen bien plus rapide de faire cela :
button = !button;
Car non-vrai vaut faux et non-faux vaut vrai !
Voici donc notre exemple réécrit :
Exemple 7.3. Un interrupteur.
boolean button = false;
int x = 50;
int y = 50;
int w = 100;
int h = 75;
void setup() {
size(200, 200);
}
void draw() {
if (button) {
background(255);
stroke(0);
} else {
background(0);
stroke(255);
}
fill(175);
rect(x, y, w, h);
}
void mousePressed() {
if (mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h) {
button = !button;
}
}
Exercice 7.6. Pourquoi le code suivant ne fonctionne pas correctement quand on le déplace dans draw()
?
if (mouseX > x && mouseX < x+w && mouseY > y && mouseY < y+h && mousePressed) {
button = !button;
}
Exercice 7.7. Dans l'exemple 4.4, un cercle se déplaçait à travers la fenêtre. Changez cet exemple de façon à ce que le déplacement ne commence qu'après que la souris ait été cliquée. Utilisez une variable booléenne.
boolean ___ = ___;
int circleX = 0;
int circleY = 100;
void setup() {
size(200, 200);
}
void draw() {
background(100);
stroke(255);
fill(0);
ellipse(circleX, circleY, 50, 50);
__________
__________
__________
}
void mousePressed() {
__________
}
Exercice 7.8. Transformer les trois LEDs de Chip en boutons :
- Un bouton poussoir rouge qui allonge l'antenne ;
- Un bouton poussoir vert qui raccourcit l'antenne ;
- Un bouton interrupteur bleu qui allume et éteint la caméra.
Voici un peu de code pour commencer :
float chipX;
float chipY;
int longueurAntenne = 40;
boolean cameraOn = false;
void setup() {
size(400, 400);
frameRate(30);
chipX = width / 2;
chipY = height - 90;
}
void draw() {
background(255);
// dessiner Chip
// le corps
stroke(0);
strokeWeight(1);
fill(191);
rect(chipX - 35, chipY, 70, 70);
// les trois boutons
fill(255, 0, 0);
rect(chipX - 25, chipY + 20, 10, 10);
fill(0, 255, 0);
rect(chipX - 5, chipY + 20, 10, 10);
fill(0, 0, 255);
rect(chipX + 15, chipY + 20, 10, 10);
// les roues
stroke(0);
fill(255);
ellipse(chipX - 20, chipY + 70, 20, 20);
ellipse(chipX + 20, chipY + 70, 20, 20);
// la tête
fill(191);
arc(chipX, chipY - 5, 70, 70, -PI, 0, CHORD);
// la caméra
if (cameraOn) {
fill(127, 0, 0);
} else {
fill(0);
}
ellipse(chipX, chipY - 20, 20, 20);
// l'antenne
strokeWeight(3);
line(chipX, chipY - 40, chipX, chipY - 40 - longueurAntenne);
}
Source xkcd
Le problème de Monty Hall est un casse-tête probabiliste. Wikipédia (consultée le 05/10/2022) donne l'énoncé suivant :
Le jeu oppose un présentateur à un candidat (le joueur). Ce joueur est placé devant trois portes fermées. Derrière l'une d'elles se trouve une voiture et derrière chacune des deux autres se trouve une chèvre. Il doit tout d'abord désigner une porte. Puis le présentateur doit ouvrir une porte qui n'est ni celle choisie par le candidat, ni celle cachant la voiture (le présentateur sait quelle est la bonne porte dès le début). Le candidat a alors le droit d'ouvrir la porte qu'il a choisie initialement, ou d'ouvrir la troisième porte.
Les questions qui se posent au candidat sont :
- Que doit-il faire ?
- Quelles sont ses chances de gagner la voiture en agissant au mieux ?
Il est assez facile de démontrer que sans changer son choix initial, le joueur n'a qu'une chance sur trois de gagner, alors qu'en ouvrant la troisième porte, ses chances de gagner sont deux sur trois. C'est ce résultat contre-intuitif qui a rendu le problème célèbre. Notre but sera de vérifier ce résultat par simulation.
Exercice 7.9. Pour vous faire une idée plus claire du problème, commencez par faire un sketch qui vous permet de jouer à ce jeu. Pour vous éviter la tâche extrêmement compliquée de dessiner une chèvre, vous pouvez considérer qu'il y a un cadeau (simple carré rouge) derrière une des portes et qu'il n'y a rien derrière les deux autres. Cela ne change pas la nature du problème.
Votre programme doit :
- Choisir une des portes au hasard et « cacher » le cadeau derrière
- Dessiner les trois portes fermées
- Demander à l'utilisateur de faire son choix initial en cliquant sur une porte
- Ouvrir une porte qui n'est ni celle choisie par l'utilisateur, ni celle cachant le cadeau (s'il y a deux possibilités, choisir au hasard).
- Demander à l'utilisateur de faire à nouveau son choix en cliquant sur une porte
- Ouvrir les trois portes et afficher le résultat de la partie
- Commencer une nouvelle partie lorsque l'utilisateur clique quelque part avec la souris
Exemple : Au début de la partie :
L'utilisateur clique sur la première porte :
L'utilisateur change son choix en cliquant sur la troisième porte :
Vous pouvez utiliser la fonction text()
pour afficher un texte dans la fenêtre.
Jouez quelques parties. Est-ce que vous gagnez plus souvent en changeant la porte ou sans la changer ?
Exercice 7.10. Vous avez réussi ? Votre programme fait combien de lignes ? (sans compter les lignes vides et les commentaires indispensables pour rendre le code plus clair). S'il est trop long, essayez de simplifier votre logique afin de tenir dans une centaine de lignes.
Exercice 7.11. Maintenant oublions les interfaces graphiques et l'interactivité. Votre programme va faire à la fois le présentateur et le candidat. À chaque passage par draw()
, simulez une partie avec et une partie sans changement de porte. Comptez le nombre de victoires dans chacun des deux cas. Au bout d'un grand nombre de parties, disons 1 million, affichez le taux de parties gagnées avec et sans changement de porte. Est-ce que vos résultats correspondent aux prédictions théoriques ?