lftp, c’est quand même mieux

Vous savez que depuis des années, j’essaye de maintenir à peu près à jour un lifestream chez free, à partir de mes contributions sur WordPress, Shaarli, Goodreads, … J’ai un gros bout de code Java qui génère le site web localement (assez lentement, mais ça n’est pas grave pour l’instant). Mais jusqu’à aujourd’hui, j’avais un autre problème au moins aussi pénible : l’upload sur ftpperso.free.fr. J’utilisais WinSCP et son mode de synchronisation, mais d’une part c’était très lent, et surtout, WinSCP semble ne pas être capable de bien gérer le maintien des connexions avec free.fr. Du coup, j’avais des tonnes de déconnexions. Un truc vraiment pénible.

Je savais bien que ça venait en partie du fait que j’envoyais quelques milliers de petits fichiers, plutôt que quelques gros fichiers. Alors en réfléchissant un peu, je me suis dit que je pourrais envoyer un ZIP que je dézipperai sur le serveur. Par exemple, ce script PHP permet de dézipper localement un fichier. Malheureusement, la version de PHP disponible sur free.fr n’inclut pas le support des fichiers ZIP. Du coup je me retrouve avec des messages d’erreurs pénibles : « Your PHP version does not support unzip functionality. ». Bref, ça ne marche pas.

Alors, en désespoir de cause, je me suis dit que je pouvais revenir aux fondamentaux. Le fondamental du client ftp, c’est évidement lftp. Et donc, en cherchant un peu, je suis tombé sur ces exemples de commande lftp. Et en particulier sur cette commande, qui répondait bien à mon besoin : mirror -R.

Résultat ? C’est un poil plus rapide :

lftp nicolas.delsaux@ftpperso.free.fr:/lifestream> mirror -R
Total: 141 directories, 2681 files, 0 symlinks
New: 25 files, 0 symlinks
Modified: 1487 files, 0 symlinks
123219275 bytes transferred in 7127 seconds (16.9 KiB/s)
To be removed: 399 directories, 11059 files, 0 symlinks

Evidement, 7127 secondes, ça paraît long (ça fait 1 heure, 58 minutes, 47 secondes). Bon, ça paraît long, mais au moins, ça marche (parce que les transferts avec WinSCP avaient une sale tendance à planter au bout d’une heure). Autrement dit, ça marche bien mieux !

Maven – smart code, dumb pipe

J’ai eu la chance ces deux derniers jours d’animer une formation maven. Oui, en 2021, il y a encore des gens qui ont besoin d’être formés sur cet outil.

Pendant ces deux jours, j’ai parlé abondamment des qualités de maven.

Parce qu’il a plein de qualités, évidentes ou pas :

  • La gestion de dépendances, évidement, qui fait partie des apports fondamentaux
  • Le cycle de vie, qui augmente considérablement les chances de pouvoir faire des builds corrects
  • Mais aussi, et surtout pour moi, le côté « chiant as a service »

Parce que maven n’est pas un outil commode à tordre. Il est rempli de postulats sur la bonne manière de construire un projet. Et souvent, ces postulats rentrent en conflit avec l’envie des développeurs d’ajouter de l’intelligence dans le build. On retrouve cette discussion notamment dans cet échange sur Hacker News : l’article initial dit bien que maven est limité parce qu’il s’appuie sur des plugins sans être turing-complete, ce que disent également les commentaires.

De mon point de vue, c’est une erreur conceptuelle.

Maven, comme je l’expliquais hier, est l’un des premiers maillons de la chaîne d’intégration continue.

Et comme Kafka a réussi parce que c’est un tuyau idiot, je pense qu’une bonne chaîne d’intégration continue doit être idiote. C’est-à-dire accomplir simplement les différentes étapes amenant le code de la machine du développeur jusqu’à la production. Et pour ça, avoir un outil comme maven qui pousse les développeurs dans la direction de la simplicité est, à mon avis, une bonne idée.

L’échec du skeuomorphisme

Pendant longtemps, j’ai eu du mal avec ce terme de designer.

Le skeuomorphisme, c’est concevoir une application avec une interface qu’on pourrait appeler « naturaliste », c’est-à-dire qu’elle reprend les éléments du monde physique dans le monde virtuel (mais la page wikipedia est évidement bien plus claire et plus complète que ça).

Mais pourquoi est-ce que je parle de ça alors alors que je ne suis pas designer ?

Parce que j’ai une freebox. Et que sur ma freebox, la télécommande ne marche plus du tout depuis samedi soir. Dans ces cas (parce que ça n’est pas la première fois que ça arrive), je sors une des innombrables applications/extensions de télécommande freebox.

Et ce week-end, j’ai testé

Et malgré des bugs plus ou moins nombreux, toutes souffrent du même problème : elle copient l’interface de la télécommande physique, y compris la disposition des boutons.

Or, la télécommande physique a, grâce aux différentes épaisseurs des boutons (comme sur les touches F et J des claviers d’ordinateurs), un retour haptique. Ce que ne permettent évidement pas ces applications qui s’affichent sur des écrans. Et du coup, l’expérience devient franchement pénible : je clique, puis je regarde si l’action demandée par la télécommande a bien été prise en compte par la freebox, et je reclique, et je recommence. C’est fastidieux, et inefficace. Je préférerai largement une application embrassant la diversité des interfaces pour me proposer quelque chose de plus malin : swiper pour déplacer le curseur, afficher les enregistements sur l’écran de mon appareil, etc. Tout ça est possible, mais nécessite une chose

Je vais jeter un oeil approfondi à freeteuse histoire de voir si rien n’existe …

Youtube sans youtube ?

Hier soir, j’ai eu comme une idée ..

En fait, c’est parfaitement possible et même raisonnablement facile.

Chez moi, j’ai un système d’information particulièrement sophistiqué (enfin, certains collègues me diront que non). Dans ce système, je regarde la plupart des vidéos depuis mon NAS, qui les expose avec UPnP AV. C’est simple, et c’est facile. Je dis que je regarde la plupart des vidéos, mais en fait, pas celles de Youtube. Parce que la freebox revolution a une application dédiée qui est … raisonnable. Raisonnable, en ce sens qu’on retrouve les fonctionnalités typiques de Youtube : les recommandations putaclic (dont bien sûr les vidéos conspirationnistes et autres), les vignettes « bien choisies », et globalement tous les plaisirs de l’économie de l’attention. L’un des plaisirs me touche particulièrement : la pub à tout crin. Parce que les vidéos coupées tous les 1/4 d’heure, c’est moyen pour l’immersion…

Et il n’y a pas si longtemps, j’ai découvert youtube-dl et vividl. C’est pas mal, mais l’idée que je télécharge les vidéos sur ma machine pour les copier dans le dossier vidéos de ma freebox me fatigue un poil (en plus du fait d’aller chaque jour ouvrir la page youtube d’une ou deux chaines bien choisies). Et ce week-end, j’ai réfléchi à tout ça, et je me suis dit que je pouvais simplifier les choses : avec rss-bridge (et son bridge youtube), je peux recevoir les nouvelles vidéos dès qu’elle paraissent. Il ne me reste plus qu’à les copier.

Ce que je peux faire avec MeTube, dont je peux installer l’image Docker sur mon NAS pour qu’il dépose directement les vidéos dans le dossier exposé en UPnP.

Sur le papier, c’est superbe.

Sur le papier ? Oui, parce qu’il ya parfois des moments où le hacker s’oppose à l’architecte. Le hacker dit « fait ça, ça marche ». Et l’architecte dis « ça ne correspond pas aux concepts de mon architecture ». En l’occurence, le concept, c’est que le NAS ne doit être utilisé que comme point de stockage ou d’accès, et surtout pas comme point d’acquisition de données. Autrement dit, stocker des vidéos téléchargées par mon Raspberry, oui. Télécharger directement des vidéos du web sur le NAS, non. Du coup j’ai arrêté, parce que j’allais démarrer des conteneurs sur mon Raspberry, quand je me suis rendu compte (à nouveau) que je pouvais difficilement installer un conteneur pour une application web : si je pars dans cette direction, il faut que je fasse de chacune de mes applications un conteneur. et je ne suis pas sûr d’avoir pour chaque application un conteneur compatible avec mon Raspberry PI 3. Je vais regarder tout ça de plus près … (surtout parce que grâce à watchtowerr, ces images sont toujours à jour).

Remplacer PlantUML par Kroki ?

Avant tout, un peu de mise en contexte …

Vous savez sans doute que j’écris maintenant tous mes documents avec Asciidoc. Et comme je fais un travail assez technique, je dois régulièrement illustrer mon texte de diagrammes divers et variés. Et pour ça, j’utilise autant que possible PlantUML. Et dans la plupart des cas c’est très cool. Mais dans les cas où ça ne marche pas … eh bien ça ne marche pas, et je me retrouve bien embêté.

Par exemple, pour faire les graphiques qu’on trouve dans toutes les présentations de manager, on peut facilement utiliser vega grâce à asciidoctor-diagram ! Sauf que vega, en tant que librairie Javascript, a des dépendances … exotiques. En effet, vega-cli tire directement canvas, qui s’installe très difficilement sous Windows. Entre gyp qui vous oblige à télécharger Python 2, et GTK, c’est vraiment la fête. Et autant dans Asciidoc on peut utiliser une instance externe de PlantUML (ce que je fais systématiquement grâce à Docker), autant on ne peut pas utiliser d’instance externe de vega . Correction : en cherchant comment configurer asciidoctor-diagram pour utiliser une instance PlantUML externe, je viens de découvrir qu’en fait, ça n’est pas possible dans asciidoctor-diagram (parce qu’il utilise naturellement une instance embarquée du jar plantuml) mais uniquement dans la preview Asciidoc de VSCode.

Pour en revenir à vega, il est inutilisable sous Windows.

Et c’est là que Kroki entre en scène.

Kroki fournit tout un tas de librairies de diagram as code à travers une API unifiée. Et ces librairies sont aussi utilisables dans des images Docker (ce qui supprime les problèmes de vega). Hélas, il faut lancer toutes ces images avant de pouvoir utiliser asciidoctor-kroki.

Et la doc est claire : on peut lancer toutes ces images sans docker-compose (qui me fatigue un tout petit peu) du moment qu’on met les bonnes variables d’environnement.

Et l’avantage si je me passe de docker-compose, c’est que mon instance de watchtower maintiendra tout ça à jour (j’aurais pu aussi ajouter watchtower dans le fichier docker-compose.yml, mais je n’ai pas envie de me fatiguer).

Allons-y donc !

D’abord, installer les images Docker des librairies externes (dans mon cas, excalidraw, parce que les autres font partie de l’installation standard – ce qui peut être embêtant, si kroki met à jour ses dépendances « trop lentement »)

docker run --name kroki-excalidraw --restart=always -p 29004:8004 yuzutech/kroki-excalidraw

Et ensuite, installer l’image Docker kroki avec plusieurs subtilités que j’expliquerai par la suite

docker run --name kroki --restart=always -p 29000:8000 -e KROKI_EXCALIDRAW_HOST=kroki-excalidraw -e KROKI_SAFE_MODE=unsafe -e KROKI_PLANTUML_ALLOW_INCLUDE=true yuzutech/kroki

Donc comme j’ai lancé kroki-excalidraw à part, il faut indiquer quelle machine gèrera les diagrammes excalidraw (d’où KROKI_EXCALIDRAW_HOST). Et les autres variables d’environnement devraient me permettre de continuer à utiliser des includes dans mes diagrammes PlantUML, ce que je fais … beaucoup.

Et paf, ça marche !

Reste maintenant à faire marcher tout ça dans mes documents Asciidoc avec asciidoctor-kroki. Ce qui n’est pas si compliqué, puisque j’ai bien l’impression que c’est la même syntaxe qu’asciidoctor-diagram.

Installer un gem dans un build Java n’est pas si simple, heureusement, je l’ai déja fait! Reste maintenant à tester ça, par exemple avec un diagramme excalidraw ?

Du raspberry au PC … en passant par l’action GitHub

Je vous parlais il y a quelques semaines déjà de mon lifestream, et du fait que je dépassai les limites de traitement de mon Raspberry. Et même si je sais parfaitement que le code, dans son état de l’époque, tournait raisonnablement bien sur mon PC, l’idée de lancer une tâche planifiée m’enthousiasmait … moyennement.

Et puis, les « projets perso » sont l’occasion idéale de tester des technologies plus ou moins pratiques. En l’occurrence, ça n’est pas vraiment un test, puisque c’est une technologie que j’ai déjà utilisée pour rrss2imap : GitHub Actions !

J’ai donc ajouté dans mon projet GitHub une action qui lance mon build maven et … c’est là que les choses ont dérapé.

En effet, à cause de wordpress.com, j’ai dû il y a un long moment le très chouette HtmlUnit par le presque aussi chouette, mais beaucoup plus lourd, Selenium. Evidement, les instances Docker qui font tourner les workflows GitHub ne contiennent pas d’installation de Chrome, et encore moins d’installation de Chrome disposant de Selenium. Pour ça, il faut passer par la possibilité qu’offe Selenium d’utiliser un runner distant … Et de l’action GitHub qui va bien (en l’occurence start-selenoid-server). Parce qu’évidement, comme il est plus simple de faire compliqué, le projet selenium a une longue, belle, et intéressante histoire. Et le passage au monde des conteneurs a là aussi fait des ravages. Donc pour faire tourner des runners Selenium dans le monde Docker, on utilise … Selenoid ! L’avantage de Selenoid, c’est qu’il tourne dans tous les environnements Docker. L’inconvénient de Selenoid, c’est que l’installation dévie subtilement de la méthode traditionnelle des conteneurs, en passant par un curieux Configuration Manager. C’est sans doute pour démarrer le conteneur d’orchestration avec un accès à la socket Docker … Parce que je ne vous ai pas expliqué, mais dans selenoid, il y a en fait plusieurs conteneurs : un orchestrateur qui reçoit les requêtes des clients Selenium et démarre les conteneurs hébergeant les conteneurs, et les conteneurs hébergeant les différents navigateurs. En bonus, l’utilisateur qui veut accéder aux navigateurs grâce à un client VNC web peut utiliser selenoid-ui, qui permet aussi de télécharger des vidéos des sessions de navigation.

Tout ça est fort bien. Mais, comme il n’y a pas de médaille qui n’ait son revers, certaines choses deviennent plus difficiles. Dans mon cas, il s’agit du téléchargement de fichiers. Parce que si j’utilise Selenium et un navigateur Chrome, c’est pour pouvoir télécharger les exports des données de différents sites. Et si télécharger un backup depuis un Chrome local est facile, télécharger le même backup depuis un Chrome tournant dans un conteneur distant se révèle une autre salade.

D’abord, il faut pouvoir télécharger un fichier depuis le conteneur, ce que permet selenoid grâce à son API http.

Ca marche bien dans des cas simples. Par exemple, pour goodreads, l’export est disponible via un lien HTTP. Donc quand je récupère la cible du lien avec Selenium, je peux obtenir le nom du fichier tel qu’il sera téléchargé par le navigateur, et utiliser ensuite l’api http pour télécharger le fichier localement.

En revanche, pour WordPress, c’est plus délicat. Quand je clique sur le bouton « générer un export », wordpress génère un fichier dont le nom est timestampé. Et je dois donc localiser le nom du fichier avant de le télécharger.

Et tout ça marche très bien … en local.

Parce que dès que j’exécute le bout de code dans GitHub Actions, je reçois cette erreur

Error: Failed to execute goal org.ndx.lifestream:goodreads:0.0.1-SNAPSHOT:goodreads (download goodreads) on project my-lifestream: there was a failure during pages construction: org.ndx.lifestream.plugin.exceptions.UnableToDownloadContentException: unable to download CSV from Goodreads: provided url http://localhost:4444/wd/hub can't be parsed: Could not copy "http://localhost:4444/download/e3b9d591ff9f4e3d26669cecfb1104c8/goodreads_library_export.csv" because it does not exist. -> [Help 1]

Voir par exemple cette exécution.

Et là, j’ai testé tout ça plusieurs fois, et je n’ai trouvé aucune solution pour corriger ça. ce qui fait que cette génération de lifestream marche bien (mais un poil lentement – 2 heures, à peu près) sur ma machine, mais plante en 3 minutes chrono chez GitHub.

Cela dit, j’ai quand même appris quelques détails amusants sur le pilotage de navigateurs. Et j’ai pu remplacer le Chromium avec Selenium qui tourne localement sur ma machine par un Chromium qui tourne dans Docker, ce qui est plus classe, et plus portable, je trouve. Par contre, je me retrouve quand même avec ce long build maven qui tourne sur ma machine, ce qui ne m’enchante pas du tout. Mais je vais trouver une autre solution.

Le retour du casque sanglant

Ceux qui me connaissent savent que je suis un grand joueur …

Attention : je ne veux pas dire que j’aime jouer de l’argent, ni que je suis un bon joueur.

Plutôt que quand on me propose l’occasion de jouer à … à peu près n’importe quoi, je suis prêt à essayer. Les jeux de carte, de fléchette, les jeux informatiques, les jeux de rôle, les jeux abstraits, je n’en connais qu’assez peu qui me rebutent (à l’exception du mao, qui m’a peut-être été mal présenté). Et dans ces jeux, certains ont pour moi une place particulière. Vous savez sans doute déjà que j’apprécie beaucoup le nomic. Et bien sûr les jeux de programmation. Mais il y a d’autres jeux pour lesquels j’ai une longue affection.

L’un d’entre eux est Blood Bowl. Le jeu de plateau, bien évidement ! Et comme je suis un nostalgique, (et parce que j’ai testé et détesté la troisième édition), je me suis récemment racheté Blood Bowl et Zone Mortelle … Et la lecture des règles m’a replongé dans des souvenirs de parties acharnés, d’halflings lancés par des hommes-arbres. Bref, j’étais en plein trip.

J’en ai donc parlé à mon fils, et nous nous sommes lancé dans une partie acharnée elfes vs elfes noirs qui a duré … trois jours, et qui s’est conclu par l’abandon de mon fils après un arrachage de ballon par mes joueurs suivi d’une blessure de l’un de ses joueurs. Vraiment pas de bol. Mais j’ai quand même été frappé par deux choses. D’abord, les règles sont sacrément complexes, et le fait d’avoir deux manuels de règles complexifie un peu la tâche de la personne chargée de lire les règles (moi, en l’occurrence). Et puis on se retrouve vite à jeter des séries de dés en farfouillant dans les règles, ce qui n’est pas commode. Et le solutionisme technologique m’a frappé. A mon avis, il ne doit pas être trop dur de reprendre les règles de base et les règles additionnelles pour en faire une espèce de site dynamique. Et il doit même être possible de se faire une application pur JS arbitrant la parti. Mais je ne sais pas encore trop comment faire …

Comment ne pas faire pousser une forêt

Cette semaine, c’était le challenge Codingame de printemps.

Il s’agissait cette fois-ci de faire pousser des arbres, en évitant de subir l’ombre de l’adversaire. Et du fait du contexte épidémique actuel, je ne pouvais pas participer comme je l’aurai souhaité : j’ai arrêté de coder mercredi soir, et j’ai choisi très volontairement, de coder en Rust, qui n’est pas le langage le plus simple (franchement l’un des plus complexes que je connaisse, en fait).

Et comme un bonheur n’arrive jamais seul, les règles incluaient certains éléments qui ont clairement augmenté la complexité du problème.

  • D’abord le fait que le terrain soit hexagonal ajoute une complexité certaine : le stockage de données dans un tableau de tableaux n’est à priori pas utilisable (j’écris à priori très volontairement parce que je conservais, depuis Coders of the Carribean, un excellent article de référence sur la modélisation de terrains hexagonaux).
  • Par ailleurs, l’unité de tour de jeu, le jour, n’est en fait pas atomique : les deux joueurs vont effectuer alternativement autant d’opérations qu’ils le peuvent, en continuant même si l’adversaire n’a plus d’actions possibles. Pour moi, ça a ajouté beaucoup de complexité.
  • En plus, chaque joueur a deux échelles à optimiser : le nombre de points de soleil produits chaque jour, et le score final (qui dépend des arbres récoltés, de leur ordre de récolte et de la position de la récolte).

Franchement, quand on ajoute tout ça, ça donne un résultat facilement complexe. Et comme je n’avais aucune intuition géniale, j’ai utilisé un modèle de programmation hyper classique (pour moi) :

  1. Décrire chaque entité du jeu dans un type (on a donc une Action qui est un enum Rust, un struct pour chaque arbre, et enfin un modèle représentant le terrain). Petite subtilité : j’avais commencé par représenter le terrain via l’une des structures indiqué dans l’article mentionné plus haut, avant de me rendre compte que le moteur de jeu fournissait la géométrie du terrain au démarrage, du coup, il était bien plus simple de simplement stocker la géométrie dans un Vec, et le contenu dans un autre Vec.
  2. A partir de là, déterminer toutes les actions possibles
  3. Éliminer les actions dont le coût est supérieur aux points de soleil disponilbe
  4. Et trier les actions selon une heuristique

Avec ça, j’arrive à cette solution qui atteint le niveau argent. Pas trop mal en sachant que j’ai arrêté de coder mercredi …

Bon, là; je me donne des excuses faciles.

En vérité, je savais dès Lundi, en particulier à la lecture de cet article sur les échecs « A step-by-step guide to building a simple chess AI » que ce que je faisais n’avait pas du tout les moyens d’atteindre le haut niveau. Sauf que j’ai été intellectuellement bloqué par le fait que ce que je voyais comme l’unité de durée du tour (la journée) ne l’était pas. Et je pense sincèrement que c’est un blocage psychologique stupide de ma part. Ca veut dire quelque chose sur moi, et quelque chose de pas très chouette (mais que je sais déjà). J’ai bien l’impression d’être incapable de tenter des solutions authentiquement innovantes (pour moi) quand je suis pris par la pression du temps. Pour le dire autrement, le biais d’ancrage est fort. Suffisamment fort pour que, la prochaine fois que je suis tenté de faire un challenge codingame, je m’oblige à utiliser ces fameuses techniques de Monte-Carlo, de min-max et autres, qui impliqueront des solutions pas forcément répétables, mais ça n’est pas trop grave.

Parce qu’il y a quand même des choses qui ont bien marché !

J’ai enfin réussi à comprendre le sens des lifetimes Rust ! Et ça implique que j’ai réussi à écrire du code avec beaucoup moins de clone(). Et ça implique à son tour que ce code, qui certes ne fait pas grand chose, est beaucoup plus efficace que mes précédentes solutions.

En bonus, mon article précédent expliquant comment démarrer une solution Rust m’a vraiment beaucoup aidé : je n’ai eu aucun mal à produire une solution raisonnablement intéressante.

Bref, j’ai encore appris beaucoup sur moi, et c’est ce que je demande à Codingame !

La limite du lifestream est à 8000 articles

Pour ceux qui ne savent pas de quoi je parle, j’ai un projet de lifestream sur lequel je travaille depuis des années (les archives du tag remontent à 2010 au moins). ce projet a connu plusieurs incarnations. La dernière était un ensemble de plugins maven générant des fichiers Asciidoc que je passais ensuite dans un plugin JBake pour générer un site statique. Sur le papier, l’idée était chouette.

Mais le vrai problème d’un projet de développeur, c’est de le faire survivre à la prod. Mon choix pour la prod était de tout faire tourner sur mon Raspberry, parce qu’il tourne, et que je m’attache de plus en plus à limiter mon empreinte carbone numérique. Sauf que le Raspberry 3 a une mémoire limitée, et que mon lifestream comprend quelques fichiers :

  • 2200 fichiers générés depuis Goodreads
  • 8400 fichiers générés depuis Shaarli
  • 1300 fichiers depuis WordPress

Ca fait un bon paquet, et même en essayant d’optimiser les choses, je n’arrive pas à générer un site viable. Qu’est-ce que je peux faire maintenant ?

  • La solution la plus simple serait de changer de machine de génération : faire ça depuis un vrai ordinateur (comme par exemple celui depuis lequel j’écris cet article) avec une belle quantité de RAM me sortirait cette épine du pied
  • Une autre solution serait d’abandonner le site statique pour passer à un site dynamique … mais ça m’embête beaucoup

Parce que l’un des prérequis de ce lifestream est d’être hébergé dans les pages perso de free.fr. Pourquoi ? Parce que c’est une solution performante :

C:\Users\nicolas-delsaux>hey http://nicolas.delsaux.free.fr/lifestream/

Summary:
  Total:        6.2935 secs
  Slowest:      4.1379 secs
  Fastest:      0.3488 secs
  Average:      1.0254 secs
  Requests/sec: 31.7790

  Total data:   6755645 bytes
  Size/request: 33778 bytes

Response time histogram:
  0.349 [1]     |
  0.728 [104]   |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.107 [38]    |■■■■■■■■■■■■■■■
  1.486 [22]    |■■■■■■■■
  1.864 [13]    |■■■■■
  2.243 [6]     |■■
  2.622 [6]     |■■
  3.001 [1]     |
  3.380 [2]     |■
  3.759 [4]     |■■
  4.138 [3]     |■

Vous pouvez faire le test de chez vous, vous aurez normalement d’assez bonnes performances. Malgré la lenteur des machines de free.fr. Et ça, c’est un des plus importants objectifs de ce projet.

Du coup, je pense que la prochaine étape devrait tout simplement être de déplacer la génération vers mon ordinateur de bureau, avec une quelconque solution de tâche planifiée pour Windows … Je vais néanmoins ajouter l’écriture des fichiers dans un repository GitHub, pour faciliter la réutilisation, et travailler sur l’écriture dans un outil comme AsciiBlog. Ou alors tenter de générer le site dans GitHub Pages, ce qui pourrait aussi être chouette (même si ma 1/2 heure de génération JBake ne risque pas d’être compatible avec GitHub Actions). Bref, j’ai encore des idées…

Je suis toujours mauvais à Pacman

Evidement, je ne parle pas de jouer moi-même au pacman, mais plutôt de celui auquel il faut jouer dans le spring challenge 2020. Parce que comme j’ai l’intention de concourir dès demain, en essayant autant que possible de faire du Rust, j’ai essayé de me remettre à niveau en reprenant le problème précédent, et en essayant d’améliorer la solution dont je disposais déja. Et, comme vous pouvez le voir dans cette simulation, ça ne marche pas si bien. Avant d’aller plus loin, on va d’abord voir rapidement ce que fait mon robot.

Donc, à chaque tour

  • D’abord, je découpe le terrain (autrement dit, je construis un pavage du plan) pour disposer pour chaque pac d’un terrain disjoint.
  • Et ensuite, pour chaque pac, je score chaque déplacement en fonction des pillules atteignables au 7 prochains tours.

Il y a un certain nombre de choses qui ne sont pas implémentées, et c’est normal : je ne gère pas les ennemis, ni les changements de couleur, ni même le boost. Bref, c’est pas le niveau argent …

Cela dit, mon robot est bien plus mauvais que simplement parce qu’il n’implémente pas ces fonctionnalités. Ce qui le rend vraiment mauvais, c’est qu’il privilégie l’accessoire à l’essentiel. Parce que dans ce jeu, il y a deux choses essentielles :

  1. Survivre à l’adversaire
  2. Manger les grosses pilules

Et mon code n’essaye de faire aucun des deux … Bon, c’est con. Et, pire que tout, il n’est même pas performant, sans doute parce que le pavage du plan est assez consommateur en temps. Bref, la prochaine fois que je voudrais m’y mettre, je pense que je suis bon pour … supprimer tout le code existant et tout refaire, tout simplement. J’ai donc appris un certain nombre de choses. La plus importante étant que faire du code performant en Rust … c’est pas facile du tout. Lisez donc tranquillement cet article Moves, copies and clones in Rust, et vous comprendrez que l’impact de la sécurité mémoire sur le code écrit est loin d’être négligeable. Et je trouve ce problème assez … stimulant (même si ça me donne parfois l’impression d’avancer avec un boulet au pied).