Projet personnel
ManGuessr
Un jeu de devinettes sur l’univers anime et manga, avec un défi par jour, le même pour tout le monde. En ligne, et servi depuis mon Raspberry Pi.
- Période
- 2026
- Rôle
- Seul, de la conception à l’exploitation
- Technologies

Le jeu
Inspiré d’AniGuessr. Chaque jour, un défi par mode, identique pour tous les joueurs, plus un mode illimité. On reconnaît une œuvre à partir d’images de plus en plus parlantes, on retrouve la série de quatre personnages, on devine un anime à son générique, on déduit un titre par comparaison d’attributs façon Wordle, ou on identifie une jaquette de manga de moins en moins floue.
Autour du jeu : comptes, séries de victoires, classement, statistiques de profil et partage du score.
Un moteur de parties qui accepte de nouveaux modes
Chaque mode implémente la même interface : quelles œuvres il peut tirer, ce que le client a le droit de voir, comment valider une réponse. Le cycle de vie d’une partie et le calcul du score restent au même endroit, et le score est toujours recalculé côté serveur. Ajouter un mode ne touche pas au reste.
Le catalogue vient de trois API publiques, AniList en GraphQL, MangaDex et AnimeThemes, ingérées avec une limite de débit propre à chaque source et un regroupement des saisons d’une même série.
Empêcher de tricher avec les outils du navigateur
Dans un jeu de devinettes, l’URL d’une image ou le nom d’un fichier audio peut donner la réponse. Tous les médias passent donc par un proxy qui délivre des jetons signés : rien dans ce que reçoit le navigateur ne trahit l’œuvre.
Les images sont floutées ou recadrées côté serveur, puis mises en cache sur disque. Le navigateur ne reçoit jamais la version nette avant la fin de la partie.
Tenir sur un Raspberry Pi
Flouter une image coûte cher sur un processeur ARM. J’utilise un flou séparable appliqué en plusieurs passes, qui reste peu coûteux même sur le Pi.
Les images d’une partie sont préparées en arrière-plan dès sa création, et celles du défi du jour dès qu’il est tiré. La première image passe d’environ 0,9 seconde à quelques millisecondes. La liste des modes disponibles est mise en cache : la page d’accueil est passée de 1,6 seconde à 6 millisecondes.
Ouvert sur Internet
Le site passe par un tunnel Cloudflare : il n’a besoin d’aucun port ouvert sur ma box. nginx limite le débit par visiteur (connexion, création de parties, reste de l’API) et pose les en-têtes de sécurité : CSP, HSTS, interdiction d’affichage dans un cadre. Le backend tourne sans les droits root.
Un piège trouvé pendant l’audit : derrière le tunnel, nginx voit toutes les requêtes venir de la même adresse, celle de cloudflared. Une limite de débit par adresse IP aurait bloqué tout le monde d’un coup. Elle se base donc sur l’en-tête CF-Connecting-IP, que Cloudflare écrase et qu’un client ne peut pas falsifier.
Un autre, plus bête : la redirection vers HTTPS reposait sur le schéma de la requête, toujours « http » côté Pi. Résultat, l’accès depuis le réseau local était redirigé et cassé. Elle se base maintenant sur l’en-tête CF-Visitor, absent en accès local.
Le projet compte 263 tests JUnit 5, unitaires et d’intégration, exécutés par GitHub Actions à chaque push.