Bug et propositions d’améliorations pour l’outil All-Seeing Eye

Coucou. Voici un deuxième article dans le même mois !

Comme promis, j’ai envoyé 20 euros à All-Seeing Eye, pour remercier Joonas Hirvonen d’avoir créé cet outil permettant de modifier le jeu Eye Of The Beholder. Je m’en suis servi pour créer un challenge de Hacking pour la THCON. Tout est expliqué dans ce précédent article.

Ce fameux Joonas Hirvonen m’a remercié par mail et m’a encouragé à transmettre mes signalements de bugs. Je vais lui envoyer un document récapitulant tout ça, j’en profite pour le copier-coller ici. Ça ne vous intéresse peut-être pas, mais moi ça me fait un article à peu de frais. (Quelle blague ! Tous mes articles sont à peu de frais !)

C’est en anglais, vive le rosbif !


These bugs and improvement proposals are sorted from most important to less important.

Technical context :

Do what you want with these bugs, as I am not sure I will have the occasion to re-use ASE.

Modifying scripts provokes graphical bugs

Many wall images are scrambled when the game scripts are modified.

Here are steps to reproduce the bug.

  1. Start a game, create a party, go to the first drainage grate.
  2. Everything looks OK.
  1. Launch ASE.exe and set the folder
  2. Launch EOB1_Explorer.exe
  3. Add an instruction anywhere in the code, for example : F8 "I add a new message here." 00 02 00
  4. Press Ctrl-F9 to compile.
  5. You get a message box telling « Range check error. »
  1. Click the button « update INF ».
  2. Close and restart EOB1_Explorer.exe
  3. Add another instruction anywhere in the code.
  4. Press Ctrl-F9 to compile.
  5. This time you don’t get the error message.
  6. Click once again the button « update INF ».
  7. Start another game, re-create a party, go to the first drainage grate.
  8. This time the image is buggy. I do not know from where it was taken. It looks like a part of an attacking leech.

Other bugs appear on many wall textures. Once it happened, there is no hope to come back to a correct display.

I guess it’s a problem with memory offsets in the game information. It is quite annoying, it cancels all the involvement I would put in the creation of a totally new Eye Of The Beholder game.

Object sub-position cannot be redefined

Information related to objects can be modified in the tab « F4:Items ». But their sub-positions cannot be modified.

As we all know, an object on a square in the map has 5 possible sub-positions : the four corners on the ground, and the alcove carved on a wall.

The function ADD_ITEM shows it. It has 3 parameters : OBJECT_INDEX, POS_X_Y, SUB_POS.

The initial data contained in the .PAK files undoubtedly contains these supositions. When I modify existing object coordinates, they land on different sub-positions, depending on their index.

If the initial sub-position of the object is in an alcove, and if the object coordinates is on a square that has no wall, we have flying objects !

These flying objects can not be taken. Too bad.

I would say the tab « F4:Items » should have a field « sub-position », that allows us to read and write sub-position of each object.

A weird thing in the game engine : there is only one sub-position to define an object in an alcove, not one per wall side.

If you put a square with an alcove wall on each side, and if you put an object sub-positioned in the alcove on this square, then the object is visible from the four sides ! Logically, when you take it, it goes away from the four sides. In my opinion, this weird behaviour should be documented somewhere.

Encounter scripts should be editable, if possible

I do not know if it is possible at all. Some encounters seem to execute the same kind of instructions as the ones in the script :

  • speaking with Armun puts some objects in front of the party and sets a global flag,
  • there are movements automatically done before and after speaking with the dwarven cleric, and the previous global flag is checked to initiate the conversation,
  • speaking with Shindia spawns the monster « Shindia »,

I guess the dialogue texts, reply choices, player characters joining the party, etc. are all stored somewhere in the game files. If we could modify all this, it would be really cool, and we could make a totally-totally new Eye Of The Beholder game.

Some event parameters are not parsed in the script

Here is the code we usually have with stairs and ladders :

IF         TRIGGER_FLAG = 1 (on_enter)
ELSE_GOTO  $0473

[...]

IF         3 = 0
ELSE_GOTO  $044C
TELEPORT   TYPE = $E8 (party), SOURCE = <00,00>, DEST = <18,24>

IF         3 = 1
ELSE_GOTO  $0459
TELEPORT   TYPE = $E8 (party), SOURCE = <00,00>, DEST = <17,23>

IF         3 = 2
ELSE_GOTO  $0466
TELEPORT   TYPE = $E8 (party), SOURCE = <00,00>, DEST = <18,22>

IF         3 = 3
ELSE_GOTO  $0473
TELEPORT   TYPE = $E8 (party), SOURCE = <00,00>, DEST = <19,23>

IF         TRIGGER_FLAG = 4 (on_put_item)
ELSE_GOTO  $04AE

IF         3 = 0
ELSE_GOTO  $0487
TELEPORT   TYPE = $F5 (item), SOURCE = <18,23>, DEST = <18,24>

IF         3 = 1
ELSE_GOTO  $0494
TELEPORT   TYPE = $F5 (item), SOURCE = <18,23>, DEST = <17,23>

IF         3 = 2
ELSE_GOTO  $04A1
TELEPORT   TYPE = $F5 (item), SOURCE = <18,23>, DEST = <18,22>

IF         3 = 3
ELSE_GOTO  $04AE
TELEPORT   TYPE = $F5 (item), SOURCE = <18,23>, DEST = <19,23>

END

The translated script shows comparrison between litteral numbers : « 3 = 0 », « 3 = 3 », … which looks really dumb.

This is of course not what the script does. These values corresponds to the direction from where the party comes, or the direction from where the object was thrown. It is used to reposition the party or the object from where it comes, because nothing is supposed to stay at the same square of a stair or ladder.

Some specific words should be used in the translated script to describe this process. For example, 3 = 0 should be replaced by FROM_DIRECTION = NORTH.

The direction identifiers are the usual ones : 0 = north, 1 = west, 2 = south, 3 = east.

Labels should be shown in the « Events » tab

The « F2:Events » tab shows the translated scripts, but the GOTO instructions have the litteral adresses.

The « F3:Scripts » tab shows GOTO instructions with labels, but the script is displayed with raw bytecode.

The text labels should also be displayed in the « F2:Events » tab, to have all the human-readable script in one tab.

It is quite painful to modify the scripts, I was always switching between the two tabs : write some code, check it is correct, and so on. A really nice feature would be a mode to display the two tabs side by side. The « F2:Events » would automatically updates while we type in the « F3:Scripts » tab. But maybe that’s too much to ask.

Texture atlas needed

I had to try different wall numbers in the map, then play the game to see the corresponding images, then repeat for the next level palette, etc.

Some identifiers are the same in many palettes, for example : with « brick » and « blue », the value 51 corresponds to a wall with writings on it.

Other identifiers exist only in one palette, or are different between palettes, for example : all the stone portal images.

That would really be helpful to have the complete list of wall images, with their identifier, for all the palettes. You don’t even have to integrate it in the existing software. Just toss an external document with the images and their identifiers, that would be perfect.

Command parameters should be documented

The tab « F5:Commands » lists all the existing commands, which is great, but not enough.

The parameters of each command should also be documented. Some commands, such as TELEPORT or ADD_MONSTER, have numerous, varied and complex parameters.

The documentation you linked to https://moddingwiki.shikadi.net/ is interesting, but really hard to read and to understand. I’m not sure it explains all the commands of Eye Of The Beholder.

Program crashes when writing coordinates with one digit

This one is simple to reproduce :

  • Go to « F3:Script » tab, go to the « event trigger points » zone.
  • Modify a coordinate of any event, write it with only one digit. For example, replace « 03 » by « 3 ».
  • Press Ctrl-F9.
  • You get an error message : « Access violation ».

Code size limit should be checked

When the script of a level is too big, it can lead to unexpected behaviours.

The last address value in « F2:Events » should be checked. If it is greater than $1AF0, there are risks to have unexpected behaviours. I’m not sure of the exact limit, it may be a rounded value, like $1B00.

This should be checked by EOB1_Explorer.exe, and a warning message should be displayed when updating/saving the INF file.

The unexpected behaviour seems quite random, and occur when the party arrives in the level having the INF too big.

Sometimes, it’s an immediate crash with a totally unrelated message :

Some other times, it’s a crash with a message « Far heap corrupt! » when you click on the button « CAMP » :

Race and class values are not completely documented

The file EOB1_Explorer_Notes.txt has an uncomplete list of race indexes. Here is the complete one :

  • 00 = Human
  • 01 = Elf
  • 02 = Half-elf
  • 03 = Dwarf
  • 04 = Gnome
  • 05 = Halfling

The Class identifiers are not correct and does not correspond to the HAS_CLASS function.

There are only 4 « base class »,

  • 01 = Fighter
  • 02 = Mage
  • 04 = Cleric
  • 08 = Thief

HAS_CLASS adds all the classes of your characters, by using the boolean « OR », and returns true if there is at least one set bit in common with the parameter given to the function.

Multi-class activates the corresponding bits of all the base classes.

For example, let’s say the party has a Fighter/Mage and 3 clerics (that’s a weird party).

HAS_CLASS(2) will return True. It evaluates to :

  • has_class_param & (classes_chara_01 | classes_chara_02 | ... )
  • 2 & ((fighter | mage) | cleric | cleric | cleric)
  • 2 & (fighter | mage | cleric)
  • 2 & (1 | 2 | 4)
  • 2
  • True

It is not possible to check if there is a ranger in the party. This class only lights the « fighter » bit.

It is also not possible to check if there is a paladin. This class lights the « fighter » and the « cleric » bits.

The way how the function HAS_CLASS works should be documented.

That’s all

Thank you once again for having created these tools. They are wonderful, even with those tiny bugs.

I used EOB_Explorer1 to create a hacking challenge that involves the game and your tools. This challenge was part of the CTF at the Toulouse Hacking Convention 2022. The people who tried it said it was really interesting (a few of them managed to solve it, though). I will submit it to root-me.org in the upcoming months and I will let you know about it, if root-me accepts it.

For the moment, I have not planned to create other challenges or other games with Eye Of The Beholder, so there may have no specific reason to fix these bugs. Anyway, I’m really happy to have « revived » this game in a quite uncommon way. It was an important part of my childhood, I took many years to finish it.

See you next time !


Voilà, c’était tout ce que j’avais à dire. Je l’envoie à mon nouvel ami Joonas ce soir. S’il me répond des trucs intéressants, je vous en ferais part.

Bien entendu, j’ai plusieurs autres idées de challenge « TUR-ROX », avec d’autres jeux. J’essayerais d’en concrétiser une ou deux pour la prochaine THCON.

Cet article est un rapport de bug.

Rapport de bug -> bug -> ladybug -> l’héroïne de dessin animé -> vêtements aux motifs de cette héroïne -> femme ronde !

Enjoy :

Pour le mois prochain, je me permettrais d’être moins blogalement productif. Je vous rassemblerai un package de commentaires de jeux du Ludum Dare, et ça devrait suffire.

D’ailleurs, faut que je vous annonce mon classement. Eh bien on fera tout ça en même temps.

THCON + Ludum Dare, ça a été un beau bazar ces dernières semaines. Peut-être que maintenant je vais avoir du temps pour faire la road-map de Squarity ?

On ne sait pas…

TUR-ROX-👁 Post-creationem-challengem

Heeey !

J’ai terminé la réalisation de mon challenge de hacking pour la THCON. Je l’ai soumis aux personnes qui organisent l’événement. Je ne sais pas encore s’il sera accepté. Dans tous les cas, je ne peux rien vous révéler avant la fin du Capture The Flag, qui aura lieu entre le 16 et le 17 avril.

Cependant, ça me démange de ne pas pouvoir vous parler de mon œuvre, alors je vais vous révéler son contexte et tout ce qui m’est passé dans la tête durant sa création. Ça ne devrait rien spoiler, je ne décris pas les énigmes en elle-même.

Le contexte

J’en ai déjà parlé un peu à la fin de cet article.

Il s’agit du jeu vidéo Eye Of The Beholder, dont j’ai modifié les plans des niveaux et les événements à l’aide de l’outil All-Seeing Eye et de son éditeur.

Pour résoudre le challenge, il faut jouer à la version modifiée tout en inspectant son contenu avec All-Seeing Eye. Il faut analyser les scripts et en déduire les actions à faire dans le jeu, pour obtenir le code secret permettant de valider le challenge. Je ne vous en dis pas plus.

Ce challenge, ainsi que les deux autres créés pour la précédente THCON, s’inscrivent dans un objectif plus global : l’instauration d’une nouvelle catégorie de challenge de hacking, que j’ai sybillinement baptisé « TUR-ROX ». Ça signifie « TURing-appROXi-complete video games ». C’est même pas un vrai sigle, c’est pas du tout les initiales des mots, mais j’m’en fous total, j’suis un ouf’ rebelle malade.

Principe des challenges de type TUR-ROX

Le principe consiste à trouver un jeu vidéo comportant un système de script intégré, à le modifier dans le but d’y implémenter un algorithme plus ou moins simple, et à faire en sorte que le flag ne puisse être découvert que lorsque cet algorithme est compris.

L’inspection et la modification du jeu peuvent être effectuées par un outil « officiel » (comme l’éditeur de ZZT), ou bien par un outil ne provenant pas des personnes ayant créé le jeu (comme All-Seeing Eye). Le système de script peut être plus ou moins élaboré. Au minimum, il doit pouvoir accéder à certains éléments du jeu. S’il comporte quelques structures algorithmiques de base, (if, while, …), c’est déjà bien.

En général, on fournira cet outil avec le challenge, afin de donner tous les moyens nécessaires à sa résolution. Mais on pourrait faire exception à cette règle.

Beaucoup de jeux actuels sont fortement moddables et scriptables. Les gens qui les développent ont réfléchi deux secondes et se sont dit qu’il serait bon de profiter des expériences du passé. Les scripts s’appuient maintenant sur des langages de programmations standards, multi-usages et documentés. Par exemple : Roblox utilise le Lua, Twine utilise du JavaScript, les gros moteurs de jeux comme Unity autorisent plusieurs langages (python, C#, …).

C’est la meilleure démarche possible, ça facilite les liens entre les personnes qui veulent créer des jeux vidéos et qui ne savent pas (encore) coder, et les personnes qui codent sans créer de jeux.

Mais ce n’est pas avec ça que je peux faire des chouettes challenges TUR-ROX. Du code à rétro-ingénierer dans un langage connu, c’est trop facile. Un foisonnement de documentation, d’outils d’analyse et de debugging sont à portée de clavier. Une solution serait de faire exprès d’écrire du code le plus incompréhensible possible, mais serait alors un challenge de type obfuscation, et non plus un TUR-ROX. Il existe déjà une foule de gens capable de créer des challenges d’obfuscation, et ce mieux que moi.

Un TUR-ROX est un mix entre un jeu vidéo et du code source à décortiquer, le système de script doit donc être spécifique au jeu. En revanche, il peut être obscur et mal documenté, ça fait partie du challenge de le comprendre avec les moyens disponibles. C’est pour ça que je préfère utiliser des jeux anciens ou exotiques. On est obligé de se plonger dans leur histoire, de retrouver comment pensaient les concepteurtrices de jeux à une époque où il y avait moins de standards.

Et cette année, j’ai choisi Eye Of The Beholder. Mais pourquoi donc ?

Attention : Turok ≠ TUR-ROX

Eye Of The Beholder, parce que nostalgie

Ce n’est pas la première fois que j’écris un article sur ce jeu. Il a marqué mon enfance. Je l’ai commencé alors que j’étais en sixième, en explorant un peu au hasard. Une solution a été publiée dans un magazine Tilt, j’ai demandé à mon frère de la photocopier. On a commencé à s’en servir avec mon autre frère, on est allé jusqu’au niveau 7. Ensuite, notre intérêt pour le jeu a diminué. De plus, ce niveau est graphiquement plus inquiétant que les précédents (très sombre, des symboles d’araignés omniprésents), et il commence par un difficile combat contre un groupe d’elfes noirs. J’ai douté de mes capacités à en venir à bout, j’ai laissé tomber.

Moustache ! moustache !

Je me suis fait engueuler par mon frère, qui m’a reproché que les photocopies avaient été faites pour rien puisque je n’étais même pas foutu de les utiliser entièrement.

Quelques années plus tard, j’ai recommencé le jeu seul. J’ai pris le temps d’élaborer une équipe bien équilibrée, couvrant le plus de classes possibles. J’ai tué tous ces tocards d’elfes noirs et je suis arrivé au niveau 10. Il comporte un générateur de Mantis Warrior. J’avais réussi à faire abstraction du générateur d’araignée géante présent au niveau 4, mais celui des Mantis m’a psychologiquement bloqué.

Ce que j’aime dans ce jeu, c’est le sentiment de « nettoyer » les souterrains. On explore partout, on tue tous les monstres qu’on rencontre et on prend tous les objets. À chaque fois qu’on finit un niveau, on peut se dire : « Voilà, il n’y a plus aucun danger. Je pourrais revenir me promener dans ce niveau sans aucune crainte ». Concrètement, on n’a pas besoin d’y revenir, mais j’aimais éprouver cette sécurité et cette impression de rendre le monde meilleur. Or, les générateurs de monstres enlèvent complètement ce sentiment.

Encore une ou deux années plus tard, je me dis que c’est quand même dommage d’être allé aussi loin dans le jeu sans le finir. Je détruis 4 Mantis Warrior à la suite dans le niveau 10. Je parviens à me faire une raison : les prochains seront générés dans longtemps, je peux toujours éprouver un sentiment de nettoyage même provisoire, et si il faut vraiment sécuriser entièrement les souterrains, eh bien les notables de la ville de WaterDeep n’auront qu’à envoyer des magiciens destructeurs de générateurs de monstres !

J’écume les derniers niveaux, je bute Xanathar à la bourrin (je possédais le « Wand of Silvias », mais je n’avais pas compris qu’on pouvait s’en servir pour le faire reculer jusque dans des pics où il se plante stupidement). Mon frère revient à la maison pour des vacances. Je lui annonce que j’ai terminé le jeu. Il est heureux pour moi, mais ne s’excuse pas de m’avoir engueulé des années auparavant. Il ne se souvenait certainement pas de ce passage de sa vie.

Maintenant que j’y pense, ce même frère m’avait soutenu lorsque je suis allé rapporter à la FNAC un jeu qui ne marchait pas. On a pu obtenir un avoir, ce qui n’était pas gagné d’avance, et je crois que je ne l’ai jamais remercié pour ça.

D’autres années plus tard, je commence un tout petit peu Eye Of The Beholder 2, mais je ne prends pas le temps de me lancer vraiment dedans. J’essayerais peut-être de le faire un jour, en live sur Twitch, avec vous.

Pour finir, j’ai négationné mes deux frères et je me sens mieux. Je ne détaille pas plus, je ne veux pas vous embêter avec des histoires idiotes.

Aujourd’hui, j’ai retrouvé la solution, sans faire de photocopies. Merci au site abandonware-magazine !

Eye Of The Beholder, parce que script

Je me souvenais d’énigmes et d’événements tellement étranges que ça laissait supposer la présence d’un système de script relativement générique. Quelques exemples :

  • Vous avancez dans un couloir et automatiquement vous faites un demi-tour, sans être prévenu.
  • Vous posez certains objets à certains endroits, ça déclenche l’apparition d’un monstre et/ou d’autres objets.
  • Vous entrez dans une pièce, vous ramassez une potion, vous sortez, vous re-rentrez par une autre entrée, une deuxième potion est apparue, vous la ramassez, ainsi de suite jusqu’à 4 potions.
  • Des rencontres avec des PNJ déclenchant des dialogues, des ajouts de nouveaux personnages, des attaques, etc.
  • Des téléporteurs, des murs qui disparaissent quand on appuie sur un bouton, des plaques de pression qui déclenchent des ouvertures/fermetures de portes, etc etc.

J’ai cherché un éditeur de niveau et je suis très vite tombé sur « All-Seeing Eye », créé par Joonas Hirvonen (je sais pas qui c’est). Il permet de modifier beaucoup de choses, y compris les scripts.

Au bout de quelques soirées et quelques week-ends, j’avais assez bien appréhendé le « EOB-script » (appelons-le comme ça) et j’avais une idée assez précise des énigmes que je pouvais créer.

J’ai été impressionné. Ça date de 1991 et c’est une machine virtuelle fonctionnant avec du bytecode. Son langage, comme dirait les Inconnus, est « souple et solide à la fois ».

Voici les fonctionnalités de l’EOB-script qui m’ont beaucoup plues.

  • 32 variables booléennes locales à chaque niveau + 32 variables booléennes globales.
  • Opérations booléennes complexes à l’aide de la notation postfixe .
  • Beaucoup de fonctions pour analyser l’état du jeu : présence de monstre à un endroit, type de mur d’une case spécifique, présence d’un type d’objet sur une case, position de l’équipe de personnages, classe et race des personnages, …
  • Et aussi beaucoup de fonctions pour agir sur le jeu : créer/supprimer des objets/murs/monstres, lancer un sort, émettre un son, infliger des dégâts, …
  • Branchement conditionnel (if – goto), mais aussi sous-fonctions grâce aux instructions gosub et return, dont j’ai grandement profité !
  • Gestion d’événements couvrant un large spectre : on peut exécuter un script sur une récupération ou un dépôt d’objet, une arrivée ou une sortie sur une case, un clic dans la zone réactive d’un mur (manette, serrure, rune, …).
  • Cohérence dans la gestion d’événements : une téléportation déclenchera le « on_enter » de la case d’arrivée, un lancer d’objet déclenchera le « on_put_item » de la case sur laquelle l’objet atterrit.
  • Robustesse : sur un seul événement, il est possible de déplacer des dizaines d’objets, modifier des dizaines de murs, exécuter des dizaines d’instructions, écrire des dizaines de messages (même si seuls les trois derniers sont visibles), tout ça sans problème, sans plantage, sans ralentissement.

En revanche, All-Seeing Eye n’est pas toujours à la hauteur de la magnificence de l’EOB-script. En particulier, on ne peut pas changer les dialogues et les événements des rencontres avec les PNJ. Je ne sais pas si c’est dû à un simple manque de motivation ou à une impossibilité technique. Je ne sais pas si ces événements sont écrits en EOB-script ou codés en dur. Je ne sais pas non plus où ils sont stockées, (pas forcément dans les fichiers EOBDATA.PAK).

J’ai beaucoup de remarques et de propositions d’améliorations, ce qui nous amène au chapitre suivant.

Ils sont meugnons !

Review de All-Seeing Eye (Pour plus tard)

Je vais faire un petit don de 20 euros sur le Paypal de Zorbus.net. C’est amplement mérité. S’ils n’avaient pas créé All-Seeing Eye, je n’aurais jamais pu créer ce challenge de hacking dont je suis assez fier.

Comme j’ai bien utilisé et exploré leur outil pendant plusieurs semaines, et que j’ai un peu galéré à cause de certains bugs, j’ai souhaité rassembler mes remarques dans un même document, que je leur transmettrais.

Et comme j’aime bien rentabiliser les textes que j’écris, je mettrais ces remarques aussi dans ce blog. Mais pas tout de suite, ce sera l’objet du prochain-prochain article. Il sera en anglais, mais vous vous débrouillerez bien.

Je vous laisse avec des yeux tatoués sur des seins. Je trouve ça très rigolo.

THCON 2021 – ZZT Post Challengem

Le week-end du 12-13 juin a eu lieu le CTF (Capture The Flag) de la Toulouse Hacking Convention. J’y étais en tant que participant à essayer de résoudre les challenges, et en tant que contributeur qui en a créé. Normalement, on n’est pas censé faire les deux. Chut, faut pas le dire.

Ma participation

Tout se passait à distance, dans nos chez-nous respectifs. Niveau ambiance et mise en situation, on est loin du NorthSec (qui met tout de même la barre assez haute, ce que je vous avais narré l’année dernière).

Les challenges de la THCON sont simplement listés sur un site, créé avec l’outil générique CTFd. Je ne me suis donc pas trop senti transporté dans un autre monde, mais je me suis quand même beaucoup amusé.

J’ai réussi trois challenges, dans trois catégories différentes : programmation, app-script, cryptographie. C’est peu, mais ça me suffit amplement. Je suis un peu déçu d’avoir passé plusieurs heures sur un challenge d’intro tout simple (one-time pad). Il fallait faire des XOR, alors que je tentais des additions/soustractions dans tous les sens. Duboulet !!

Ou-exclusif, le shérif de l’espace. J’ai déjà fait cette blague y’a 10 ans. Je m’en lasse pas.

Ma contribution

Ayant un ancien Collègue dans l’orga de la THCON, j’en ai profité pour lui proposer un challenge de mon crû. Après de longs et passionnants échanges ainsi que moults tests, nous nous sommes mis d’accord pour le splitter en deux, du fait de son volume qui s’est révélé assez conséquent.

Je vous propose ici la version non-splittée, que je préfère. Faut s’accrocher pour le vaincre, mais vous n’avez pas les contraintes de temps de la THCON.

Il s’agit d’un unique fichier à télécharger : THCON21.ZZT.

Voici la description, comportant des indices pour vous aider à trouver quoi faire avec :

TUR-ROX

A journey in Turing-approxi-complete video games.

« ZZT » ? What is that strange file extension ? It may be something old. If it’s old, there is a MUSEUM about it. Whatever it is, I suppose it has nothing to do with Catherine ZETA-Jones.

Comme je suis gentil, je vous pré-mâche le début. Il s’agit d’une map du jeu vidéo « ZZT ». Pour l’installer, vous devez suivre cette doc (L’outil « KevEdit » n’est pas indispensable).

Si vous voulez juste y jouer pour voir un peu à quoi ça ressemble, il y a une version en ligne dans le « Museum Of ZZT » : https://museumofzzt.com/file/c/THCON21.ZIP
Rien à installer ni à copier. Vous cliquez sur le lien puis vous cliquez sur « Play online » dans le menu en haut.

Le but de ce jeu est de trouver un « flag », c’est à dire un code secret, ayant le format THCon21{-------------}. Les tirets correspondent à des caractères alphanumériques.

C’est du hacking. Ça veut dire que si vous vous contentez de jouer, vous ne trouverez pas grand-chose. Il faut décortiquer le jeu, chercher comment il fonctionne, le bidouiller, ajouter des éléments de débuggage, extraire des informations, le passer à la moulinette, etc.

Bon triturage de cerveau !

Quite unrelated pic, screenshot d’un outil bien utile pour les CTFs.

Des signes d’intérêt de la part des THConistes

J’ai eu quelques bons retours sur le Discord de la THCon, aussi bien pendant la compétition que après. Apparemment, le format est assez original. Quelques personnes ont rédigé des write-ups (ça veut dire « des solutions »), et y ont inséré un avis très positif.

Mon ego est fortement flatté de voir que des gens se sont interessés à ma création, au point de rédiger des textes à ce sujet. Je suis tout gargarisé !

Voici les liens vers lesdits write-ups. ATTENTION, Ne cliquez pas dessus si vous voulez trouver le flag par vous-même. Ce sont des énormes spoilers !!!

Vous vous en doutez, le véritable but de cette contribution n’était pas le challenge en lui-même, mais la petite publicité pour Squarity embarquée dedans. Vous la trouverez facilement. A-t-elle eu un impact ? Aucun nouvel adepte n’est venu se rallier au Discord de Squarity. C’est pas grave.

Des signes de non-intérêt de la part des ZZTistes

Les personnes qui continuent d’utiliser ZZT pour créer des jeux et autres œuvres se retrouvent dans le « Museum », ainsi que sur (encore) un serveur Discord. Vous vous en doutez, le véritable but était de faire découvrir Squarity à cette communauté. Les deux moteurs de jeux diffèrent sur beaucoup de points, mais ont un but commun : favoriser la création vidéoludique amatrice.

Mais vous ne pouvez pas débarquer en gueulant : « Regardez mon projeeeeeet ! Il est gèèèniaaaâââl ! Venez vous féoder à ma cause ! Travaillez pour moi et faites-moi de la pub en échange de ma reconnaissance faciale éternelle gratuite ! ».

Lorsqu’on veut rallier à soi-même les membres d’une communauté existante, il vaut mieux commencer par se rallier soi-même à cette communauté.

Alors je préparais le terrain. Je m’étais inscrit sur le Discord de ZZT il y a de ça plusieurs mois. Je me suis présenté, j’ai lu plein de messages sans trop intervenir. Par certains aspects, ce Discord est un « safe space ». Certaines personnes font part de leur problèmes personnels et sont inconditionnellement soutenues par d’autres personnes. C’est cohérent avec le fait que les premiers créateurtrices de jeux ZZT étaient des ados pas toujours bien dans leur peau. The « children of the glow ». Mais un safe space, ça ne s’approche pas comme ça.

Cherchez vous-même d’où vient l’expression « children of the glow »

Pendant que je créais le challenge, je parlais un peu à tous ces gens, leur disais que je trouvais ZZT cool et chouettement nostalgique, et que je me sentais bien à coder mon truc. Je mettais des petits smileys. Je testais un mini-jeu et donnais un mini-avis. Lorsque je leur parlais de ma future création, un début d’intérêt semblait se manifester. La mythique Anna Anthropy m’adressa même un ou deux messages.

Mon challenge contient des easter eggs qui durent paraître étrange aux THCONistes, et qui étaient spécialement destinés aux ZZTistes :

  • Un personnage qui raconte une histoire bizarre et totalement azimutée, le genre de propos que pourrait sortir un ado réfugié dans son petit monde. Je vous laisse le lire par vous-même, il est facile à trouver, dans le premier board.
  • Ce personnage dit aussi « Everything is familiar and everything is different ». Une phrase que l’on retrouve plusieurs fois dans le livre écrit par Anna Anthropy.
  • Le board principal contient le texte « Lawless Invisible », une référence à Kudzu, un jeu connu par bien des ZZTistes. Dans ce jeu vous rencontrez (ou pas) des personnes désignées comme « Unlawful Invisible ». L’expression est restée.
  • Le challenge contient un programme Puzzlescript permettant d’extraire un mini-jeu, dans lequel vous dirigez une tête blanche sur fond bleu, comme le « Char-1 » de ZZT, et vous devez récupérer des « Venus Symbol » rose.
Coïncidence ? Je crois pas…

À part le dernier, tous ces easter egg sont très accessibles. Il suffit de jouer au jeu. Même si vous ne résolvez pas le challenge et que vous ne comprenez rien à ce qu’il faut faire, vous tomberez dessus.

Deux jours après la THCON, je viens sur le Discord de ZZT et j’apporte mon petit jeu en cadeau. Une personne m’informe qu’un type est venu durant le week-end pour demander des indices sur le challenge. J’ai vu leur conversation, elle était tout à fait courtoise mais n’a duré que quelques messages.

Et c’est tout.

Alors voilà. Je ne m’attendais pas à un débarquement massif de ZZTistes sur mon Discord de Squarity, ni à ce que toutes ces personnes s’arrachent les cheveux sur le challenge pour tenter de le résoudre à tout prix, ni à ce que mon esprit créatif soit porté aux nues et que Tim Sweeney vienne en personne me remettre la Croix Nationale du Venus Symbol d’Honneur. Mais je m’attendais à avoir au moins une petite réaction de la part d’au moins une personne qui aurait testé le jeu juste 2 minutes.

Mon cadeau n’était pas suffisant. Tant pis. Je reviendrais plus tard si j’en ai d’autres. Pour l’instant, je laisse tomber la communauté ZZT et je me concentre sur Squarity. Il y a déjà quelques personnes qui me sont féodées (même si ça veut rien dire), je dois continuer de leur apporter régulièrement des nouveautés car je ne veux pas les décevoir.

Voilà un cadeau !

C’était quand même chouette de créer ce challenge

J’ai passé un certain temps à le réaliser, à galérer avec l’éditeur de niveau de ZZT et son ergonomie discutable des années 90, à dessiner les boards, à étudier le langage ZZT-OOP, à élaborer les énigmes, à écrire les scripts de chacun des petits objets programmables, à intégrer des gros tas de texte à l’aide de bouts de scripts python, etc.

Je me suis senti bien. Une fois de plus, ma nostalgie de création adolescente est revenue. Une nuit, il était 2 heures du matin, j’écrivais pour la 5ème fois la ligne de code « :lblMulti » , la musique de Worakls passait dans mon casque audio. C’est pas ce que j’écoute d’habitude, mais là ça collait avec ce retour en adolescence. J’étais bien. Libre. Je pouvais sentir les bulles de photons telluriques glisser le long de mes neurones.

Est-ce que c’est ma crise de la quarantaine ? Est-ce que ZZT est un retour aux sources pour moi ? Ce serait d’autant plus drôle que ce n’est pas une de mes sources. Je ne connaissais pas ZZT lorsque j’étais adolescent.

Je vais me remettre tranquillement à Squarity. Mais je proposerais également ce challenge au site root-me.org. Je ne sais pas du tout si ce sera accepté. Il y a des tas de raisons que ça ne le soit pas : la présence d’une pub pour un projet personnel, le fait que ce challenge ait déjà été utilisé, que des write-ups circulent, etc. On verra bien.

Si ça passe dans root-me, il est fort possible que je me lance dans un autre challenge du même type pour la prochaine THCON. Spoiler alert, ce sera peut-être avec cet outil.

Et puisqu’on parlait de Venus Symbol, voici Venus :

Classement Ludum Dare et presque un jeu de Soko-punk

Ludum Dare

Voici mon classement au Ludum Dare.

Il y a eu 800 participations dans la catégorie « Compo ». Une trentaine de personnes ont noté mon jeu.

  • Overall: 513ème
  • Fun: 426ème
  • Innovation: 144ème
  • Theme: 530ème
  • Graphics: 578ème
  • Humor: 438ème
  • Mood: 545ème

Pour une première participation, c’est pas si mal. Je suis assez content de mon classement en « Innovation ».

Manifestement, le jeu de mot dans le titre du jeu : « Loops in Pool » n’a pas vraiment fait décoller mon classement en Humour. On fera mieux la prochaine fois.

Presque Soko-punk

Et voici un lien vers le jeu que nous avons créé en live mercredi soir.

Il est pas fini, mais ça fait quand même des trucs. Il faut imaginer que l’héroïne que vous dirigez n’est pas censée pouvoir passer dans des éclairs.

Du Sokoban-like dans une ambiance Steam Punk.

Le but est d’électriser toutes les boules, mais vous n’êtes pas obligé de toutes les connecter ensemble dans un même graphe connexe. Lorsque le but est atteint, il aurait dû y avoir un petit « print » pour dire bravo, et un passage vers un hypothétique niveau suivant.

Une fois de plus, je me suis heurté à la lenteur de Brython. Dans mon code, j’avais un double parcours imbriqué de toutes les tiles du jeu, pour vérifier quelles boules doivent être électrisées. Rien que ça, ça mettait quelques secondes.

J’ai arrangé le code pour que ce soit plus rapide. Mais cette lenteur pose de plus en plus problème, même pour des jeux que j’aurais cru simple. J’ai peut-être une piste pour régler ça, je vous en reparle quand je l’aurais testée, c’est à dire dans un délai non défini.

À nouveau, pas d’image de femme ronde. Désolé. Je me rattraperais au prochain article. En attendant, vous pouvez chercher « yathbeauty » sur Twitter, TikTok et instagram. Très jolie, et elle sait comment se tenir pour nous faire profiter de ses atouts.

Twitch ce mercredi à 22h !

Gzolb les prolos.

Petit article très rapide, juste pour dire que mercredi 28 octobre, à 22h, je streame sur twitch. On créera un petit jeu sur Squarity, en live.

Ça se passera par ici : https://www.twitch.tv/recher_squarity

Oh, et sinon j’ai trouvé le moyen d’afficher les messages d’erreur et la traceback quand le code python plante. Ce sera mis en prod d’ici mercredi, ce qui me permettra de beaucoup moins galérer.

Pas d’image de femme ronde parce que c’est vraiment un tout petit article. Mais si vous voulez prospecter par vous-même, tapez « Nicole Nurko » dans un moteur de recherche.

Démarrage d’un gros projet complètement à l’arrache !

Ce que j’avais prévu

1 : Faire les fonds de tiroir de mes activités créatives, packager proprement les trucs qui méritent de l’être, même ceux qui ne sont pas finis. C’est ainsi que vous avez eu Blarg sur github, Kawax, Pru-pra-prok, l’archivage de tous les articles du magazine 42, L’animation du tunnel pour l’UTBM, une grosse série d’article concernant mon dernier changement de crémerie, et tout un tas d’autres machins.

2 : Terminer en packageant mes très vieux programmes Pascal que j’écrivais au lycée et à l’UTBM, et les mettre à disposition ici.

3 : Écrire un article de blog émouvant dans lequel j’explique que je me sens prêt à démarrer un gros projet créatif. Expliquer que ça fait un peu peur, que jusqu’à maintenant, le peu de projets que j’ai réussi à mener jusqu’au bout étaient soit de taille moyenne, soit une succession de mini-projets (par exemple, tous les articles pour le magazine 42). Ajouter qu’il est fort possible que j’abandonne au bout de deux mois pour cause de découragement honteux, ou pour cause de découverte d’une toute nouvelle idée que je croirais encore meilleure. Terminer l’article en demandant à mes lecteurtrices, avec des trémolos dans la voix, quelques commentaires de soutien pour ce nouveau projet, même si je ne l’ai pas du tout décrit pour le moment. Insister sur le fait qu’un soutien inconditionnel de leur part serait une magnifique démonstration de fidélité, rappeler (toujours avec des trémolos) que ce blog existe depuis plus de 10 ans et que certains d’entre vous me suivent depuis plusieurs années.

4 : Espérer deux ou trois commentaires de soutien, pas plus, pour ne pas être déçu.

5 : Travailler pendant un mois ou deux sur ce projet secret.

6 : Écrire un nouvel article, montrer la première version du projet même si elle est bancale. Expliquer que c’est une plate-forme de création de mini-jeux web en 2D sur une grille. Annoncer qu’il suffit de savoir coder un peu en python pour créer des premières choses toutes simples. Ajouter qu’il n’y a pas besoin d’installer de logiciel ou de s’inscrire (mais il faut quand même un compte github pour partager ses jeux). Arguer du fait que, même si la plate-forme est simple, sa souplesse permettrait de créer des jeux très variés tels que des Match 3, un Laser Tank, voire des jeux de stratégie comme Advance Wars. Préciser tout de même qu’il ne sera jamais possible de placer des objets à cheval entre deux cases de la grille, que c’est un choix conscient destiné à assurer la simplicité. Donner l’exemple de Zelda sur la NES, qui ne serait pas recréable sur cette plate-forme puisque le personnage peut se trouver entre deux cases. Terminer sur un ton faussement humble en disant que je me suis inspiré de PuzzleScript, Drod et même ZZT.

7 : Écrire une doc, pas forcément complète ni bien peaufinée, mais qui expliquerait au minimum comment créer et partager des jeux. Espérer que quelques personnes commencent à s’y mettre, même si c’est rustique et pas du tout ergonomique dans un premier temps.

8 : Améliorer encore le projet pendant quelques mois.

9 : Participer au Ludum Dare (une compétition de création de jeu en un week-end). Ne pas s’attendre à un glorieux classement, mais utiliser cette participation pour montrer la plate-forme au plus de gens possible.

10 : Éventuellement, créer une vidéo ou un stream sur twitch montrant la genèse du jeu créé pour le Ludum Dare. Utiliser cette vidéo comme tutoriel et comme démo de ce qu’il est possible de faire.

12 : Donner rendez-vous aux prochains Ludum Dare, tous les 6 mois. Indiquer que l’on profitera de ces compétitions pour s’imposer des dead lines fixes, qui devraient apporter la motivation nécessaire pour produire régulièrement de nouvelles versions de la plate-forme.

13 : Continuer comme ça sur plusieurs années, et voir jusqu’où ça nous mène.

Pour l’instant, ça mène ici.

Ce qu’il s’est réellement passé

1 : Réaliser qu’il n’y a plus que quelques mois avant les grandes vacances, et qu’on aimerait avoir une première version du projet à ce moment là.

2 : Laisser en plan le packaging des vieux jeux Pascal.

3 : Réaliser une première version ultra à l’arrache de la plate-forme, afin de pouvoir la montrer aux potes qu’on retrouvera pendant les vacances.

4 : Ne pas oser parler de ce projet aux potes en question, parce que l’occasion ne s’est pas présentée et que j’ai toujours l’impression de passer pour un mendiant quand je quémande de l’attention aux gens, même quand c’est mes potes.

5 : Continuer de bosser sur le projet, pester intérieurement que, comme toujours, ça avance trèèès lentement.

6 : Au passage, créer une chaîne twitch et s’amuser à streamer des Clash of Code, même si ça n’a absolument rien à voir avec la choucroute.

7 : Réaliser que le prochain Ludum Dare est dans à peine quinze jours, que la plate-forme est à peu près utilisable mais pas du tout documentée, qu’on n’aura pas forcément le temps de la documenter d’ici là, et qu’on n’a rien annoncé pour l’instant.

8 : Jeter à la gueule de ses lecteurtrices le lien vers la plate-forme : http://squarity.fr.

9 : Leur jeter un autre lien à la gueule, en croyant que ce sera suffisant pour comprendre comment créer et partager des jeux : squarity.fr#fetchez_githubgist_darkrecher/bd49300f9c480b789a70315155571e9d/raw/gamecode.txt

10 : Espérer que l’on arrivera à faire quelque chose de pas trop pourri pour le Ludum Dare.

11 : Continuer comme ça sur plusieurs années, et voir jusqu’où ça nous mène.

Pour conclure, inévitablement

12 : Expliquer que le mot « squarity » signifierait « la carrétitude », puisque les jeux sont créés sur un quadrillage en 2D. Ajouter que le fait d’aimer les carrés n’empêche pas d’aimer aussi les courbes. Pour illustrer ce propos, insérer une image de Myesha Boulton :

13 : S’endormir sur son clavier parce qu’il est tard.

NorthSec 2020

Avec des collègues de ConcreteWorld.🌍 (la boîte où je bosse), on a participé au CTF du NorthSec, c’est une compétition avec des truck à hacker.

Normalement, il a lieu sur place, au Québec, mais cette année, avec cette histoire de virus, ils l’ont fait en ligne. Je ne vais rien dire de plus à propos du Covid, car je souhaite garder ce blog le plus possible éloigné de l’actualité, pour qu’il reste une sorte de lieu magique dans l’espace éthéré.

Le NorthSec, c’était bien cool ! On a eu 31 points au total, ce qui nous a classé 39ème sur 79. Certes, je n’ai personnellement fait gagner que 2 points, mais mes collègues m’aiment bien quand même. (Je ne suis pas encore assez entrainé à ce genre de compétition).

Je me suis gardé quelques challenges offline sous le coude, que j’essayerais de réussir à tête reposé.

L’environnement et le contexte de présentation des challenges était vraiment bien chiadé. Je vous explique.

Ambiance années 90

Tout se passe dans une université fictive intitulé « Severity High School ».

Les challenges sont disponibles sur le forum de cette université, un par sujet de forum. Même si parfois, certains sujets racontent des trucs qui n’ont rien à voir.

Le style graphique du forum, avec sa magnifique texture de brique en fond, est d’une mocheté assumée.

Qui dit challenge de hacking dit site web avec des vulnérabilités à exploiter. En général, ces sites n’ont aucune mise en forme. C’est une convention, et même une fierté. On se concentre sur le fonctionnement interne du site et les failles qui s’y trouvent. Il faut éviter d’être pollué par le rendu visuel.

De toutes façons, les hackers savent rarement designer un site. C’est un autre métier. Allez sur root-me.org et essayez les challenges de type « Web-server » et « Web-client », vous verrez.

Pour le NorthSec, c’est un autre choix qui a été fait. Le design des sites et du forum a été travaillé. Travaillé pour être comme dans les années 90.

C’est pas les premiers à faire ça, mais c’est toujours amusant. On pourrait aussi y voir une critique du système éducatif et du secteur public en général, qui a la réputation d’avoir systématiquement 10 trains de retard concernant internet et l’informatique. Pour eux, des designs de ce genre, ça ne les choquerait pas.

Y’avait pas ça au NorthSec, mais vous voyez l’idée.

Ambiance dystopie

Les messages fictifs échangés sur le forum donnent l’impression que les élèves et les professeurs font tous n’importe quoi et passent leur temps à se pourrir entre eux.

  • des données comportementales sont stockées sur eux, ainsi que des données personnelles complètement WTF (proba d’échec, index social, score consommateur, couleur des yeux, ton de la voix, humidité, …)
  • ces mêmes données révèlent que toute l’école est corrompue et que les notes sont proportionnelles aux donations effectuées par les parents,
  • les élèves se volent leurs devoirs entre eux et modifient les notes (pas forcément les leurs) sur les serveurs,
  • les professeurs développent eux-mêmes leurs applications de validation des examens, qui sont bien évidemment de véritables passoires,
  • les élèves, quand ils ne sont pas occupés à tricher, passent leur temps à organiser des « parties » et à se préparer pour « le prom » (le bal de fin d’année).

Ça donne une impression de micro-société totalement dystopique, c’est très étrange et très amusant.

Il y a deux ans, j’étais allé au CTF de la Toulouse Hacking Convention. Le contexte donne forcément l’impression d’être dans un autre monde durant une nuit : plein de geeks dans une pièce en train d’essayer de hacker des trucs. Mais les challenges y étaient présentés de manière brute, sans cohérence entre eux.

L’ambiance et la contextualisation des challenges du NorthSec (même si on n’est pas physiquement sur place) m’a transporté dans un autre monde pendant tout un week-end, et ça fait du bien. Et le fait que c’était un monde dystopique était encore plus rigolo.

C’est à ça que servent les vacances, les voyages, les festivals, jouer dans une pièce de théâtre, etc. : jouer à être quelqu’un d’autre durant une parenthèse de temps plus ou moins longue.

J’espère avoir l’occasion de participer à d’autres trucs comme ça. À la prochaine !

Je vous laisse avec Busty Dusty, une nana des années 90.

Découvrir le python – chapitre 3 – list’oire de la vie

Le titre de l’article est une blague par rapport à la chanson naze et bien-pensante du Roi Lion : « C’est l’histoâââââre de la viiiiiiiie ». Ce troisième article de découverte du python a pour sujet principal : les listes.

Je tenais à expliquer cette blague, car quand on explique une blague, elle n’est plus drôle. Mais quand une blague est pas drôle dès le départ et qu’on l’explique, ça devient drôle.

Avant tout, pif paf la solution au devoir du précédent article. C’était plus un travail de géométrie que de programmation. Des fois, on croit qu’on fait un truc et on en fait un autre. C’est comme ça la vie, jeune lecteurtrice. L’histoâââre de la viiiiieeeuuuuu.

De la couleur

Remet-toi sur le site Trinket, ajoute les deux instructions habituelles du début : « import turtle », « turtle.speed(0) ».

Ensuite, tu copie-colles la fonction « dessiner_diams » créée dans l’article précédent (tu peux la récupérer dans la correction du devoir).

Pour finir, ajoute une seule ligne après la fonction, dans le programme principal : « dessiner_diams(0, 0, 50) ». On est devenu des prolos et on fait des petits diamants.

On est des prolos, mais on n’est pas forcément des communistes. On voudrait pouvoir faire des diamants ayant une autre couleur que ce rouge pétaradant.

Tu as certainement déjà repéré la ligne de code de la fonction spécifiant que le diamant est rouge. Pour les anglophonophobes, je rappelle que « red » signifie « rouge ». Indique une autre couleur à la place : « green », « blue », …

C’est top-de-la-housse, mais ça ne permet pas de dessiner des diamants de couleurs différentes.

Alors tu vas appliquer la même méthode que dans l’article précédent, lorsqu’on avait ajouté le paramètre « taille » :

  • Ajoute un paramètre supplémentaire dans la fonction « dessiner_diams ». On va l’appeler « couleur ».
  • Utilise ce paramètre dans le corps de la fonction. À toi de trouver où il faut le mettre et à la place de quoi.
  • Ajoute ce paramètre lorsque tu appelles la fonction, dans ton programme principal.

On va profiter de cette souplesse supplémentaire que tu viens de conférer à ta fonction (lecteurtrice, tu te rends compte que tu con-faire ? Tellement génial). Remplace la ligne du programme principal par ces deux lignes :

dessiner_diams(0, 0, 50, "green")
dessiner_diams(25, 25, 50, "blue")

Et voilà !

Lien vers la solution si t’as perdu tes couleurs.

De la couleur RVB

Les couleurs c’est fun et bigarré, mais c’est chiant de devoir les identifier par des noms, surtout en anglais. C’est un coup à ce que ça parte en bastonnade entre d’éminents chromatologues qui se crêperont l’arc-en-ciel sur la différence entre le cyan et le turquoise. Encore heureux que turtle ne reconnaît pas le fuschia, sinon je vous dis pas le bordel ! Personne ne connaît vraiment ce mot.

Il existe une manière plus standard de définir les couleurs en informatique. On indique trois nombres, correspondant aux quantités des trois couleurs primaires : rouge, vert et bleu.

Je t’entends protester d’ici, lecteurtrice : « han mais n’importe quoi, mon prof de dessin il m’a dit que les couleurs primaires c’est rouge, bleu et jaune ! ». Alors je te répondrai d’écouter ton prof mieux que ça, car il a plutôt dit que les couleurs primaires sont magenta, cyan et jaune. Finalement, je te répondrais que ça dépend si on est en synthèse additive ou en synthèse soustractive. Écris ces termes dans un moteur de recherche si tu veux en savoir plus.

Si tu as joué avec des logiciels de dessins, tu connais déjà les sélecteurs de couleurs. En voici un disponible en ligne : https://lehollandaisvolant.net/tout/tools/color/ .

Choisis la couleur que vu veux et retiens les trois nombres de la ligne « RGB » (Red Green Blue).

Dans ton code python, tu peux écrire ces trois nombres à la place du nom de la couleur. Mais attention, il faut mettre des parenthèses pour les délimiter. Par exemple :

dessiner_diams(0, 0, 50, (42, 234, 69))

« dessiner_diams(0, 0, 50, 42, 234, 69) », ça ne marcherait pas.

Miss Diamond Doll, puisqu’on reste sur les diamants

Tuple et zante ?

Pourquoi il faut des parenthèses en plus ?

La couleur ne doit constituer qu’un seul paramètre. Mais celui-ci peut être de type simple (un texte entre guillemet), ou de type composé.

Ces trois nombres entre parenthèses définissent une valeur de type tuple. « Tuple » est le mot générique pour dire un couple / un triplet / un quadruplet / un n-uplet.

Pour toutes les fonctions de la librairie turtle nécessitant des couleurs dans leurs paramètres, on peut indiquer un texte correspondant à un nom de couleur valide, ou bien un tuple de 3 entiers. Une conversion est effectuée en interne dans turtle.

Les tuples sont des types de valeurs comme les autres, tu peux donc les mettre dans des variables :

ma_couleur = (42, 234, 69)
dessiner_diams(0, 0, 50, ma_couleur)

Et aussi utiliser des variables numériques comme composantes d’un tuple:

le_rouge = 42
le_bleu = 69
# J'ai la flemme de créer la variable pour le vert.
dessiner_diams(0, 0, 50, (le_rouge, 234, le_bleu))

Plus d’infos sur les tuples dans cette chouette documentation : https://courspython.com/tuple.html

Profitons de cette nouvelle connaissance et réalisons un joli dégradé de couleur.

Tu me fais une boucle qui dessine 100 diamants :

  • La coordonnée X varie de 5 en 5, en partant de -200 : -200, -195, …
  • La coordonnée Y reste à 0.
  • La taille reste à 50.
  • La couleur rouge reste au maximum, c’est à dire 255.
  • La couleur verte reste à 0.
  • La couleur bleue varie de 2 en 2, en partant de 50 : 50, 52, 54, …., 246, 248.

Ça donnera un magnifique dégradé :

Lien vers la réponse, si ton cerveau s’est dégradé.

Vous êtes l’éliste de la nation

Tu vas mettre en commentaire ta belle boucle du chapitre précédent, car on va partir sur autre chose. Nous arrivons enfin au sujet majeur de cet article : les listes.

Dans le programme principal, juste après la définition de la fonction dessiner_diams, tu vas créer une liste contenant quelques nombres :

ma_liste = [-70, 38, 45, -20, 113]

Tu peux parcourir cette liste à l’aide d’une boucle. En voici une toute simple, je te laisse deviner ce que ça fait :

for elem in ma_liste:
    print(elem)

On retrouve la même syntaxe que pour les autres boucles : for {bidule} in {truc}:

Sauf que cette fois-ci, le {truc} que tu parcoures n’est pas une fonction « range » qui ne fait que compter. C’est une liste, avec tout et n’importe quoi dedans.

Lecteurtrice, je vois bien, à ton œil mouillé et ton air rébarbatoire, que tu as envie de me poser une question qui dérange :

« Wesh, auteurtrice, c’est quoi l’intérêt d’avoir inventé deux types : les listes et les tuples ? Ils font la même chose : stocker une suite d’éléments. »

Eh bien voilà : les tuples ne peuvent pas changer de contenu. Les listes, si. On peut y ajouter ou enlever des éléments, en remplacer un par un autre, etc.

Re-lecteurtrice, je te revois bien me redemander :

« Re-wesh, à quoi ça sert d’avoir inventé des tuples qu’on ne peut pas changer, si on a déjà les listes, qui font la même chose et qu’en plus on peut changer ? »

C’est pour les performances. Les tuples prennent très peu de place en mémoire et sont très rapides d’accès. Quand on a besoin d’une suite de trucs et qu’on sait qu’on ne la changera pas, on prend un tuple.

Voilà un autre lien avec plein de détails sur les listes : https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/232026-creez-des-listes-et-des-tuples-1-2

Si tu ne veux pas tout lire, je te montre très rapidement ce dont tu auras besoin pour la suite :

ma_liste = [-70, 38, 45, -20, 113]
# Accès à un élément
print(ma_liste[2])
# Ajout d'un élément à la fin
ma_liste.append(99)
# Écriture de toute la liste.
print(ma_liste)

Mets ça dans un programme et regarde ce que ça fait. C’est d’une comprenabilité qui ne me semble pas pharaonique.

On peut boucler sur les listes, comme sur les tuples. De manière générale, il y a des tas de choses en commun entre les listes et les tuples, qui se font exactement de la même manière. C’est possible grâce à un concept de programmation appelé le duck typing.

Ça disgresserait trop d’expliquer ce concept dans cet article. Pour ne rien te cacher, je l’ai mentionné juste pour avoir l’occasion de placer cette superbe image :

Si ça fait coin-coin comme un canard, c’est un canard.

List-ception

En informatique, on aime mettre des trucs dans les mêmes trucs : un sous-bloc de code dans un bloc de code, un sous-répertoire dans un répertoire, …

J’ai le plaisir de t’annoncer qu’on peut mettre des listes dans des listes, des tuples dans des tuples, des tuples dans des listes dans des tuples dans des tuples, etc.

Voici une liste avec des tuples dedans :

des_coords = [ 
    (-100, -40), (100, -40), (-80, -70), 
    (80, -70), (-60, -90), (60, -90),
    (-40, -100), (40, -100), (-20, -110), 
    (20, -110), (0, -115),
    (-70, 120), (70, 120),
]

Tu remarqueras qu’on peut l’écrire sur plusieurs lignes, à condition de ne pas oublier le crochet ouvrant au début ni le crochet fermant à la fin, et de respecter l’indentation.

Ajoute cette liste de tuples dans ton programme et fais une boucle dessus.

  • À la première itération, tu récupéreras le tuple (-100, -40), et tu me dessineras un diamant aux coordonnées X=-100 ; Y=-40.
  • À la deuxième itération, tu dessineras au autre diamant en X=100 ; Y=-40.
  • Puis en X=-80 ; Y=-70.
  • etc.

Pour la couleur des diamants, met ce que tu veux. Personnellement, j’ai choisi un espèce de jaune-orangé : (250, 150, 0).

Tu obtiendras un joli sourire de joker !

Why so serious ?

Lien vers la réponse, si t’as plus de cartes joker.

Je return à ma maison

OK lecteurtrice, si tu en es là dans l’article, et que tu as codé tous les exercices avec les doigts nus de tes mains nues, sans trop regarder les réponses, tu as de la motivation et de la comprenitude. J’ai confiance en toi, je sens que tu ne vas pas t’évanouir si je t’apprends DEUX choses différentes dans un même chapitre.

Mets-toi face à moi pour bien recevoir toute ma décharge cognitive.

1)

Une fonction peut renvoyer quelque chose (un nombre, une liste, …). Le code ayant appelé la fonction peut récupérer ce qui a été renvoyé.

Voilà un exemple :

def renvoyer_une_liste():
    print("coucou")
    return [4, 6, 15, 9]
    print("On ne voit pas ce texte.")
    print("Car une fonction s'arrête après un return.")

liste_que_je_recupere = renvoyer_une_liste()
print(liste_que_je_recupere)

Si tu exécute ce bout de code, ça écriras : « coucou », puis « [4, 6, 15, 9] ».

On a le droit d’écrire plusieurs « return » dans une même fonction. Mais dès que l’exécution arrive sur l’un d’eux, la fonction s’arrête, et renvoie ce qui est indiqué.

2)

La fonction turtle.pos() renvoie un tuple de deux éléments, contenant les coordonnées de la position actuelle de la tortue. Tu peux tester comme ceci :

ma_position = turtle.pos()
print(ma_position)

Et maintenant tu vas mettre tout ça en pratique.

  • Au début de la fonction dessiner_diams, tu crées une liste vide. On va l’appeler « coord_des_pointes ».
  • Dans la boucle de la fonction, juste après l’instruction turtle.circle, tu récupères la position actuelle de la tortue.
  • Tu ajoutes cette position dans coord_des_pointes. Celle liste contiendra donc des tuples.
  • À la fin de la fonction, tu « return » coord_des_pointes.
  • Dans le programme principal, tu exécutes une seule fois la fonction dessiner_diams,
    • coordonnées X=0 ; Y=0, taille = 50, couleur = ce que tu veux.
  • Tu récupère la valeur renvoyée par la fonction, et tu la printes.

Ça devrait t’afficher ça dans la sortie standard :

[(0.0, 50.0), (50.0, 0.0), (0.0, -50.0), (-50.0, 0.0)]

Lien vers la solution, en cas de surcharge cognitive.

Je vous emmerde et je return à ma maison

Des diamants dans des diamants

Puisque tu as récupéré des coordonnées, profites-en pour dessiner des diamants avec ! Mais tu les fais un peu plus petits. Une taille de 25, par exemple.

Ça donnera quelque chose dans ce style (même si c’est pas ces couleurs là) :

Est-ce que tu vois ce qui va venir après, lecteurtrice ? Est-ce que tu réalises qu’on est maintenant très proche de l’image finale que je t’ai promise il y a plus de deux mois ?

À chaque fois que tu traces les 4 petits diamants, la fonction renvoie à nouveau les coordonnées de leurs 4 coins. Tu pourrais toutes les récupérer, (16 coordonnées en tout), et dessiner sur chacune d’elles un autre diamant, encore un peu plus petit que les précédents. Ne serait-ce pas fractalement uber-classe ?

Il te reste à régler un petit détail : tu ne récupères pas les 16 coordonnées en un seul coup.

Dans le programme principal, avant la boucle où tu dessines les diamants de taille 25, tu crées une liste vide. On va l’appeler coord_mini_diamants.

À chaque dessin de diamant, tu récupères 4 coordonnées, que tu ajoutes dans coord_mini_diamants. Sais-tu comment on ajoute une liste à la fin d’une autre liste ?

Eh non lecteurtrice, ce n’est pas la fonction « append ». Celle-ci permet d’ajouter un élément simple au bout d’une liste. Pour ajouter une liste au bout d’une liste, on utilise l’addition, tout simplement.

Tu aura donc ceci dans ta boucle :

coord_mini_diamants = coord_mini_diamants + dessiner_diams(x, y, 25, (255, 0, 0)

Une fois que cette liste est remplie, plus qu’à s’en servir.

Et paf, un niveau de diamant supplémentaire :

Lien vers le code, si les diamants ne sont pas tes meilleurs amis.

diamant(diamant(diamant(diamant())))

Pour finir, tu re-appliques la même démarche, pour un dernier niveau supplémentaire.

Je te récapitule le tout :

  • premier diamant : taille = 50, couleur= (128, 0, 0)
  • 4 diamants autour : taille = 25, couleur= (255, 0, 0)
  • 16 diamants autour : taille = 12.5, couleur= (255, 70, 70)
  • 64 diamants autour : taille = 6, couleur= (255, 120, 120)

Le fait de reproduire plusieurs fois la même forme, à d’autres endroits, et de plus en plus petite, me permettrait de te présenter les notions de fractales et de récursivité. D’ailleurs, on pourrait dessiner tous les diamants en une seule et même boucle. Mais je ne vais pas t’embêter avec ça, cet article est déjà assez cerveauphage.

Tout ça pour dire que nous arrivons enfin à notre image finale ! Yiiipiii !

Lien vers la réponse, si tu t’es fait une fracture de la fractale.

C’est tout pour aujourd’hui

Voici inévitablement des devoirs. Pour cette fois, je t’en donne deux !

Le premier est assez facile, c’est une reprise du devoir précédent. Il faut dessiner cet anus de licorne :

Petit indice : tu vas devoir trouver comment tracer des formes remplies de couleur, mais sans le trait noir de délimitation.

Le deuxième :

Là c’est un peu plus dur. Il faut reprendre ce qu’on a fait avant, en annulant le dessin de certain diamants. Le tout est de savoir lesquels. Un coup c’est celui de droite, un coup celui du haut, …

Tu vas peut-être devoir apprendre par toi-même à utiliser une notion que je n’ai pas du tout présentée : le branchement conditionnel, if-then-else.

 

Et la suite ?

La librairie turtle permet également de créer des animations. Dans Trinket, on peut faire quelque chose comme ça :

Je pourrais donc te pondre un ou deux articles là-dessus, lecteurtrice. Mais je ne sais pas si ça vaut le coup. Je voudrais écrire d’autres trucs et me lancer dans d’autres projets.

J’ai mis cette série d’article de découverte du python sur mon compte Linkedin (vous ne savez pas lequel c’est, mais vous n’en avez pas besoin). Dans l’ensemble, j’ai eu des retours très aléatoires :

  • zéro commentaire dans ce blog, mais ça j’y suis habitué,
  • plein de commentaires et repartages Linkedin pour le premier article,
  • un seul repartage (de mon super-pote) pour le second article.

Je suis un peu perplexe. Je ne sais pas si c’est les gens qui ne me répondent rien parce que je ne les intéresse pas, ou si c’est Linkedin qui ne met pas mes articles dans le fil d’actualité des gens pour une raison obscure (le fait d’avoir placé le mot « fouetter » lorsque j’ai décrit les sévices corporels destinés aux personnes ne faisant pas leur devoirs ?)

Et désolé, il n’y a pas autant de blagues et de jeux de mots débile dans cet article. Un réajustement effectué par mon inconscient ? Je ne sais pas.

Je vais attendre les retours, mais si j’en ai à nouveau aussi peu, je ne me prendrais pas la tête. Le tout dernier article de la série (s’il y en a un) sera du code brut : les corrections des devoirs et l’animation.

Tchôrp.

Découvrir le python – Chapitre 2 – Diamonds in the skaaaaaaye

Wèèèèèsh lecteurtrice !! Bien ou bien ?

T’as lu mon article précédent et t’as grave kiffé au taquet de ouf’ ? Sois jouasse, en rev’là une gorgée !

Avant qu’on s’y remette, j’espère que t’as fait tes devoirs, ou que t’as au moins essayé.

Si ce n’est pas le cas, ce n’est pas grave, je vais simplement prévenir mes robots correcteurs. Ils te jetteront du sable fin sur les sourcils, puis te pendront par les pieds et te fouetteront jusqu’à ce que tes larmes aient entièrement nettoyé tes sourcils.

Voici la solution du devoir, avec plein de commentaires qui expliquent comment c’est gaulay.

Un diamant

Let’s go sur un truc simple pour commencer. Retourne dans Trinket, ton bel environnement de dev, enlève le programme initial et ajoute les deux lignes habituelles au début : « import turtle », « turtle.speed(0) ». On va commencer simple, tu vas me dessiner un diam’s.

♫ C’est le python qui va dicter ton code. ♪ Génération Oui-Oui. ♬♪

Un diamant, c’est quatre quarts de cercle. Sauf qu’après chaque quart, la tortue fait demi-tour, pour changer le sens de tracé du quart suivant.

On n’est pas des prolos et on veut un gros diam, alors tu me fais des quart de cercles de 100 pixels de rayon. Tu as le droit de le réaliser d’abord à la crade, c’est à dire en copiant-collant 4 fois le même morceau de programme. Mais ensuite, tu me met ça sous forme d’une boucle.

Tu devrais réussir à bling-blinguer un diamant cossu comme ça :

Lien vers la réponse si tu as du mal à te remettre dans la vibe.

Laissez-moi vos coordonnées

Si tu as fait quelques années d’école, lecteurtrice, tu as sûrement placé des points sur une feuille, pour faire un graphique ou pour tracer une droite. Tu utilisais alors des coordonnées : un nombre en X, un autre en Y.

Si tu es Muriel Robin, lecteurtrice, tu pourrais faire la blague « laissez-moi vos cordonniers, je vous rappellerais », et tu aurais été super drôle, pour l’époque.

La tortue se déplace dans un espace défini par des coordonnées. Cet espace est orienté de la même manière qu’en cours de maths :

  • La coordonnée (0, 0) correspond au milieu de la zone de dessin.
  • Lorsqu’on va vers la droite, la première valeur (le X) augmente, elle diminue lorsqu’on va vers la gauche.
  • Lorsqu’on va vers le haut, la deuxième valeur (le Y) augmente, elle diminue lorsqu’on va vers le bas.

C’est pas du tout orienté selon les conventions habituelles en informatique. On va pas s’attarder là-dessus pour l’instant, sinon je vais encore traiter les matheux de ringards.

La fonction « turtle.setpos » permet de positionner la tortue à des coordonnées données (Wesh, lecteurtrice ! « coordonnées données » ça fait une répétition lolilol !).

Voici un lien vers la doc de la fonction.

Au début de ton programme, ajoute un setpos pour positionner la tortue aux coordonnées X = -40, Y = +30.

Le déplacement va dessiner un trait moche. On réglera ça au chapitre suivant.

Mais avant, tu vas mettre ces coordonnées dans deux variables, intitulées « pos_x » et « pos_y ». Tu te souviens de ce qu’est une variable ?

Au début du programme, tu définis la variable « pos_x » à -40 et la variable « pos_y » à 30.

Ensuite, dans l’instruction « turtle.setpos », tu n’utilises plus directement les valeurs -40 et 30, mais les deux variables que tu viens de définir.

Pour l’instant, ces variables servent à R (Wesh lecteurtrice, langage de jeune !!), mais ce sera utile très bientôt.

Lien vers le code, si t’en chie.

Attention, les majuscules-minuscules sont importantes. « pos_x » et « Pos_x », ce n’est pas la même chose.

Les conventions de codage en python conseillent d’écrire les noms de variables tout en minuscules. Lorsqu’un nom est composé de plusieurs mots, on les sépare par des « _ » (le tiret bas). Un jour, jeune programmeurtrice dont les poils n’ont pas encore poussés, je te parlerai plus en détail de la notion de « convention de codage » et du PEP-8.

Si t’as envie d’appeler tes variables « posx » et « posy », t’as le droit, tu fais tout qu’est-ce que tu veux, c’est ta création, c’est bonheur. Il faudra juste que tu t’en rappelles, parce que dans la suite de l’article je continue de les appeler « pos_x » et « pos_y ».

Il n’est pas rare de s’over-prendre la tête en boucle pendant des heures sur la manière de nommer une variable.

Up and Down

C’est trognon ces variables, mais ça règle pas le problème du trait dégueubeurk que fait le setpos.

Je te donne juste le nom des deux fonctions permettant de régler le problème : « turtle.penup », « turtle.pendown ». Tu te débrouilles pour savoir ce que ça fait, comment ça marche, est-ce que ces fonctions ont besoin de paramètres, où les appeler, etc. Peut-être que tu peux commencer par les taper dans un moteur de recherche.

Tu regardes des docs et des exemples de code, tu testes dans ton programme, si ça fait des trucs pétés, tu testes autre chose. Azy lecteur, fais pas ta tafiolle, azy lectrice, fait pas ton tafiot (Wesh lecteurtrice, t’as vuuuu ? Écriture inclusive sur le mot « tafiolle » !)

Quand tu auras réussi, ça fera le même diam’s qu’au tout début, mais sans le trait moche.

Lien vers la réponse, si t’es au bout de ta vie.

Tout ça pour ça ? Je t’entends penser d’ici : « you you foutay de magl, ou bien ? ».

Ne brûle pas cet article tout de suite lecteurtrice, la suite est plus intéressante.

lofteurs.up() and lofteurs.down()

Pléthore de diam’s

Ce serait le top de la bling-bling-itude de pouvoir dessiner des diamants où on veut. Mais pas en copiant-collant le code, sinon ce serait le top de la cra-cra-itude. Alors on va créer une fonction.

Tu ne sais pas ce qu’est une fonction ? Eh bien si. Tu en utilises depuis le début : « print », « turtle.circle », « turtle.penup », … Maintenant, tu vas en créer une nouvelle qui sera rien qu’à toi.

Lorsqu’on crée une fonction, on lui associe un bloc de code, qu’on appelle le corps de la fonction. Comme tu as bien retenu la leçon précédente, tu sais déjà que ce bloc peut éventuellement contenir des sous-blocs, sous-sous-blocs, etc.

Ensuite on exécute la fonction (on peut aussi dire « appeler la fonction »), ce qui revient à exécuter son corps. Ça fait très bizarre de dire « exécuter son corps », alors dites-le uniquement dans votre tête, d’à-corps ?

Tu vas donc me créer une fonction, sans paramètre, intitulée « dessiner_diams ». Dans le corps, tu mets ce que tu as déjà écrit, depuis « turtle.penup » jusqu’à la boucle dessinant les quarts de cercle. Tu n’y mets pas l’initialisation des variables pos_x et pos_y, c’est fait exprès.

Voilà un lien de doc qui pourra t’aider : https://courspython.com/fonctions.html, à lire jusqu’au chapitre « Fonction avec paramètre ». Cela dit, les chapitres d’après peuvent t’aider pour la suite, et c’est assez bien expliqué. Y’a juste pas de fun, contrairement à mes articles qui regorgent d’espiègleries toutes pailletées de saveurs foisonnantes prestige t’as vu wesh ?

Seulement voilà. Si t’exécutes ton code là maintenant, t’auras queud’. Zéro diams. Retour à l’âge de prolétaire.

C’est normal, il faut appeler la fonction que tu as créée. À la fin du programme, ajoute « dessiner_diams() », et re-bling-bling !

Si tu appelles plusieurs fois de suite la fonction, ça ne changera pas le dessin final, car tu dessines plusieurs fois au même endroit.

Or, la fonction utilise les variables pos_x et pos_y pour se positionner avant de dessiner. Il suffit donc de les modifier puis de re-appeler la fonction.

Tant qu’on y est, tu vas ranger un peu ton code. En général on met les imports au début, puis la définition des fonctions, puis le code principal. Ça va donc donner un truc comme ça :

import turtle
# Cette ligne, y'a qu'à la laisser au tout début.
turtle.speed(0)

# Ici, il y a la définition de la fonction "dessiner_diams"

# Ensuite, la définition des variables pos_x et pos_y, 
# avec les mêmes valeurs qu'avant : -40 et +30

# Appel de la fonction.
dessiner_diams() 

# On redéfinit les variables pos_x et pos_y, 
# avec, disons : pos_x = 10 et pos_y = 60

# Re-fonction.
dessiner_diams()

Et ça devrait donner deux diamants, à deux endroits différents :

Lien vers la réponse, si t’es en PLS.

Avec des paramètres

On a fait un truc bizarre dans le chapitre précédent. On a utilisé les variables pos_x et pos_y à l’intérieur d’une fonction, alors qu’on ne les a pas définies dans la fonction elle-même. Ça n’a pas planté, grâce à la manière dont le langage python gère la portée des variables.

  • Ton programme principal est dans un certain contexte, avec des variables.
  • Tu exécutes la fonction « dessiner_diams », qui se crée un petit sous-contexte pour elle toute seule, avec ses variables.
  • Le python autorise l’accès aux variables d’un contexte englobant, mais uniquement en lecture.

Tu verras peut-être, dans les différents tutoriels du grand internet, les notions de « variables globales » et « variables locales ». C’est à peu près la même chose, mais je n’aime pas trop ces termes, car ça donne l’impression qu’il n’y a que deux contextes, alors qu’il peut y en avoir bien plus.

Je vais pas trop t’embrouiller avec des choses que t’as pas encore vues. Un module possède lui aussi un contexte. D’autre part, tu peux déclarer une fonction dans une fonction, ce qui crée un sous-sous-contexte, et une fonction dans une fonction dans une fonction. Et en plus ça s’appelle pas des contextes mais des « espaces de nommage ». Oublions tout ce batacouac.

Y’a des fois, c’est justifié d’accéder à une variable du contexte englobant, mais là non, c’est pourri.

Déjà, parce que ça oblige à écrire trois lignes de code pour dessiner un diamant où on veut : deux lignes pour pos_x, pos_y, et la dernière pour la fonction. Et ensuite ça fait bizarre, car le comportement de la fonction dépend de choses extérieures à elle. C’est inhabituel, alambiqué, complexe et source de bugs.

Alors on va ajouter deux paramètres à la fonction. Devine comment on va les appeler ? pos_x et pos_y. Hu hu hu.

Il faut ajouter ces paramètres dans la définition de la fonction (la ligne de code avec le mot-clé « def »), ainsi qu’à chaque exécution de la fonction.

Tu n’as plus besoin des lignes de code définissant les variables pos_x et pos_y. Tu peux mettre les coordonnées directement dans les paramètres d’appel à la fonction. Tiens voilà un exemple issu de la même doc que tout à l’heure . Ne t’attardes pas sur le corps de la fonction, osef. Regarde juste comment elle est déclarée, comment elle est appelée, et tu fais le même genre de chose avec ta fonction à toi.

Tu peux faire un petit test pour bien te rendre compte que les paramètres pos_x et pos_y existent dans l’espace de la fonction, mais plus en dehors. Ajoute « print(pos_x) » à la fin de ta fonction. Ça va fonctionner. Ajoute la même ligne tout à la fin de ton programme, ça va merdoyer.

C’est ce qu’on veut, lecteurtrice. Chaque variable et chaque paramètre doit être présent uniquement là où on en a besoin, et ne pas être accessible ailleurs. Sinon ça embrouille, on se demande ce que ça fout là, et on passe pour une baltringue ou un baltringuet (Wesh ! Écriture inclusive !).

Pour finir ce chapitre sur quelque chose d’over-classe, tu mets une boucle de 20 itérations dans laquelle tu exécutes ta fonction. Pour ses deux paramètres, indique directement le compteur de la boucle.

Ça va faire un truc comme ça :

Lien vers le code si t’as vomi ton cerveau.

On taille les diamants

Maintenant que tu as compris le truc avec les paramètres de fonctions, profitons-en pour en rajouter un troisième, permettant de choisir la taille du diamant.

C’est à peu près comme au chapitre précédent :

  • Ajoute un paramètre dans la définition de la fonction, après pos_x et pos_y. On l’appellera « taille ».
  • Utilise ce paramètre dans le corps de la fonction. Ça va se passer dans la ligne de code « turtle.circle(100, 90) »
  • Ajoute le paramètre lors de l’exécution de la fonction. Dans un premier temps, tu peux indiquer une valeur fixe. Le plus simple est de remettre 100.

Avec ça, tu auras la même image que précédemment, mais ton programme sera plus souple, comme lui :

Modifie la boucle affichant les 20 diamants, en faisant varier leur taille, de 0 pixels (inclus) à 100 pixels (exclus), en avançant par pas de 5 pixels.

Et plop :

Lien vers la réponse si t’es pas de taille.

Recentrons-nous

Hé, j’ai une idée géniale. On pourrait afficher des diamants de taille différente, mais tous en coordonnées (0, 0). Ça ferait un truc trop joli, comme une espèce de porte des étoiles magiques en forme de vagin de l’espace.

Sans plus attendre, re-re-modifie la boucle exécutant la fonction, et indique 0 dans les deux premiers paramètres.

Et paf, c’est moche.

Qu’est-ce à dire que ceci ?

La fonction positionne la tortue aux coordonnées (pos_x, pos_y), puis commence tout de suite à dessiner.

Donc ces coordonnées ne correspondent pas au centre du diamant, mais à la pointe de gauche.

Avant de dessiner, il faut se décaler vers la gauche, d’une distance égale à la taille du diamant.

Y’a deux manières de le faire :

Soit tu te positionnes dès le départ où il faut, en changeant le premier paramètre de l’instruction turtle.setpos. Au lieu de « pos_x », tu mets « pos_x – taille ».

Soit tu laisses le setpos comme il est, mais juste après, tu recules de la taille du diamant. La fonction pour reculer, c’est « turtle.backward », elle fonctionne comme le forward.

Je te conseille fortement la deuxième option. Tu en auras besoin au moment de faire tes devoirs. Eh ouais, lecteurtrice, t’auras encore des devoirs. La vie est dure.

Après avoir appliqué le décalage, ça devrait dessiner ça :

Lien vers la réponse, si t’es déconcentré ou déconnecentrée (Wesh, écriture inclusive, t’as vuuuuuu ?)

Diamonds are a girl’s best friend

I have a good filling about this

On va momentanément partir sur autre chose.

Teste-moi ça, dans un autre onglet Trinket :

import turtle
turtle.fillcolor("red")
turtle.begin_fill()

turtle.forward(60)
turtle.left(120)
turtle.forward(60)
turtle.left(120)
turtle.forward(60)

turtle.end_fill()

Ça va dessiner un magnifique triangle rempli de rouge.

Explications :

  • La fonction fillcolor est assez simple : elle définit une couleur de remplissage. Tu peux remplacer « red » par « green », « blue » ou d’autres couleurs en anglais.
  • La fonction begin_fill indique à la tortue qu’on va commencer à tracer un dessin qui sera à remplir.
  • Ensuite, des forward et des left, que tu connais déjà.
  • Finalement, la fonction end_fill indique à la tortue qu’on a fini le dessin et que le remplissage doit être effectué.

Ce n’est pas forcément évident à comprendre. Ces fonctions de fill impliquent beaucoup de traitements réalisés en interne par le python.

Lorsque tu es entre un begin_fill et un end_fill, à chaque fois que tu traces un bout de truc, que ce soit avec un forward, un circle ou autre chose, la tortue enregistre tous les pixels par où elle est passée. Au moment du end_fill, la tortue en déduit l’espace interne de pixel délimité par ton tracé, et elle les colorie. Comme l’outil « pot de peinture » dans les logiciels de dessin, t’as vu ?

Pas besoin de te prendre la tête avec ces traitements compliqués, le python le fait tout seul (plus exactement, c’est la librairie de code turtle qui le fait, osef). Mais du coup, c’est pas évident de comprendre ce que ça fait.

On pourrait presque considérer que tout ce que tu fais entre le begin_fill et le end_fill constitue un sous-bloc de code. D’ailleurs, si ça ne tenait qu’à moi, j’aurais fait un context manager, et ça se serait explicitement écrit comme des sous-blocs de code. Un « context manager », c’est un truc spécial du python que tu peux mentionner quand tu veux montrer à tes lecteurtrices à quel point tu es un over-expert du python.

Et là, lecteurtrice, je me plaît à imaginer que tu t’intéresses à ce que je te raconte jusqu’au plus profond de tes neurones, et que donc tu te poses plein de questions :

  • Si je dessine n’importe quoi et que je ne reviens pas à mon point de départ, est-ce que ça va colorier toute l’image comme dans Paint ?
  • Si je croise des traits, ça va se colorier comment ?
  • Si je fais plusieurs begin_fill et/ou plusieurs end_fill à la suite, ça donne quoi ?

Réponse : t’as qu’à tester. Tiens, je t’ai fait quelques exemples.

Bon, ceci étant expliqué, je veux un diamant de sang ! Yaaahahahhaaarrrr !!

Modifie la fonction de façon à ce qu’elle dessine un diamant coloré en rouge. Il suffit d’ajouter les instructions de fill, aux bons endroits.

Ensuite, les diamants vont se dessiner en se recouvrant l’un après l’autre. À la fin, tu auras ça :

Lien vers la réponse, si tu commences à pleurer du sang.

Ce sera tout pour aujourd’hui

Dans mon article précédent, j’ai dit qu’on ferait ça :

On n’y est pas encore, mais on s’en rapproche. Je m’arrête là pour l’instant. J’ai d’autres trucs à faire que d’écrire des cours de python. Désolé. Promis, le mois prochain, on y arrive.

En attendant, pour pas que tu t’ennuies, BADAM-BING ! LES DEVOIRS !!!

Dessine-moi ça :

Petit indice : depuis le début, on dessine des diamants tout droit, parce que l’orientation initiale de la tortue est droite. Et si tu tournais un peu ta tortue avant de dessiner un diamant ? Pas trop, un ou deux degrés…

Découvrir la programmation python et faire des petits dessins, là tout de suite

Hey !

Alors comme ça tu veux essayer la programmation et tu ne sais pas par où commencer ? Tu as joué un peu avec des formules Excel et tu as trouvé ça relou ? Tu as pris peur devant la quantité faramineuse de case à cocher et de Giga-octets nécessaires à l’installation de Visual Studio ? On s’est moqué de toi quand tu as dit que le C++ était mieux que le HTML ? Allez, viens, t’as cliqué à la bonne porte. J’ai filé des cours de python à des jeunes en collèges et lycées, et j’ai presque eu l’impression d’avoir été intéressant. Autant t’en faire profiter !

Non-installation des outils nécessaires

Quand on débute en programmation, il faut s’inscrire sur des sites (github, stackoverflow, …), installer des interpréteurs, des compilateurs et un tas d’autres bronxs. Sauf que toi, tu veux pas forcément t’y mettre sérieusement, tu veux juste découvrir pour savoir si ça vaudra le coup de t’y mettre sérieusement. Tkt, lecteurtrice, je suis là pour ça.

Va voir par ici : https://trinket.io/python (ouvre le lien dans un nouvel onglet, car tu auras besoin de passer de l’un à l’autre).

C’est un petit environnement d’exécution en python, rien que pour toi. « Python » est le nom d’un serpent, mais aussi d’un langage de programmation.

Dans la partie gauche, c’est le code que tu écris, dans la partie droite, le résultat de l’exécution de ce code. T’as vu lecteurtrice ? J’ai utilisé le mot « code ». Ça veut dire exactement la même chose que « programme », mais ça fait plus expert. Hésite pas à utiliser ce mot.

Un premier exemple s’est déjà exécuté. On s’en fout. Efface tout le texte de la partie gauche et met seulement ces trois lignes :

import turtle
turtle.forward(50)
turtle.left(90)

En haut de la fenêtre se trouve un bouton « Play » (une flèche vers la droite), clique dessus. Si y’a pas, t’as un bouton « Stop » à la place, clique puis reclique dessus.

Devant tes yeux ébahis, un trait va se tracer.

Tant qu’à faire, clique sur le bouton « Expand » en bas de la fenêtre, ce sera plus pratique.

Mais que sont donc ces trois lignes que tu as copié-collées ?

La première sert à importer la librairie de code intitulée « turtle ». C’est ce qui te permet de tracer des petits dessins.

Si t’es un vieux ou une vieille, lecteurtrice, tu te souviens peut-être du plan « Informatique Pour Tous » et surtout du langage « Logo ». Turtle en est inspiré.

Le principe, c’est que la petite flèche noire dans la partie droite représente une tortue (ou n’importe quelle autre animal si tes goûts en matière de zoophilie sont différents). Tu la diriges avec des instructions de code.

  • « turtle.forward(50) » signifie que tu la fais avancer de 50 pixels.
  • « turtle.left(90) » signifie que tu la fais tourner de 90 degrés vers la gauche.

Juste pour montrer que tu as bien compris, tu vas changer le code pour me dessiner un beau carré de 50 pixels de côté. T’as juste à re-écrire trois fois de plus les deux dernières lignes. Fais un copier-coller, ça passe crême. Ne recopie pas « import turtle », qui n’a besoin d’être écrit qu’une seule fois au début. Appuie sur Play pour relancer le programme.

Ça y’est, t’imprimes ?

On peut aussi écrire du texte. À la fin de ton programme, ajoute la ligne :

print("yo ho ho ho. lolilol")

Le carré va se redessiner et du texte va s’afficher dans la partie juste en-dessous.

La fonction « print » affiche en bas à droite le texte que tu lui indiques. Ça s’appelle la sortie standard. C’est un truc de base que tu as tout le temps, même avec autre chose que le python.

En général, quand on découvre la programmation, on ne commence qu’avec ça, on ne dessine pas avec turtle. Du coup, on se retrouve à faire des exercices poucraves, genre pisser la suite de Fibona-qui-chie ou sortir la solution d’une équation du premier degré. C’est chiant. Nous on fais pas ça, nous on met des paillettes dans la life, t’as vu ?

Quand tu programmes, que tu sois débutant ou pas, il y a plein de fois où tu écris des trucs pétés qui déclenchent des erreurs. Ça fait partie du jeu. Dans le bel environnement de développement que je te propose, les erreurs s’affichent en bas de la fenêtre, et la ligne de code pétée est surlignée en rouge. On va tester ça tout de suite. Après le print, tu écris n’importe quoi. Vas-y lâche toi, fais pas ta/ton mijauré·e. (Wesh ! T’as vu lecteurtrice ? Je fais de l’écriture inclusive, c’est à la mode).

Appuie sur Play, et tu verras un message outrageusement irrespectueux, genre « NameError: nanani » ou « Bad input: nianiania ».

Les messages d’erreurs sont plus ou moins explicites, selon ton niveau de connaissance, la débilité que tu as écrite et la complexité de ton programme. Des fois ça peut aider de chercher sur internet ce que ça veut dire. Des fois pas. Tkt, lecteurtrice, tout au long de ce tuto, je t’ai mis des exemples de code qui fonctionnent. Si t’es grave dans la derche, ça t’aidera. Mais essaye à chaque fois de trouver la réponse par toi-même.

L’autre truc rigolo, c’est quand ton programme n’a aucune erreur, mais qu’il ne fait pas du tout ce que tu veux. Dans ce cas, met des « print ». Ça te permet de voir par où passe l’exécution, d’afficher le contenu de tes variables, etc.

On va tester ça tout de suite. Ajoute ça à la fin de ton programme :

x = 5
print("Valeur de x")
print(x)

Tu peux aussi écrire plusieurs trucs sur une même ligne, en séparant chaque valeur par une virgule. Ajoute aussi ça :

print("Valeur de x :", x)

Et rond et rond

On continue avec un truc simple. En plus du carré, tu vas me dessiner un cercle de rayon 60 pixels. Lorsque ton programme s’exécute, on doit voir ça :

Tu sais pas comment on dessine un cercle ? Mince alors. Eh bien, cherche. Va voir par là si j’essuie : https://docs.python.org/3.8/library/turtle.html#turtle.circle

C’est aussi ça, la prog. Tu passes ton temps à chercher comment on fait certains trucs, parce qu’on ne peut pas retenir tous les trucs.

Ensuite, au lieu d’un cercle entier, tu me dessines juste un tiers de cercle. Faut que ça donne ça :

Pour t’aider, re-zy-va voir le lien internet sus-cité.

Comme promis, lecteurtrice, voilà un lien vers la réponse, à n’utiliser qu’en cas d’extrême urgence mondiale, si tu n’arrive pas à trouver par toi-même.

Humeur variable

Mine de rien, on a fait un truc super important dans le chapitre pré-précedent, avec l’instruction « x = 5 ». On a déclaré une variable.

Ooooohhhhh.

Définition : une variable est un truc dans lequel tu mets le machin que tu veux. Tu peux ensuite réutiliser le truc, ça fait comme si c’était le machin. Débrouille-toi avec ça.

Le signe « égal » utilisé n’a pas la même signification qu’en maths. C’est une affectation et pas une égalité. Ce sont deux notions différentes, mais qui existent toutes les deux en programmation. On verra ça plus tard.

Dans ta tête, quand tu vois écrit « x = 5 », tu peux te dire « x prend la valeur de 5 », ou bien « x devient 5 ». Ce sera plus exact que « x égal 5 ».

Tu peux aussi te faire dans ta tête la voix française de Jim Carrey. Sérieusement, ça aide.

Si tu veux plus de détails sur les variables, va voir ce tutoriel. Il est assez bien fichu, y compris les chapitres après, sur les listes, les tuples, les fonctions… Ça pourrait te servir pour la suite.

On est pas chez ces ringards de matheux, t’as le droit d’avoir des noms de variables de plusieurs lettres, à condition qu’il n’y ait pas d’espace. Par exemple : « ma_super_variable = 5 ».

Ton environnement de dev a aussi un mode console, mais je t’embête pas avec ça pour l’instant. Par contre, un truc utile, au lieu de cliquer sur Play, tu peux utiliser le raccourci Ctrl+Entrée. Ça exécute ton code.

Si tu veux avoir l’air d’un vrai programmeur qui a des gonades, utilise des raccourcis claviers plutôt que ta souris. (Wesh lecteurtrice ! T’as vu ? Écriture inclusive avec le mot « gonades »). Comme t’as souvent les mains sur le clavier pour écrire du code, vaut mieux essayer de tout faire avec le clavier plutôt que de perdre une demi-seconde à attraper la souris.

Autre bidouille, spécifique à turtle. Au début de ton programme, juste après l’instruction « import », tu peux ajouter la ligne « turtle.speed(0) ».

La tortue dessinera ses traits et ses cercles plus vite. Tu me remercieras quand tu en seras à exécuter ton code pour la vingtième fois parce que tu seras en train d’essayer de faire marcher quelque chose qui ne veut pas marcher.

Une boucle pour une spirale

Commence par nettoyer le bazar des chapitres précédents. Supprime toutes les instructions de dessins : « turtle.forward », « turtle.left » et « turtle.circle ». Si tu tiens absolument à garder ces premiers blablablutiements, sauvegarde-les dans un petit fichier texte. Ou sinon, laisse-les où ils sont, mais en commentaire. Il suffit d’écrire un caractère dièse « # » au début de la ligne.

Très utile les commentaires. Ça peut servir à expliquer des choses directement dans le programme. Tu peux aussi écrire ton autobiographie.

Si je te fais enlever tout ça, c’est pour repartir sur une zone de dessin propre. Parce qu’on va faire du fun.

Des spirales comme la coiffure de princesse Leia

Si t’es pas trop vieux ou vieille, lecteurtrice, tu as certainement entendu l’expression de langage de jeune : « être en boucle sur quelque chose ». Ça vient de l’informatique.

Sans plus attendre, copie-colle moi cette ligne à la fin de ton programme :

for compteur in range(11):print(compteur * 8)

Tu exécutes, et ça écrit la table de 8 dans la sortie standard.

Cette ligne de code est à lire en deux parties, séparée par le caractère « : ». La première partie indique que tu veux faire une boucle, c’est à dire exécuter plusieurs fois la même chose.

Le « 11 » indique que cette boucle s’exécutera 11 fois, en comptant de 0 à 10. Ça a l’air crétin de commencer à compter à partir de zéro, mais ça se justifie. Je prendrais le temps de t’expliquer plus tard, quand tes poils auront poussés, jeune lecteurtrice.

Le morceau de code après le deux-points, c’est le contenu de ta boucle. C’est à dire l’instruction que tu veux exécuter plusieurs fois. Tu la connais déjà, c’est un « print ».

Le caractère étoile « * » signifie simplement la multiplication. En maths, la multiplication est notée par une croix, mais ce caractère n’existait pas sur les premiers ordinateurs. D’ailleurs, ces crétins de matheux n’utilisent même pas la croix, ils mettent rien du tout. « 2a » signifie « 2 multiplié par a ».

Déso, mais le caractère rien-du-tout ça existe pas, même sur les ordinateurs récents. Qu’est-ce qu’ils sont cons ces matheux.

Revenons aux boucles. Celles-ci sont gentilles. En plus de répéter un truc, elles te donnent gratochement une variable. Ici, on l’a appelé « compteur ». Celle-ci change automatiquement de valeur à chaque passage de la boucle.

Ça fait comme si t’avais écrit :

compteur = 0
print(compteur * 8)
compteur = 1
print(compteur * 8)
compteur = 2
print(compteur * 8)
...
compteur = 10
print(compteur * 8)

Ceci étant expliqué, on est bien d’accord que la table de 8, c’est des maths et c’est ringardos. Nous on veut du fun. On va dessiner une putain de spirale !

Tu sais comment ça se dessine, lecteurtrice ? Il suffit de tracer des morceaux de cercles de plus en plus grand. On dit qu’on fait des tiers de cercles, avec des rayons qui augmentent de 10 pixels en 10 pixels. On en fait 15.

Si t’es débile, lecteurtrice, tu vas faire ça :

turtle.circle(0, 120)
turtle.circle(10, 120)
turtle.circle(20, 120)
...

Mais tu vaux mieux que ça et tu me le fais sous forme d’une boucle, Okaye ?

Le dessin tracé ressemblera à ça :

Le lien vers la réponse, si jamais tu galères.

Une spirale qui grossit et des blocs de code

Sais-tu qu’on peut changer la taille du trait ? Mets-nous l’instruction « turtle.pensize(5) » juste avant ta boucle. Voilà une jolie spirale plus épaisse. Le nombre indiqué correspond à une taille en pixel. Évite de mettre trop gros, ça fera des pâtés moches. Le pensize doit être petit. D’ailleurs « pensize » est un anagramme de « ze penis ».

Soyons over-fun. Tu vas me dessiner une spirale de plus en plus épaisse. Le trait pourrait varier de 0 à 14 pixels, par exemple. Ça tombe bien, ta boucle s’exécute déjà avec un compteur qui va de 0 à 14.

Et là on arrive à une notion très importante en programmation (python ou autre) : les blocs de code.

Un programme est un gros bloc de code, avec plusieurs instructions dedans.

Dans un bloc de code, il peut y avoir des sous-blocs, eux-mêmes pouvant contenir des sous-sous-blocs, etc. Un peu comme ces ringards de matheux quand ils imbriquent des parenthèses. Zou, un exemple.

3 * ((1 + 6) * 2) + 4 * (2 + 5) – 1
Cette expression contient deux sous-blocs : ((1 + 6) * 2) et (2 + 5).
Le premier sous-bloc contient un sous-sous bloc : (1 + 6)

La comparaison s’arrête là. En maths, on évalue en premier les sous-blocs les plus profonds. En programmation, on commence au début, et on avance en rentrant (ou pas) dans les sous-blocs.

Une boucle annonce un sous-bloc, qu’elle exécutera plusieurs fois. Dans le chapitre précédent, ton sous-bloc n’avait qu’une seule instruction : « turtle.circle(compteur * 10, 120) ».

Chaque début et fin de sous-bloc doit être clairement indiqué dans le programme. Dans les langages C et Javascript, on utilise pour ça des accolades « {} », en LISP, des parenthèses « () », en Pascal, les mots « begin » et « end », etc.

Pour que le programme soit plus facilement lisible, on indente les blocs. Toutes les instructions d’un sous-bloc doivent être écrites avec un décalage de quelques espaces au début (en général on en met 4). Les instructions d’un sous-sous-bloc doivent être décalée deux fois, soit 8 espaces, et ainsi de suite. Quand on sort d’un bloc, on enlève un décalage d’indentation.

Il y a des tas de gens qui ne respectent pas cette mise en forme. Leurs programmes sont alors beaucoup moins lisibles. Si tu rencontres ce type de personnes, il est de ton devoir de les insulter et les stigmatiser.

Tu as sûrement déjà vu des images corporate bullshit montrant un écran d’ordinateur avec des lignes de code. C’est hype, swag, geek, hacker et tagada-tsoin-tsoin.

T’as vu lecteurtrice comment sont organisées ces petites lignes ? Ça se décale, puis ça se recale, se redécale, se rerecale. C’est ça l’indentation.

Et en python, comment donc marque-t-on les débuts et fin de blocs ?

Tu vas rire lecteurtrice, c’est par l’indentation elle-même ! Quand tu veux que des lignes de code soient dans un sous-bloc, tu leur mets des espaces au début. Quand ton sous-bloc se termine, tu écris la ligne suivante sans les espaces.

C’est un peu déroutant, mais ça a un très gros avantage. Ton code est obligé d’être bien présenté. En python, tu ne pourras jamais te faire insulter ou stigmatiser parce que tu n’auras pas respecté les règles d’indentation. (On pourra toutefois t’humilier pour plein d’autres raisons).

Les éditeurs de texte conçus pour la programmation prennent en compte cette particularité des indentations. Essayons ça tout de suite. Retourne à ton programme et copie-colle ce texte, sans oublier les deux-points à la fin : « for compteur in range(15): » , puis tu appuies sur Entrée. Ton curseur va à la ligne d’en dessous, mais pouf ! Il s’est décalé de 2 espaces ! Écrit un truc puis réappuie sur Entrée, ça restera décalé.

Quand tu as fini ton bloc, appuies sur backspace, ça enlève les deux espaces d’un coup, et tu retournes au bloc de code supérieur.

Dans l’environnement que je t’ai proposé, une indentation est représentée par 2 espaces. Ailleurs, ce sera 4, ou 8, ou une tabulation. Il y a des discussions sans fin entre les développeurs pour savoir s’il vaut mieux indenter avec des espaces ou des tabulations. Je ne t’en parle pas maintenant, tu n’es pas encore assez mature pour réaliser à quel point les développeurs peuvent avoir des discussions immatures.

Assez discuté, au boulot ! Tu me fais cette boucle, avec un sous-bloc de deux instructions. La première est un « pensize », la seconde un « circle ».

Ça doit donner un truc comme ça.

Lien vers la réponse si c’est la hass pour toi.

Ce sera tout pour aujourd’hui !

J’espère que ça t’as plu lecteurtrice. To be continued le mois prochain. On fera un dessin comme ça :

En attendant, voici tes devoirs. Essaye de me faire un dessin comme ça :

Je te donne pas le code. Tu te débrouilles. C’est réalisable avec les notions que je viens d’expliquer. Deux petits indices, quand même :

– On peut faire des boucles imbriquées, c’est à dire une boucle dans une boucle. Pense à rajouter une indentation (un décalage d’espace au début de la ligne) à chaque fois que tu rentres dans une nouvelle boucle. Et utilise un nom de variable différent pour chaque boucle, sinon ça va confusionner.

– Les tracés de cercle (et de morceaux de cercle) dépendent de l’orientation initiale de la tortue. Tu peux tester ça vite fait : écrit un programme qui fait uniquement « turtle.circle(60, 120) », puis ajoute juste avant l’instruction « turtle.left(180) ». Le morceau de cercle changera de sens.

Un dernier conseil : n’écris pas trop de code d’un coup. Tu rajoutes quelques lignes, tu appuies sur Play pour vérifier que ça swagge, tu rajoutes ou modifies quelques autres lignes, tu retestes, etc. Si t’écris trop d’un coup et que ça plante quand tu testes, ce sera plus difficile de trouver où que c’est que ça merdouille.

Comme je suis gentil et fun, tu auras le la réponse à l’exercice dans mon prochain article.

Ook ook à toi, lecteurtrice !

*Drop du clavier et je m’en vais comme un prince.