Activer le mode zen
Ressource au format PDF
Mots-clés
Classification
Comment hacker le jeu du dinosaure du navigateur Chrome
06/01/2025
Résumé
Une photorésistance permet de jouer à un jeu informatique sans intervention de l'utilisateur. La situation permet le conditionnement d'un capteur résistif (programme de seconde) et une illustration d'informatique embarquée (l'un des thèmes de SNT).
Table des matières
Introduction
Google Chrome propose un jeu pour se distraire en l'absence de connexion à Internet ou en cas d'interruption de cette connexion.
Le jeu est simple : un dinosaure, qui ressemble à un T-Rex[1], doit sauter au-dessus des cactus qui viennent à sa rencontre (ou vers lesquels il court, selon le point de vue[2]).
Enfin ça, c'est dans un premier temps. Des difficultés (oiseaux, mode nuit, augmentation de la vitesse…) s'ajoutent au fur et à mesure.
Pour jouer, il suffit d'écrire dans la barre d'adresse du navigateur Chrome : chrome://dino et de valider. Si vous lisez cet article sur un autre navigateur, le jeu est aussi accessible à l'adresse suivante "https://dinorunner.com/fr/". Pour lancer le jeu, on appuie sur la barre [espace] ou sur la flèche haut (↑), à droite de la barre espace.
Sur le compte Instagram @diyelectronic, j'ai vu une vidéo dans laquelle les sauts du dinosaure étaient commandés grâce à une photorésistance et un sketch Arduino. La photorésistance est scotchée sur l'écran, un peu en amont du dinosaure, côté photosensible orienté vers l'écran.
Quand des cactus passent au niveau de la photorésistance, la luminosité locale de l'écran diminue. Un test de valeur de cette luminosité commande alors la mise en fonction d'un servomoteur sur lequel est fixée une petite languette de façon à actionner la touche [espace] dans le but de faire sauter le dinosaure. Génial !
L'article présenté ici propose de détailler les différentes étapes de mise en œuvre de cette manipulation pour en faire le fil rouge de l'étude et du conditionnement d'un capteur.
1. Étude de la photorésistance
Une photorésistance est un composant passif résistif dont la résistance change avec la luminosité. La dénomination anglaise LDR décrit ce comportement : Light Dependent Resistor.
Dans un premier temps on peut mettre en évidence le caractère résistif de la photorésistance à l'aide du tracé de la caractéristique courant-tension du composant. Il suffit de relever l'intensité I du courant qui la traverse en fonction de la tension U à ses bornes, à luminosité constante bien sûr.
Dans l'exemple qui suit, nous avons utilisé une alimentation stabilisée et nous faisons varier la tension aux bornes de la photorésistance.
U (V) |
2,55 |
3,69 |
4,54 |
5,57 |
6,36 |
7,83 |
8,15 |
I (mA) |
1,3 |
1,9 |
2,3 |
2,8 |
3,2 |
4,0 |
4,2 |
Le graphique représentant U en fonction de I, figure 1, est une droite passant par l'origine, dont le coefficient directeur est la résistance de la photorésistance pour la luminosité donnée de l'expérience (lampe de bureau). Dans ces conditions de mesure, la photorésistance utilisée a une résistance de l'ordre de 2 kΩ.
2. Réalisation du système de pilotage à l'aide de la photorésistance
L'objectif, pour la suite, est de travailler avec une tension (et non plus une résistance) qui varie avec la luminosité afin de s'en servir pour piloter l'une des entrées analogiques d'une carte Arduino.
Pour résoudre ce jeu, nous avons uniquement besoin de déterminer une valeur de seuil de la tension (image de la luminosité), afin de différencier les deux états suivants :
- le dinosaure court librement sans obstacle (grande luminosité),
- le dinosaure s'apprête à rencontrer en cactus (faible luminosité car codé avec des pixels noirs).
Ainsi on propose de réaliser un pont diviseur de tension à l'aide de la photorésistance et un résisteur R, alimenté par la broche 5 V de la carte, figure 2. Le point milieu est branché sur l'entrée analogique A0 de l'Arduino.
La valeur du résisteur du pont diviseur dépend de celle de la photorésistance, elle est, pour notre application, de quelques kΩ, l'idée est d'avoir un seuil bien marqué.
Ainsi sur l'entrée analogique A0 de l'Arduino, on impose la tension \( V_{A0} = 5 \frac{R}{R+R_{ph}} \), en notant \(R\) le résisteur (quelques kΩ) et \(R_{ph}\) la résistance de la photorésistance.
Carte Arduino
Pour une première prise en main avec Arduino, on pourra consulter l'article « Débuter avec Arduino ». On trouvera des informations sur le matériel, l'alimentation, l'utilisation de la plaquette ou breadboard, l'installation du logiciel et le téléversement des programmes dans la carte.
Pour rappel, nous présentons ci-dessous une vision restreinte des entrées/sorties de la carte Arduino modèle Uno.
Pour aller plus loin et commander une sortie numérique, on pourra consulter « Détection de 3 niveaux de luminosité et pilotage de sorties d'un micro-contrôleur Arduino », et pour utiliser un script Python pour récupérer les données envoyées sur le port série de l'Arduino « Mesure de la température à l'aide d'un montage Arduino et représentation graphique des données avec python ».
2.1 Test préliminaire
On propose ici de vérifier la chaîne de commande en soumettant l'allumage d'une LED à deux états de luminosité : obscurité ou lumière.
Le premier script ci-dessous permet d'afficher, dans le moniteur série de l'interface Arduino, la valeur lue \( V_{A0}\) sur l'entrée A0.
On rappelle qu'une conversion analogique → numérique a lieu sur l'entrée analogique. Des tensions variant entre 0 et 5 V sont associées à des nombres compris en 0 et 1023 (convertisseur 10 bits → 210 = 1 024 valeurs).
On masque ou on illumine la photorésistance et on note les valeurs correspondantes de la tension retournées par la lecture de l'entrée A0 dans le moniteur série.
void setup()
{
Serial.begin(9600);
}
void loop()
{
int tens_A0 ;
tens_A0=analogRead(A0);
Serial.print("VA0=");
Serial.println(tens_A0);
delay(800);
}
On choisit ensuite une valeur de référence, qui va représenter notre seuil, entre les 2 valeurs précédentes.
Pour notre application, en utilisant une LDR03 et un résisteur de 10 kΩ, nous avons retenu la valeur seuil de 500, correspondant à \( \frac{500}{1024} \times 5 \) = 2,44 V
On branche la LED sur la borne 3 de la carte Arduino et on la protège à l'aide d'un résisteur de 1 kΩ en série. C'est maintenant l'occasion de présenter une commande conditionnelle.
Le script est alors modifié afin de tester la validité du seuil choisi :
void setup()
{
pinMode(3,OUTPUT);
digitalWrite(3,LOW);
Serial.begin(9600);
}
void loop()
{
int tens_A0 ;
tens_A0=analogRead(A0);
Serial.print("VA0=");
Serial.println(tens_A0);
if (tens_A0<500)
{digitalWrite(3,HIGH);}
else
{digitalWrite(3,LOW);}
delay(800) ;
}
Ainsi, lorsque la valeur lue est inférieure à 500, la borne 3 est mise à l'état haut (HIGH), la LED est alimentée et s'allume.
Dans le cas contraire la borne 3 est à l'état bas (LOW) et la LED est éteinte.
2.2 Test sur l'écran
Maintenant, on peut fixer la photorésistance sur l'écran de l'ordinateur et noter la valeur lue (tension \( V_{A0}\)) quand il n'y a pas d'obstacle et quand un cactus se trouve sous la photorésistance. On fixe un seuil entre ces valeurs (ici 220) et on vérifie que le passage d'un cactus allume la LED branchée sur la borne 3, figures 3 à 6.
2.3 Émulation du clavier
Il existe une bibliothèque Python qui permet d'émuler le clavier, ou plus exactement qui permet de contrôler le clavier (et la souris) pour interagir avec d'autres applications. Elle s'appelle pyautogui.
Installation de nouvelles bibliothèques pour python
Nous proposons ici un petit rappel sur l'installation de nouvelles bibliothèques dans l'environnement python. En fonction du système d'exploitation (Windows, macOS ou Linux) utilisé, la ligne de commande à taper dans le terminal sera différente. On pourra retrouver les spécifications sur la page Installation de la bibliothèque pyautogui.
La documentation de cette bibliothèque est par elle-même très intéressante : elle présente justement l'automatisation d'un jeu mais aussi la réalisation du dessin d'une spirale par contrôle de la souris. Cette bibliothèque va permettre de simuler l'appui sur la touche « flèche haut », l'une des 4 touches de défilement.
Quant au déclenchement de cet appui, il va être conditionné par ce qui s'affiche sur le moniteur série de l'Arduino.
Le script Arduino est légèrement modifié pour qu'à la place d'allumer une LED, le dinosaure puisse sauter :
void setup()
{
Serial.begin(9600);
}
void loop()
{
int tens_A0 ;
tens_A0=analogRead(A0);
Serial.print("VA0=");
Serial.println(tens_A0);
if (tens_A0<220)
{
Serial.println("up");
delay(300);
}
}
On voit que le message « up » s'affiche dans le moniteur série quand le niveau lu devient inférieur au seuil de 220. Ce sera le message de commande qui entraînera la simulation de l'appui sur la touche « flèche haut ».
Le script Python qu'on va écrire doit être capable de lire la liaison série entre l'ordinateur et la carte Arduino via le câble USB. Pour cela, on utilise une autre bibliothèque : pyserial.
Important
Une fois obtenue la valeur de seuil grâce au moniteur série (Arduino), il faut fermer ce moniteur pour libérer le port. Sans ça, le script Python ne fonctionnera pas.
Le script Python est le suivant :
import serial
import pyautogui
arduino_serie=serial.Serial(port = 'dev/ttyACM0', baudrate = 9600)
while 1:
data=arduino_serie.readline()
if "up" in data.decode('utf-8'):
pyautogui.press('up')
arduino_serie.close()
Sur mon système (Linux, Ubuntu 18.04), le port de communication avec Arduino est 'dev/ttyACM0', ce qui explique cette indication dans l'identification du port. Il faudra changer ce nom en fonction de votre propre installation. Sous Windows, il se présentera sous la forme 'COMxx' où xx sera un numéro.
On voit que le script va décoder la présence d'un « up » et provoquer l'appui sur la flèche haut par la commande pyautogui.press(‘up'). On propose aussi de terminer le programme avec la commande close() pour fermer le port série. Cela permettra, notamment lors des réglages, de revenir à la lecture dans le moniteur série sans créer de conflit.
Il reste à exécuter le script Python dans votre environnement Python :
run dans IDLE ou python3 ~/chemin_vers_le_script/prgm.py si pgm.py est le nom du script, en ligne de commande, et de revenir à la fenêtre du jeu pour le lancer.
La figure 7 présente une photo de l'écran avec les différentes interfaces, dans le cas d'un fonctionnement sous Windows avec utilisation de l'interface Jupyter Notebooks.
Il est arrivé que je rencontre une erreur d'encodage lors de la lecture du port série : ‘utf-8' codec can't decode byte 0xfa in position 2.
J'ai modifié le script Python comme suit pour ne pas tenir compte de cette erreur :
while 1:
data=arduino.readline()
if "up" in data.decode('utf-8', errors='ignore'):
pyautogui.press('up')
arduino_serie.close()
Il reste à vérifier que tout fonctionne correctement en scotchant la LDR sur l'écran, un peu en amont du dinosaure et c'est parti :-)
Voici la vidéo de l'un de mes tout premiers essais.
3. Utilisation d'un servomoteur
Une modification très chouette à faire consiste à commander l'appui matériel sur la touche [espace] à l'aide du bras d'un petit servomoteur.
Pour cela, on utilise la bibliothèque Arduino Servo
Le servomoteur est commandé par 3 fils :
- un fil rouge, relié au 5 V ;
- un fil marron, relié à la masse (GND) ;
- un fil orange, qui est le fil de commande, à relier à la borne correspondante de la carte Arduino (ici, ce sera la borne 3).
Pour plus de détails, on pourra consulter la page d'Arduino France qui est dédiée à l'utilisation d'un servomoteur : https://arduino-france.site/servo-arduino/. La figure 8 présente le conditionnement du servomoteur que je possède.
À présent, on n'utilise plus de script Python. Le script Arduino est quant à lui modifié de la façon suivante :
#include <Servo.h>
Servo servoMoteur ;
void setup()
{
Serial.begin(9600) ;
servoMoteur.attach(3);
}
void loop()
{
int tens_A0;
tens_A0=analogRead(A0);
if (valeur<220)
{servoMoteur.write(0);
delay(300) ;}
else
{servoMoteur.write(90);}
}
Il reste à placer le bras au dessus de la touche espace du clavier et à re-placer la photorésistance sur l'écran. La masse de 200 g que l'on voit sur la figure 8 permet de stabiliser la plateforme qui accueille le servomoteur.
Un (très) très bon ami a perfectionné le montage afin de pouvoir régler les positions haute et basse du bras qui actionne la touche [espace]. La vidéo présente le résultat du montage et son fonctionnement.
4. Invincibilité
La satisfaction est grande de voir le jeu jouer ‘tout seul' et le score augmenter sans faire quoi que ce soit.
Et puis, des oiseaux arrivent, en plus des cactus. Pour les éviter, le dinosaure peut soit sauter par dessus, soit se baisser. Hélas ils ne sont pas détectés par la photorésistance compte tenu de sa position pour les cactus. On pourrait bien sûr placer une seconde photorésistance et modifier le montage en conséquence.
Peu après, le jeu passe en mode nuit, c'est-à-dire que l'écran devient noir et les obstacles deviennent blancs. Une nouvelle amélioration du montage serait nécessaire. À ce stade, je pense cependant que l'objectif a déjà été atteint : à savoir conditionner un capteur résistif pour collecter des données et commander un actionneur. Il est temps de changer de méthode et de modifier le code source du jeu :-)
On demande à Inspecter la page du jeu : Clic droit > Inspecter ou simplement on appuie sur la touche F12. Dans l'interface qui s'ouvre, on voit un onglet qui s'appelle Console.
Dans cet onglet, saisir chacune des lignes qui suit, en validant par [entrée] à chaque fois (un message d'avertissement peut apparaître, le lire et suivre les instructions qu'il indique) :
Runner.instance_.setSpeed(30)
var original = Runner.prototype.gameOver
Runner.prototype.gameOver = function(){}
Maintenant le dinosaure traverse tous les obstacles :-D
Pour revenir à la situation normale, écrire ce qui suit (toujours dans la Console) puis valider la commande avec la touche [entrée] :
Runner.prototype.gameOver = original
Évidemment, tricher c'est mal. Ne le faites pas trop.
Au sujet de l'auteur
Laurent Mathieu a publié plusieurs ouvrages aux éditions Ellipses : « Quand je fais de la physique » et « Comment j'ai pesé la Terre avec un chronomètre ».
Références
[1] Jeu T-Rex Chrome Dinosaure.
[2] Débuter avec Arduino, Delphine Chareyron et Antoine Bérut, 2022.
[3] Détection de 3 niveaux de luminosité et pilotage de sorties d'un micro-contrôleur Arduino, Delphine Chareyron et Antoine Bérut, 2022.
[4] Mesure de la température à l'aide d'un montage Arduino et représentation graphique des données avec python, Antoine Bérut, 2022.
[5] PyAutoGUI's documentation, consultée en novembre 2024.
[6] Bibliothèque Pyserial 3.5 , consultée en novembre 2024.
[7] Bibliothèque Servo, Programming library Arduino, consultée en novembre 2024.
[8] Branchement servomoteur Arduino, programme, Tutoriels et Projets avec Arduino, consulté en novembre 2024.
[9] Basic servo control, tutoriel en anglais, consulté en novembre 2024.
Pour citer cet article :
Comment hacker le jeu du dinosaure du navigateur Chrome, Laurent Mathieu, janvier 2025. CultureSciences Physique - ISSN 2554-876X, https://culturesciencesphysique.ens-lyon.fr/ressource/Hack-dinosaure_Mathieu.xml