Les nouvelles technologies pour l’enseignement des mathématiques
Intégration des TICE dans l’enseignement des mathématiques

MathémaTICE, première revue en ligne destinée à promouvoir les TICE à travers l’enseignement des mathématiques.

Programmation événementielle et géométrie dynamique
Pong et Casse-briques

La programmation de jeux graphiques n’est pas nécessairement plus difficile avec un logiciel de géométrie dynamique, qu’avec une plateforme dédiée à la programmation visuelle. Or que sont les murs, raquettes et balles de Pong, sinon des droites, segments et points ? Ces objets sont présents et parfois réactifs dans les micromondes de la géométrie dynamique.

Article mis en ligne le 15 février 2016
dernière modification le 6 août 2019

par Alain Busser, Patrice Debrabant

Cet article est une étude de cas : Le jeu Pong, proposé au programme comme exemple de projet à mener en programmation. Mais par suite d’un lob, une confusion entre Pong et casse-briques a mené à un enrichissement non monétaire : Programmer un casse-briques en plus de Pong.

Dans cet article sans filet, il sera question de raquettes.

Cet article peut être librement diffusé et son contenu réutilisé pour une utilisation non commerciale (contacter les auteurs pour une utilisation commerciale) suivant la licence CC-by-nc-sa (http://creativecommons.org/licenses/by-nc-sa/3.0/fr/legalcode)

Plan de l’article :

  1. Introduction
  2. Le déplacement de la balle et de la raquette
    • A) dans un repère absolu
      • avec CaRMetal
      • avec GeoGebra
    • B) dans un repère mobile (avec Scratch)
  3. Pong complet
    • A) dans un repère absolu
      • avec CaRMetal
      • avec GeoGebra
    • B) dans un repère mobile (avec Scratch)
  4. Pong 3D
  5. Pong multi-joueurs
  6. Pavages et papiers peints
    • A) avec CaRMetal
    • B) avec Scratch
  7. Casse-briques (thème sous-jacent = clonage)
    • A) avec Scratch
    • B) avec CaRMetal
    • C) avec GeoGebra

1) Introduction

La programmation évènementielle est connue des lecteurs de cette revue depuis cet article sur l’introduction de ladite programmation évènementielle en géométrie dynamique. L’article en question décrivait déjà Scratch comme « basé sur les événements ». Que dit le programme de cycle 4 sur cette question ? Voici des passages mis en exergue parce qu’ils semblent tous en lien avec la programmation évènementielle (le programme suggère même de commencer par ce genre de programmation) :

Brevet du jeu de Pong

Le jeu de Pong a été brèveté aux USA sous le numéro 3659284 le 22 mars 1971 sous le nom « television gaming apparatus » : C’était une console de jeu à brancher sur la télé familiale et chaque joueur tenait une boîte reliée à la console, munie de potentiomètres à tourner pour déplacer la raquette (représentée par un gros carré sur l’image ci-dessous) :

Cette autre figure extraite du brevet montre que l’écran de télé est censé représenter une table de ping-pong vue de dessus :

Chaque raquette est donc un rectangle pouvant être déplacé dans une partie restreinte de l’écran, la balle est représentée par un gros pixel, et le rebond de la balle contre une raquette ou un mur (le rebord de l’écran) est modélisé géométriquement de la façon suivante :

  • On calcule la nouvelle position de la balle en ajoutant à l’ancienne position, un vecteur modélisant la vitesse de la balle ;
  • Si l’abscisse est trop petite ou trop grande (sortie de l’écran par la gauche ou la droite), on multiplie l’abscisse du vecteur par -1 ;
  • idem pour les ordonnées
  • On efface l’ancien pixel et on affiche la balle sur sa nouvelle position.

Cet algorithme utilise le fait que le rebond de la balle contre un mur se fait par symétrie par rapport au vecteur normal :

Pong en console

L’éditeur de texte Emacs possède plusieurs jeux que l’on peut jouer en console. Pour lancer le jeu Pong, il faut appuyer sur la touche Alt du clavier (en langage Emacs, elle s’appelle « Meta ») et, sans la relâcher, donner un bref appui sur la touche « x ». Ensuite relâcher Meta, puis entrer au clavier les lettres « p », « o », « n » puis « g ». Ceci produit en bas d’Emacs, ce texte :

M-x pong

Le code source montre que pour les rebonds, c’est la technique du repère fixe (voir ci-dessous) qui est utilisée :

  1.        ((<= pong-y 1)
  2.         (setq pong-yy (- pong-yy)))
  3.  
  4.        ((>= pong-y (- pong-height 2))
  5.         (setq pong-yy (- pong-yy)))

Télécharger

« setq », en Lisp, désigne l’affectation ; les deux premières lignes disent que si l’ordonnée pong-y est plus petite que 1 alors il y a rebond vers le haut, ce qui revient à remplacer l’ordonnée pong-yy par son opposée. Les deux lignes suivantes disent que si l’ordonnée pong-y de la balle est supérieure à la différence entre le haut de l’écran (pong-height) et 2, alors là aussi il y a rebond.

La balle (en rouge ci-dessous) est représentée par un curseur comme on en utilise couramment dans ce genre de logiciel, les raquettes sont représentées chacune par 3 curseurs superposés (on voit celle de gauche, en blanc).

Voilà ce que ça donne sur un Raspberry Pi muni d’un écran à sa taille :

(on aperçoit à droite, le branchement du clavier : Il n’y a pas de souris puisque tout se fait dans la console avec le clavier)

Ce genre de « pong du pauvre » a donné une idée toujours sur Raspberry Pi, en Python.

Dans un premier temps, on va donc considérer un « demi-jeu de Pong » avec une seule raquette et une balle rebondissant entre les murs et la raquette.
Intéressons-nous d’abord à la balle. Voici la figure DGPad de ce que l’on doit obtenir pour la balle et l’aire de jeu (la table) :

La question est de savoir comment on va obtenir ce résultat. Fondamentalement, il y a deux options :

  • travailler dans le repère mobile de la balle ;
  • travailler dans un repère absolu.

Les vecteurs, c’est de la balle !

Dans le cas du repère absolu, avec un logiciel de géométrie dynamique, on va effectuer des calculs de coordonnées en utilisant celles du vecteur vitesse.

  • Euh m’sieur, les vecteurs ils sont plus au programme du cycle 4 !
  • Dites, vous les stagiaires, vous êtes aussi pénibles que les élèves quand il s’agit de poser des questions pénibles qui cassent l’ambiance...
  • Oui mais n’empêche que les vecteurs ils sont pas au programme.
  • Quitte à jouer sur les mots, les élèves doivent « comprendre les effets d’une translation », alors rien n’empêche d’utiliser les vecteurs (ou les nombres que plus tard on appellera leurs coordonnées) qui produisent « les effets d’une translation ».
  • OK M’sieur, mais aucun logiciel de géométrie n’est suggéré par le programme pour la programmation, c’est Sc***ch qui est « fortement recommandé par l’Inspection Générale ». Alors pourquoi ne pas utiliser Sc***ch ?
  • Mais j’allais le faire, soyez patients ! Mais pour l’autre option seulement.
    Il n’y a pas de vecteurs dans Sc***ch (mais, je vous l’accorde, on dispose de coordonnées qui peuvent produire leur effet), et Sc***ch n’est pas une panacée. Faire des calculs de cinématique avec Sc***ch est une souffrance.
  • Mais alors on fait quoi ?
  • On suit l’exposé pour changer ! Et ne me chatouillez pas avec les programmes et le diktat de Sc***ch : à moins de faire des contorsions, dans Sc***ch il n’y a que du graphisme tortue qui est basé sur les coordonnées polaires (distances et angles), lesquelles ne sont pas non plus au programme.
  • Mais comment on fait avec Sc***ch pour rester dans les clous ?
  • On bricole.

2) Le déplacement de la balle et de la raquette

Dans ce contexte, le déplacement du centre M de la balle est régi par la relation $\vec{dM}=dt.\vec{v}$, la vitesse étant supposé constante lors des intervalles de déplacement (si on modifie la vitesse, c’est par sauts).

On obtient donc $\vec{\Delta M}=\Delta t.\vec{v}$ , relation dont il faut il faut arriver à convaincre les élèves (par forcément sous cette forme si vous présentez la séance lors d’une inspection dans le cadre de la réforme).

A) dans un repère absolu

Par exemple, on dira donc plutôt que l’on applique la relation $d=v \times t$ horizontalement et verticalement (étant admis qu’il y a une vitesse horizontale et une vitesse verticale).
Mais c’est encore un peu compliqué. M ayant pour coordonnées (x,y), disons plutôt qu’à chaque itération (et tant que la vitesse n’a pas été modifiée), on augmente x d’une petite quantité a, et y d’une petite quantité b, ce qui donne la conviction d’un mouvement uniforme.

avec CaRMetal

Le script CaRMetal sera le suivant :

NB : ce script demande une version >= CaRMetal 4.0.1
La version 3D (voir plus loin) et certaines figures 2D demandent une version >= CaRMetal 4.0.2

On remarquera la possibilité d’utiliser une syntaxe en français et d’utiliser := en lieu et place de = .
On s’est efforcé dans cet article d’utiliser la syntaxe en français et := plutôt que =.
(On peut rappeler que l’assistant de script génère une syntaxe en français et sans Javascript quand on opte (case à cocher) pour le mode Beginner. Basculer d’un mode à l’autre est possible à tout moment. )

Les plus audacieux construiront un vecteur vitesse v de coordonnées (0.05,0.07) (ce vecteur doit avoir des coordonnées non fixées) et oseront le script suivant :

Passons maintenant à la table.
On va prendre une table au format 8 x 6.

Ce rectangle n’est présent que pour la visualisation. On utilisera plus tard un rectangle noir (= un polygone).

La balle est un disque de rayon r.
Le rebond de la balle sur le mur x==4 se traduira par le script suivant :

Faisons quelques remarques sur ce script :

a) L’instruction Déplacer("M",X("M")+a,Y("M")+b); et l’instruction a:=-a; ont des effets comparables.
La première est dans l’esprit de Sophus, avec une modification in situ.
La seconde est une affectation.

b) L’instruction r:=0.2; n’est pas couplée avec la figure, ce qui est un peu regrettable.
C’est un problème « classique » de CaRMetal que l’on peut résoudre en utilisant la CaRCommande experte getC() qui fait le pont avec le noyau Java de CaRMetal.

  • getC() donne la construction ;
  • getC().find(« c1 ») donne l’élément de nom c1 (ici le disque) ;
  • getC().find(« c1 »).getValue() donne son rayon.

Remarque 1 : ce code pourrait donner lieu à une CaRCommande Rayon. Ce sera fait dans la prochaine version de CaRMetal.

On obtient alors ce script (cela dit, ce raffinement n’est pas absolument indispensable) :

Remarque : dans CaRMetal, le rayon d’un cercle est un élément dynamique. Par conséquent, ce script rendra possible de modifier dynamiquement le rayon (par manipulation directe).

Si on veut rester dans l’esprit de Sophus, on peut utiliser le vecteur vitesse v et utiliser la CaRCommande Déplacer() pour le modifier :

On fait ensuite pareil avec les autres murs.
Et on peut compacter deux instructions en une en utilisant la valeur absolue.

  • Mais m’sieur, c’est la fête aux vecteurs, vot’ truc ! Sont pas aux programmes, les vecteurs...
  • Dans ce cas, on va utiliser les « les effets d’une translation ». Vous l’aurez voulu.

On crée un point A(0 ;0) et un point B(0.01,0.2) (ce point doit avoir des coordonnées non fixées)
A chaque itération, on crée l’image de M par la translation qui transforme A en B (vous avez vu un vecteur ?), et on met son nom dans une variable Javascript P.
Ensuite on déplace M en P, puis on supprime le point image de la translation (dont le nom est dans P).

Reste à ajouter la raquette, qui sera simplement un segment.

Les segments, c’est du racket !

Comme on est en géométrie dynamique, on a la possibilité de déplacer un point libre (qui fera office de poignée de raquette) mais aussi directement un segment (sans poignée).
Par ailleurs, la raquette peut être rivée à un segment horizontal (comme c’est typiquement le cas), mais peut aussi « flotter », voire être inclinée. Envisageons ces différentes possibilités :

1) raquette à poignée (point libre)

On crée un point P qui sera le centre de la raquette.
On crée une expression m qui donnera la demi-largeur de la raquette (et que l’on pourra s’amuser à modifier ainsi que la vitesse dans des versions plus ludiques que celles proposées dans l’article).
Les extrémités du segment-raquette sont des points E1 et E2 aux coordonnés « fixées » :
E1(x(P)-m,y(P)) et E2(x(P)+m,y(P))

Le point P peut être totalement libre ou être un point sur un segment horizontal centré de longueur 8-2m.

2) raquette sans poignée (segment)

La première option, possible avec CaRMetal et GeoGebra, consiste à créer un segment horizontal s reliant des points totalement libres que l’on rendra cachés (sinon le joueur pourrait modifier la longueur de la raquette). Dans ce cas, la raquette est flottante.

CaRMetal (mais pas GeoGebra sauf ignorance) permet aussi de prendre comme extrémités des points sur un segment horizontal centré de longueur 8 tout en gardant le même comportement. On obtient ainsi un Pong classique.

Avec CaRMetal, on a aussi la possibilité de prendre un segment s de longueur fixe w comme raquette. La raquette se déplace alors par ses deux extrémités dissymétriques (ce qui permet de jouer comme Suzanne Lenglen et de monter au filet invisible). C’est une option amusante, mais qui demande un traitement particulier du rebond sur la raquette.

Avant de s’intéresser au rebond sur la raquette, il faut d’abord détecter le choc de la balle sur la raquette. La méthode choisie, basée sur une projection du centre M de la balle sur la droite support de la raquette, sera la même pour toutes les options présentées plus haut. Mais en pratique, elle demandera un tout petit effort supplémentaire dans le cas d’un segment de longueur fixe.

Une expression c (qui aura le sens de « touché ! ») va être utilisée pour détecter la collision.
On utilise la projection H1 de M sur la droite support de la raquette.

  • Si la raquette est horizontale (= sauf pour le segment de longueur fixe) H1 est le point « fixe » de coordonnées (x(M),y(E1)) où E1 est une extrémité de la raquette.
  • Pour le segment de longueur fixe, on crée la perpendiculaire à la raquette passant par M, et on appelle H1 l’intersection de cette perpendiculaire et du segment raquette. Ensuite, on masque la perpendiculaire. Si l’intersection n’existe pas, cela restera cohérent avec le test de collision.

On donne alors à c la valeur (d(M;H1)<v+c1)&&((x(E1)<x(H1))&&(x(H1)<x(E2)))

ou (d(M;H1)<v+c1)&&(d(P;H1)<m) dans le cas d’un segment horizontal.

un dernier rebondissement

Reste à traiter le rebond.
C’est évident dans le cas d’une raquette horizontale, car l’effet est le même que pour un rebond sur un mur horizontal.
On récupère la valeur de c à l’aide de la CaRCommande PrendreValeurExpression.
Enfin, pour ralentir un peu la balle et uniformiser la vitesse, une utilise une pause de 10 ms.

Le script est le suivant :

On ajoute un bouton démarrer pour lancer le script (que l’on déclenche par ce bouton) dans le gestionnaire de script.

Voici le classeur CaRMetal correspondant avec les deux figures de Pong :

pongs classiques - raquette avec poignée & sans poignée
Classeur CaRMetal

On complique un peu le test de collision pour le segment de longueur fixe :
(d(M;H1)<v+c1)&&(((x(E1)<x(H1))&&(x(H1)<x(E2)))||((x(E2)<x(H1))&&(x(H1)<x(E1))))

Autrement dit $d(M,H1)<||\vec{v}||$+ rayon(c1) et H1 est entre E1 et E2.

On calcule alors l’image de la vitesse dans la symétrie axiale par rapport au segment raquette s :

$\vec{v’}=2\dfrac{\vec{E_1E_2}.\vec{v}}{\vec{E_1E_2}^2}\vec{E_1E_2}-\vec{v}$

D’où le script (dans CaRMetal, les segments ont des coordonnées comme les vecteurs, s a les mêmes coordonnés que $\vec{E_1E_2}$), au signe près :

Et le fichier CaRMetal :

Pong avec un segment de longueur fixe
figure dynamique CaRMetal

avec GeoGebra

On va procéder cette fois en utilisant un curseur animé et son script par actualisation.
On crée les différents éléments, et ce curseur t (dont la valeur n’a pas d’importance).
u est un vecteur de coordonnées (0.05,0.07)
La vitesse de la balle est déterminée également par la vitesse d’animation du curseur.

A chaque modification du curseur par l’animation, le script par actualisation est lancé.
On peut alors reprendre et adapter les scripts CaRMetal en adoptant la syntaxe des scripts GeoGebra.

Commençons par la méthode qui exploite les deux coordonnées a et b du vecteur $\vec{dM}$.

Cette fois, tout le script est joué à chaque itération donc l’initialisation de a et b doit se faire à l’extérieur du script. En pratique, on crée un curseur a = 0.05 et un curseur b=0.07
Le script est alors le suivant :

La syntaxe utilisée s’en déduit assez facilement.

On peut maintenant utiliser un vecteur u de coordonnées (0.05,0.07) et réaliser les modifications par des opérateurs :

Enfin, on peut utiliser des transformations au lieu des opérateurs pour le même résultat.
Pour modifier le vecteur u, on utilise une symétrie axiale par rapport à une droite d1 verticale ou par rapport à une droite horizontale d2 (ces droites ont été créées préalablement) :

Rebond sur la raquette

On traite ensuite le rebond sur la raquette en procédant comme précédemment, mais selon la syntaxe des scripts par actualisation.

Dans le premier cas envisagé plus haut (coefficients de vitesse), on obtient ce script :

Et voici le fichier GeoGebra correspondant :

Pong classique avec GeoGebra (bases du jeu)
figure dynamique GeoGebra

On peut aussi donner donner la version vectorielle en utilisant la distance dans le script plutôt que les coordonnées :

Pong classique avec GeoGebra (version vecteurs, bases du jeu)
figure dynamique GeoGebra

B) dans un repère mobile

Dans un repère mobile, la balle est une tortue (ça ne devrait pas être permis, d’utiliser des animaux comme balles !). Alors la vitesse est un paramètre numérique (la norme du vecteur vitesse) et l’angle change au cours du rebond.
Pour le déplacement interactif de la raquette, il est indispensable d’utiliser un logiciel pouvant gérer des événements, que ce soit des événements souris (par exemple dans les logiciels de géométrie dynamique) ou des événements clavier (par exemple dans Sc***ch). Avec une tortue LOGO qui ne gère pas les événements, comme par exemple GéoTortue, on peut juste chorégraphier des ballets de Pong comme l’a fait avec brio Julien Pavageau pour illustrer son article sur GéoTortue.

Dan cet article, on se limitera à utiliser Sc***ch comme une tortue LOGO (ce que l’interface invite à faire).

Voici une figure DGPad illustrant l’effet d’un rebond :

On peut déplacer la balle (bleue) et la poignée définissant sa direction (losange cyan). On voit que la trajectoire commence par coïncider avec la demi-droite ainsi définie, mais seulement jusqu’à l’intersection avec le rebord (en vert). Après le choc, on remplace la demi-droite par sa symétrique par rapport au bord. Les angles par rapport à l’horizontale, des deux demi-droites, sont affichés, ce qui permet de voir qu’ils sont supplémentaires.

Alors pour modéliser le rebond avec Sc***ch, il suffit de dire au lutin « balle » que « lorsque j’atteins un bord », on remplace direction par 180°-direction. Idem lorsque le lutin « raquette » (un rectangle) est touché par la balle.

Sc***ch propose aussi un bloc (pour ne pas dire une boîte noire) « rebondir si le bord est atteint », qui fera la joie des élèves mais dont la valeur pédagogique n’est pas flagrante...

Le Pong de Scratch 1.4

Parmi les exemples fournis avec Scratch 1.4, se trouve, outre un labyrinthe (« pacman »), un jeu « de Pong » :

La raquette (rectangle noir) est un lutin appelé paddle qui suit, à la verticale, la souris :

Quant à la balle, c’est elle aussi un lutin, qui réagit au contact avec la raquette :

C’est un jeu où il faut éviter de laisser tomber la balle par terre. Mais l’usage du son tend à donner un mouvement saccadé à la balle, et il est difficile de reprendre le contrôle de la souris sans faire sortir la raquette. On va donc refaire quelque chose de similaire avec Scratch 2.0.

Ceci dit, en pédagogie de projet, on est habitué à ce que les élèves confondent « expertise des moteurs de recherche » avec « copier-coller ». Le risque est grand, si on lance une activité sur Pong avec Sc***ch, que des élèves, plutôt que créer leur propre Pong, préfèrent phagocyter celui d’un autre, en espérant que cela ne se verra pas. Dans le cas présent cela a déjà été fait, comme on peut le voir ici. La description « comment j’ai fait » est intéressante à lire :

* La palette se déplace juste qu’il est coordonnée X à quelque X de la souris de coordonnées est
* J’ai dessiné la balle en utilisant l’effet de dégradé dans l’éditeur de peinture
* Le plus délicat chose pour moi était de mettre la balle rebondir sur le droit pagaie. J’ai fini par aller sur le site et trouver comment quelqu’un d’autre l’avait fait : « point dans la direction (180 - direction) ». Je trouve toujours cela confus, mais ça marche vraiment bien. Puis-je transformer une quantité aléatoire pour le rendre plus intéressant.

Le style littéraire évoque fortement une traduction en français, d’un fichier écrit dans une autre langue, traduction probablement effectuée par un logiciel. La seconde phrase est un mensonge (la balle est exactement celle du fichier originel) et la troisième éclaire sur l’obtention de la formule (en omettant de dire quel est « le site » en question). Il sera probablement difficile d’expliquer à ce genre d’élèves que copier une formule mathématique sur un site sans chercher à la comprendre, ne relève pas de la compétence « partager ses fichiers ». Et chercher systématiquement la « fraude » dans les projets de tous les élèves d’une classe, sera difficile. On peut imaginer rapidement deux parades possibles :

  1. On laisse tomber Sc***ch et on programme son Pong dans un autre langage, où le projet n’a pas déjà été mené (idéalement, un outil original comme processing ou Pharo). Euh oui M’sieur mais si dans le sujet du brevet il y aura du Sc***ch alors il vaut mieux que je travaille avec Sc***ch non ?
  2. On laisse tomber Pong et on s’attaque à d’autres activités, par exemple celles qui sont proposées au programme (table de conjugaison, algorithme d’Al Kindi etc). Bref, on ferait des maths au lieu de faire du projet. Euh oui M’sieur mais pourquoi vous avez rédigé tout un article sur Pong si vous préférez faire des maths à la place ? Eh bien, parce que programmer son propre Pong, c’était fun, et le fun, ça se partage (copier-coller le Pong d’un autre, c’est beaucoup moins fun)...

D’autres réactions sont possibles, comme valoriser le copier-coller, ne pas faire de programmation du tout, changer la façon de travailler (donner des fichiers Sc***ch tout faits mais comportant des erreurs, à corriger, ou donner un fichier calculant la table de valeurs d’une fonction et demander de le modifier pour avoir la table de valeurs d’une autre fonction...).

Dans la suite, on va montrer comment un Pong peut être créé ex nihilo avec Sc***ch 2.0 :

La base avec Sc***ch : Balle et « paddle »

Le jeu de base comporte deux lutins : La raquette (un rectangle orange) et une balle de tennis. La forme des lutins a de l’importance puisque l’un des évènements utilisés sera le contact entre les lutins. Ce contact est détecté par le fait que l’intersection entre leurs « costumes » est non vide. On a donc dessiné un rectangle pour la raquette, avec l’éditeur de costumes. Quant à la balle, il se trouve que Sc***ch en fournit une sous la forme d’une photo détourée de balle de tennis, et elle portera donc le doux sobriquet de « tennis ball ». Avec un fond également dessiné, ça donne ceci :

La raquette connaît deux sortes d’évènements (en dehors du célèbre clic sur le drapeau vert) : Le contact avec le mur gauche (abscisse trop petite), et le contact avec le mur droit (abscisse trop grande). Elle ne répond aux touches portant des flèches que par modification de son abscisse et si on veut « tricher » (en mode construction seulement, c’est presque dommage), il faut discrètement glisser la raquette à l’aide de la souris. Voici donc le script de la raquette :

En bref :

  • lorsque le drapeau vert est cliqué, la raquette se positionne au milieu en bas ;
  • lorsque la flèche gauche est « cliquée » (c’est eux qui le disent), l’abscisse de la raquette est diminuée de 8 pixels, mais seulement si aucun bord n’est touché, auquel cas la raquette sortirait de l’écran.
  • lorsque la flèche droite est « cliquée », l’abscisse de la raquette est augmentée de 8 pixels, là aussi seulement si aucun bord n’est touché.

Quand à la balle, elle a (là encore, outre le drapeau vert) 2 sortes d’évènements à gérer : Le choc contre un bord de l’écran, et le choc contre la raquette.

Lorsque le drapeau vert est cliqué, la raquette se place et s’oriente en une position de départ, puis entre dans une boucle sans fin, dans laquelle elle ne fait qu’avancer de 6 pixels à la fois, sans cesse. Mais [1]

  • Si elle touche un bord, elle rebondit (cette instruction est fournie par Sc***ch) ;
  • Si elle touche la raquette, son orientation est remplacée par le supplémentaire de celle-ci, comme le montre la figure DGPad ci-dessus, avec la symétrie axiale.

projet Scratch

amélioration par le calcul parallèle

Tout cela serait très bien n’était la grimace des élèves 2.0 devant l’écran : le jeu est trop lent, la raquette rame (« pas étonnant qu’elle s’appelle paddle ! », dira l’élève près du radiateur).
On est confronté à un des problèmes de Sc***ch : la lenteur générée par le parallélisme des fils (thread) d’instructions dans certaines circonstances.

Plus précisément ici, les fils déclenché par l’événement « flèche cliquée » posent problème. La cadence de test est gérée par Sc***ch, et elle est trop lente en pratique.
Comme indiqué dans cette note, il est recommandé de procéder autrement pour accélérer la cadence de test.

Reprenons le script de la raquette. Il y a trois fils d’instruction.
On pourrait faire en sorte qu’il n’y en ait qu’un pour obtenir ce script, qui est plutôt moins élégant que le précédent, mais plus rapide en pratique. C’est fâcheux, quand-même...

projet Scratch

Par ailleurs, un élève pourrait vous demander pourquoi on n’a pas géré la raquette comme une tortue LOGO, faisant ainsi coexister deux types de référentiels [2] de nature différente (mais il est plus probable, il faut le reconnaître, qu’il fasse miauler un chat quand vous avez le dos tourné). Soit.

projet Scratch

Enfin, dans la phase de conception, il y a la rondeur de la balle, qui n’aide pas à la compréhension de sa direction. Il est sans doute préférable de commencer par un lutin tortue, et lui donner un costume de balle uniquement pour le gala de fin d’année.

Avec Sc***ch, on a fait le choix de travailler en repère mobile, mais il serait aussi possible de travailler dans un repère absolu en adaptant les grandes lignes du script CaRMetal.
Denis Beaubiat l’a réalisé sur cette page avec Blockly.
Il est intéressant de constater de visu que dans ce cas le script Blockly est une traduction quasi-littérale (ou l’inverse) du script écrit en HTML 5 pour la balise canvas. Le rendu visuel du déplacement de la balle est obtenu en répétant :

tracer le rectangle de fond (= tout effacer)
tracer la balle à sa nouvelle position

Cette mécanique élémentaire et fondamentale est souvent utilisée sans que l’on s’en rende compte dans les logiciels graphiques permettant de coder à un niveau d’abstraction « supérieur ».

3) Pong complet

A) dans un repère absolu

avec CaRMetal

Gérer le score et la fin de partie se fait de la même façon pour les différentes variantes de Pong présentées plus haut. On va le faire pour la version la plus simple (raquette avec poignée)

Pour gérer le score :
On crée une expression score égale à 0.
On crée un texte caché nommé bye qui affiche « Game over !!! ».

  • Si on touche le mur du haut, on augmente le score de 1.
  • Si on « touche le mur du bas » (plus exactement si on va trop bas) on montre bye et on sort de la boucle infinie par un break. On peut aussi créer un « booléen » fini qui sera initialisé à « false » et qui ne devriendra « true » que lorsque la balle arrive en bas de l’écran. On remplace alors while(true) par while(!fini), le point d’exclamation désignant la néagation, et la boucle se fait alors tant que le jeu n’est pas fini, ce qui est putôt logique.

Ensuite, on crée une expression fin égale à 0 et un nouveau bouton arrêter qui déclenche un nouveau script ping qui a pour seul effet de mettre fin à 1 (vrai).

Dans le script principal pong, on initialise tout ce qu’il faut avant la boucle.

Voici les deux scripts :

et la figure CaRMetal :

Pong simple complet - raquette avec poignée
figure dynamique CaRMetal

avec GeoGebra

On commence par créer un cursor sc pour le score. La valeur de ce curseur est affichée dans un texte tScore, le curseur lui-même étant rendu invisible.

Dans le script d’actualisation de t, on découple le rebond sur les murs horizontaux pour ne garder que celui du haut pour la condition y(M)>=3 - r.
La même condition produit l’incrémentation du score sc.

Pour la sortie par le bas, la condition y(M)<-4 provoque la fin de l’animation (= la fin de partie).
On crée un texte gameOver à visibilité conditionnelle avec la même condition.

Propriété du texte gameOver :

A ce stade, on peut jouer, mais on aimerait pouvoir rejouer.
Il faut donc pouvoir gérer l’initialisation de la position de M et du score dans un autre script. Ce script est déclenché par un bouton Relancer et relance aussi l’animation :

L’abscisse de M est un entier choisi au hasard entre -3 et 3.
Son ordonnée est mise assez haut pour éviter une percussion avec la raquette.
La vitesse est réinitialisée (on pourrait affiner).

Voici le fichier GeoGebra :

Pong complet avec GeoGebra (version vecteurs)
figure dynamique GeoGebra

B) dans un repère mobile

avec Sc***ch

Le principe est très similaire à ce qui a été vu avec CaRMetal :

  • on crée deux variables fini et score, cette dernière étant affichée dans le jeu ;
  • chaque fois que le mur du fond est touché, on incrémente la variable score (initialisée à 0) ;
  • dès que le mur du bas est touché, on positionne à 1 la variable fini ;
  • il suffit de remplacer la boucle sans fin par une boucle conditionnée par la nullité de fini, pour que le jeu s’arrête quand il doit s’arrêter.
  • La balle dit alors que le jeu est fini :

Pour jouer, on pourra suivre ce lien
Cela dit, le jeu ne casse pas des briques. [3]

Voici, pour comparaison avec ce qui précède, le script complet de la balle de tennis :

Remarques :

  1. les variables de Sc***ch étant numériques, la variable fini, qui aurait dû être booléenne, est simulée numériquement par la correspondance 0=faux, 1=vrai. C’est couramment pratiqué dans des langages comme bash ou C...
  2. La détection du contact avec le mur du fond se fait traditionnellement, en Sc***ch, par la création d’un lutin spécial représentant le mur, et la détection du contact avec ce lutin : ici on a préféré dire que la balle touche le mur du fond lorsque son ordonnée est supérieure à 162, paramètre déterminé empiriquement.
  3. De même, la détection de la sortie de court se fait par comparaison entre l’ordonnée de la balle et -160.

4) Pong 3D

CaRMetal 4.0.2 permet de réaliser un pong 3D, qui s’apparente plus au squash qu’au tennis de table, en reprenant les idées utilisées en 2D.
La difficulté consiste à déplacer la raquette dans l’espace et à créer des artifices de visualisation pour rendre plus perceptible le mouvement de la balle.
Pour simplifier, la balle est un gros point (de forme disque).

On propose ici deux variantes (toujours avec une raquette à poignée) :

  • un Pong classique, où le point-poignée se déplace dans un polygone horizontal. On peut alors jouer sans déplacer le repère mobile. On a ajouté les impacts de balle sur le mur du haut.

Voir le script principal

SetIconSelected est une nouvelle CaRCommande de CaRMetal 4.0.2 qui permet de modifier les préférences (ici, pour créer un point en forme de disque). En fait, c’est comme si on appuyait par script sur un bouton de l’interface. La troisième instruction fait revenir en mode déplacement.
impacts est un tableau Javascript qui contient les noms des points d’impacts. On supprime ces points en fin de partie.

  • un Pong avec raquette « flottante » où le point-poignée est un point libre. Cette version permet d’appliquer (et de comprendre) le déplacement 3D dans CaRMetal 4. Pour réussir à atteindre la balle, on doit déplacer le point-poignée et le repère mobile.

Le classeur suivant contient les deux variantes :

Pong 3D : deux variantes
classeur CaRMetal

5) Pong multi-joueurs

Comme il n’y a pas de raison que les profs de maths soient les seuls à jouer en codage, voyons ce que dit le programme de technologie du cycle 4, et qui pourrait être intéressant à faire en EPI [4] :

Ceci, quoique d’apparence exotique et difficile, peut être assez facilement mis en œuvre avec CaRMetal :

  • L’un des joueurs démarre un serveur dans CaRMetal, dans le menu « réseau » activé depuis une figure contenant un Pong à deux raquettes ;
  • Cela lui fournit une suite de chiffres qui est son adresse IP ; il la transmet à l’autre joueur ;
  • ce joueur démarre une session réseau, en fournissant l’adresse IP lorsque c’est demandé. Dès lors, les deux joueurs manipulent le même figure CaRMetal ; ils se mettent d’accord pour se partager les deux raquettes...
  • ... et n’ont plus qu’à jouer.

Le joueur qui sert (= qui fait serveur ;)) doit paramétrer sa box pour la redirection réseau.
Il faut chercher quelque chose qui s’appelle NAT PAT dans les paramètres de la box.
Ensuite, il faut ajuster les paramètres pour que tout ce qui arrive sur le port 2357 soit redirigé sur l’ordinateur (repéré par son IP locale) sur ce même port.
Que l’on se rassure, sur les box récentes, c’est assez intuitif.

Comme le jeu est en réseau, il n’y a, sous réserve que la connection internet soit bonne, pas de limite de distance entre les deux joueurs.

Voici un pong deux joueurs pour jouer en réseau (la balle accélère progressivement, on pourrait imaginer d’autres variantes, comme réduire la taille m des raquettes) :

Pong deux joueurs
figure dynamique CaRMetal

6) Pavages et papiers peints

Avant de s’intéresser au casses-briques, on va faire le mur.

  • Dessine-moi un mur, demanda le Petit Prince (qui s’est droitisé, c’est moche...).
  • Pour dessiner un mur, on peut faire un pavage de briques.

avec CaRMetal

Commençons par envisager un pavage du fond :

Dans CaRMetal, il y a la possibilité de mettre une image en fond :

Essayons avec ce pavé rustique :

La brique, ici dessinée avec CaRMetal, est un rectangle marron (couleur assez fréquente pour des briques), et encerclée d’un peu de blanc qui figurera le mortier.

Le pavé apparaît sans surprise au centre de la fenêtre de CaRMetal :

Mais les plus téméraires auront peut-être vu ce bouton tout à droite :

Et en cliquant dessus, « miracle » [5], un pavage s’est fait :

  • Ce mur est nu, c’est indécent, dit le Petit Prince. Cela ne sera pas du goût de tout le monde...
  • Dans ce cas, on peut essayer de poser un motif du papier peint sur chaque brique du mur.

On peut choisir cette image par exemple (ou toute autre image assez petite, au format jpg ou gif) :

Le pavé ci-dessus est extrait d’une aquarelle de Maurits Cornelis Escher.

En, plein dans le mille :

En activant l’option « pavage », on obtient ceci :

Pour ceux qui voudraient dessiner des figures géométriques sur ce pavage, voici le fichier CaRMetal obtenu :

Voici d’autres pavés, toujours de M.C. Escher, qui permettent de varier les fonds de CaRMetal (ou de html, Css permet aussi de paver le fond de la page) :

JPEG JPEG JPEG JPEG
JPEG JPEG JPEG JPEG
JPEG JPEG JPEG JPEG
JPEG JPEG JPEG JPEG

Un peu de poésie dans un monde de brutes, c’est toujours ça de pris. Passons à la suite.
Plutôt que de paver le fond, on peut aussi créer un pavage au dessus du fond.

Créer un pavage avec CaRMetal sans toucher le fond

Pour réaliser ce pavage avec CaRMetal, on va utiliser la programmation orientée objet en Javascript pour créer un objet Brique.
L’aspect graphique de cette brique sera un polygone (grosso modo un rectangle w x h aux coins rabotés selon le paramètre e). On peut choisir les dimensions (que l’on aurait pu mettre en curseurs).
Voici le script de ce pavage :

On reconnaît le constructeur de l’objet Brique puis l’utilisation du prototype pour construire les briques réelles via l’instruction Javascript new. On crée le centre, les sommets, puis le polygone.
Le résultat est le suivant :

Et voici la figure CaRMetal :

Pavage positif avec CaRMetal
figure dynamique CaRMetal

Par cette méthode, on ne peut pas obtenir facilement le pavage de type papier peint car les commandes d’insertion du motif ne sont pas accessibles depuis le script.

Pour le casse-briques, l’objectif sera d’essayer de plaquer ce papier peint sur les briques du mur cassable selon une mécanique semblable à celle, automatique, du pavage du fond. Ou de faire comme si, en rusant, dans le cas du papier peint...

Paver avec Sc**ch

En Sc**ch, pour faire un pavage, on fait une frise (verticale) de frises (horizontales).

Comment fait-on un mur de briques ? Facile ! On pose les briques une par une et couche par couche. En tenant compte du fait qu’ici le maçon est une brique [6], « poser une brique » signifie ici "créer un clone de soi-même à l’endroit précis où on se trouve. Donc

  • On se rend à l’endroit où on doit poser la première pierre (normalement en présence du député mais il n’était pas libre pour le cocktail qui s’en suivait) ;
  • On pose ladite brique (le premier clone) ;
  • On se déplace jusqu’à l’emplacement de la brique suivante ;
  • On reclone et on recommence jusqu’à ce qu’o soit au bout de la première ligne ;
  • On revient (en reculant) à l’emplacement de la première brique ;
  • On descend pour passer à la seconde couche ;
  • On recommence tout (cloner+déplacer jusqu’à ce qu’on soit au bout du mur) ;
  • et encore
  • et encore...

Le script Sc***ch ressemble à ceci :

Ah oui, les deux dernières lignes décrivent ce qui se passe lorsque le mur est construit : Le maçon prend sa retraite, c’est-à-dire qu’il va en bas et disparaît (se rend invisible en fait).

projet Scratch

Pour obtenir le pavage d’Escher, il s’agit juste d’habiller le lutin brique du costume adéquat et de modifier les coordonnées de translation :

projet Scratch

7) Casse-briques (thème sous-jacent = clonage)

Les briques disparaissent au toucher, mais en occasionnant tout de même un rebond. En dehors de cette similitude de comportement, faire le mur est l’occasion de construire un pavage, dans le cas présent, comme une frise (verticale) de frises (horizontales). Avec la géométrie dynamique, cela se fera par deux boucles imbriquées ; avec Sc***ch, cela se fera par clonage (lequel n’est d’ailleurs plus au programme). Avec Css ou CaRMetal, cela se fera juste en pavant l’écran (voir plus bas comment on fait du papier peint avec CaRMetal).

Encore des extraits du programme, montrant qu’avec un pavage, clonage ou non, on reste dans les rails :

A) Un jeu qui casse des briques avec Sc**ch

Maintenant il faut que la brique puisse être détruite lorsqu’elle est touchée par la balle. Ceci se déclare pour la première brique qui transmettra cette fragilité génétiquement à ses clones :

Au moment de mourir, une brique prononce, en guise de chant du cygne, le mot « pong ». Pourquoi ce chant funèbre ? Et bien parce que le programme parle de « déclencher une action par un évènement », et que ce genre de « déclenchement » se fait, entre objets, par émission-réception d’un message : La brique dit à la balle qu’elle a senti le choc, et la balle réagit en rebondissant [7]. Ceci se programme en lui expliquant (à la balle), que lorsqu’elle reçoit le message « pong », elle doit aussi rebondir :

C’est la base de ce jeu.

projet Scratch

Et voici la variante Escher :

projet Scratch

B) Un jeu qui casse des briques avec CaRMetal

Pour réaliser un casse-briques avec CaRMetal, on va utiliser la programmation orientée objet en Javascript déjà évoquée pour le pavage. On va le faire ici pour la version classique (raquette avec une poignée).
L’aspect graphique de cette brique sera un polygone (grosso modo un rectangle w x h aux coins rabotés selon le paramètre e). On peut choisir les dimensions (que l’on aurait pu mettre en curseurs).
Voici le script du constructeur :

On crée le centre, les sommets, puis le polygone.
On ajoute une méthode effacer qui permet de supprimer tous les éléments CaRMetal.

Contrairement à ce qui se passe dans la méthode avec Sc***ch, il n’est pas possible de gérer l’événement collision avec la balle dans le prototype (= dans l’objet Javascript). On le fera donc dans la boucle infinie du script principal.

On commence par créer les briques en tenant compte de leurs dimensions et de l’espace disponible. On met leur nom dans un tableau briques.

Puis on gère la collision avec toutes les briques du tableau, tableau qui est mis à jour après chaque collision.
On tient compte du fait qu’une brique peut être touchée par un bord vertical ou horizontal. La brique est supprimée après la collision :

Pour terminer, on gère la fin de partie (et l’arrêt) en supprimant toutes les briques restantes quand on sort de la boucle infinie.

On pourra remarquer que dans cette version le joueur pense avoir de l’initiative mais n’en a aucune. Une fois la balle positionnée initialement au hasard, son déplacement est entièrement prévisible, le rebond sur la raquette étant le même quelle que soit la position de la raquette. Il faudrait modifier ce comportement pour donner de l’initiative au joueur (en modifiant l’angle de rebond selon le point d’impact sur la raquette par exemple).

Voici la figure CaRMetal :

Casse-briques (raquette avec poignée)
figure dynamique CaRMetal

L’avantage de CaRMetal et ses fonds pavables, c’est qu’il est aisé de dessiner les briques (deux clics de souris !). L’inconvénient, c’est que ces briques, en fond de figure, ne peuvent interagir avec la balle. Il faut donc construire un pavage de briques qui soient des objets et ainsi puissent interagir avec la balle. Avec CaRMetal c’est de la POO, avec Sc***ch c’est du clonage.

C) Un jeu qui casse des briques avec GeoGebra

Pour créer un casse briques avec GeoGebra, on va adapter le fichier du Pong.

On commence par créer les briques dans le Javascript global :
On crée une fonction faireBrique(a,b,u,v)
(a,b) est la position du centre « C »+v
Les sommets seront nommés « D »+j (en fait u=v+8*(v-1), on aurait pu se passer de ce paramètre)

Bref, la fonction faireBrique crée le centre « C »+v, les sommets D..., et le polygone brique « br »+v.
Les points sont masqués, on donne au polygone la bonne apparence.
La fonction crée aussi un booléen « b »+v qui va servir plus tard (= brique déjà cassée ?)

Ensuite, dans la fonction ggbOnInit() on crée une double boucle qui crée les 30 briques comme pour le script CaRMetal. Cette fonction es lancée à l’ouverture de la figure.



Et on obtient le pavage :

Ensuite, on revient dans le script d’actualisation de t.
Pour gérer la brique br1, on ajoute quelques lignes au script :

On supprime l’augmentation du score par percussion du mur du haut.

Si b1 est vrai (brique br pas cassée), en cas de collision :
on gère le rebond, on supprime br1, on augmente le score, et b1 passe à faux.

Seulement, il y a 30 briques et réécrire à la main le code pour les 29 briques restantes ne soulève pas l’enthousiasme. Comment faire ?
On va utiliser CaRMetal. Non, ce n’est pas une blague ! CaRMetal permet de générer ce type de code très facilement, et c’est ce que l’on va faire.

On lance CaRMetal et on crée le script suivant :

On lance le script, et on obtient le code dans la fenêtre de sortie.

Il suffit alors de copier-coller ce code dans GeoGebra.

On donnera plus bas une figure CaRMetal (vide) avec le script.

Ensuite, on aimerait pouvoir rejouer. On reprend donc le script par clic du bouton replay.
Il faut recréer les briques. Pour la briques br1, on ajoute ces lignes :

Et comme précédemment, on repasse dans CaRMetal, et on crée un script pour générer les 30 séries de lignes :

Et on copie-colle dans GeoGebra.

Voici le fichier CaRMetal pour générer les scripts :

Figure CaRMetal avec les scripts pour générer les scripts GeoGebra
figure CaRMetal

Enfin, on ajoute un texte youWin qui s’affiche selon la condition sc==30 et on modifie la condition d’affichage du bouton « relancer » en y(M)<-4 || sc==30 (qui signifie y(M)<-4 ou score == 30). Et on augmente la vitesse u.
(Remarque : le jeu est un peu "long à l’allumage".)

Voici la figure GeoGebra complète :

Casse briques
figure dynamique GeoGebra

Pour terminer, on va réaliser le casse-brique Escher avec CaRMetal. Et il va falloir ruser.
Précisons tout de suite que la méthode aura un inconvénient, elle ne supportera pas le zoom (car le zoom n’agit pas sur le fond). Il faudra jouer sans zoomer.

L’idée consiste à travailler « en négatif » (= par masquage) :
On crée le pavage sur le fond.
On crée les briques noires qui serviront de masque et qui sont initialement cachées.
On crée une méthode montrer de l’objet Brique pour montrer le masque (et donc cacher le pavage).
On crée un cadre noir (pas rempli) pour matérialiser la table.
On masque la partie basse par un rectangle noir.
On masque en orange une couronne autour de la table (pour le rendu visuel).

Ensuite, parallèlement au tableau briques, on crée un autre tableau briques2 qui contient les noms de toutes les briques et servira d’archive.
Lors de la collision avec une brique, au lieu de supprimer la brique CaRMetal, on la montre, ce qui cache le pavage Escher du fond. Mais on supprime l’élément du tableau briques comme précédemment. Par conséquent, c’est comme si cette brique n’existait plus (on n’en tient plus compte pour les collisions avec la balle), même si son élément CaRMetal existe et est affiché.

En fin de partie, on supprime réellement toutes les briques en utilisant le tableau briques2 qui n’a pas été modifié.

Voici la figure CaRMetal :

Casse-briques Escher
figure dynamique CaRMetal