Le jeu n'écrit jamais un casse-tête à la main. Il en fait pousser un à partir d'un seul nombre, puis il joue ce casse-tête contre lui-même pour s'assurer qu'il n'est ni trivial ni impossible avant même que vous le voyiez.
Rédigé et édité en anglais. Cette version française a été produite par traduction automatique ; en cas de doute sur un point précis, l'original anglais fait foi. Lire l'original en anglais →
Un niveau de Contraption n'est stocké nulle part. Aucun fichier n'indique où se trouve le but ni comment les rebords sont disposés. À la place, le niveau tout entier — le point de départ de la balle, la position du but, le nombre et l'emplacement des murs fixes, l'inventaire des pièces qu'on vous remet — est calculé à partir d'un seul nombre de départ appelé graine. Donnez la même graine au générateur et vous obtenez, octet pour octet, le même casse-tête. 1
C'est possible parce que la seule source de « hasard » du générateur est un petit générateur de nombres pseudo-aléatoires déterministe. Celui qu'utilise Contraption est mulberry32, un générateur 32 bits compact écrit par Tommy Ettinger ; on le reconnaît à sa constante caractéristique, 0x6d2b79f5, ajoutée à l'état à chaque tirage. 2 Il est rapide, il tient en quelques lignes et, surtout, il n'a rien d'aléatoire : c'est une suite mathématique fixe qui a simplement l'air mélangée. Donnez-lui un point de départ et il produit à jamais le même flux de nombres. Le générateur lit ensuite ce flux pour décider où placer chaque chose.
Le mode Daily s'appuie sur ce déterminisme pour faire quelque chose de social. Sa graine n'est pas aléatoire, elle est fabriquée à partir du calendrier. Le jeu prend la date du jour en temps universel, l'écrit dans une courte chaîne comme « contraption:2026-7-28 », puis passe ce texte dans une fonction de hachage pour le réduire à un seul nombre de 32 bits. 1
Le hachage est FNV-1a, un mélangeur non cryptographique bien connu : il part d'un décalage fixe, le décimal 2166136261, soit 0x811c9dc5, et, pour chaque caractère, incorpore la lettre par un ou exclusif avant de multiplier par le nombre premier FNV 16777619. 3 Il en sort un nombre stable pour un jour donné et radicalement différent d'un jour à l'autre : changer un seul caractère de la date disperse complètement la graine. Puisque le calendrier est le même pour tout le monde, la graine l'est aussi, et le casse-tête également. Deux inconnus aux antipodes l'un de l'autre ouvrent Daily et affrontent exactement le même terrain, ce qui est précisément ce qui donne un sens à la comparaison des scores.
Le défi quotidien est équitable non pas parce qu'un serveur envoie le même niveau à tout le monde, mais parce que la copie de chacun recalcule indépendamment le même niveau à partir de la même date.
Une géométrie aléatoire, c'est facile ; une bonne géométrie aléatoire, non. Un générateur qui se contente d'éparpiller un but et quelques rebords produira parfois un niveau où la balle, lâchée depuis le haut, tombe tout simplement droit dans le but, toute seule. Ce n'est pas un casse-tête, c'est une cinématique. Aussi, avant d'accepter un monde candidat, Contraption effectue un test discret que le code appelle preSolved : il simule le niveau sans aucune pièce posée et vérifie si la balle nue atteint le but par la seule gravité. Si c'est le cas, le niveau est jeté et le générateur recommence. 1
C'est une inversion subtile de la façon dont on se représente d'habitude un moteur physique. La même fonction simulate() qui fait tourner votre machine est retournée vers l'intérieur, au moment de la génération, pour auditer la difficulté du niveau lui-même. Le moteur est à la fois l'arbitre de votre solution et le contrôleur qualité du casse-tête.
Rejeter le niveau trop facile n'est que la moitié du travail. L'autre danger, c'est le niveau impossible, un but niché à un endroit qu'aucun agencement de pièces ne peut atteindre. Contraption s'en prémunit avec un petit solveur par force brute nommé plausiblySolvable. Il ne cherche pas à être malin. Il essaie, c'est tout. 1
Concrètement, il balaie le terrain avec une seule rampe le long d'une grille de positions et, à chaque emplacement, l'essaie inclinée de quatre façons différentes. Pour chacun de ces placements d'essai, il lance la simulation complète et pose la question : cette unique rampe a-t-elle conduit la balle à destination ? Dès qu'un placement réussit, le niveau est déclaré soluble et vous est livré. Si aucun ne fonctionne au terme du balayage complet, le générateur préfère écarter le niveau et essayer une nouvelle graine.
| Paramètre de balayage | Plage | Signification |
|---|---|---|
| Rampe X | 15 → 85, pas de 14 | colonnes en travers du terrain |
| Rampe Y | 30 → 120, pas de 18 | lignes de haut en bas du terrain |
| Angles essayés | 4 | ±0.45 et ±0.8 radians |
| Test par placement | 1 simulation complète | la balle atteint-elle le but ? |
Il vaut la peine d'être honnête sur ce que cela prouve, car le code l'est aussi : le commentaire parle d'une « vérification de plausibilité peu coûteuse… non exhaustive ». Un niveau qui passe possède assurément au moins une solution grossière à une seule rampe. Un niveau qui échoue peut malgré tout être soluble avec un ventilateur, un engrenage ou une rampe plus astucieuse que la grille n'a jamais essayée ; le générateur ne traite donc pas l'échec comme une preuve d'impossibilité. Il conserve le premier monde non trivial qu'il a trouvé comme solution de repli, et si quarante tentatives passent sans solution propre à une seule pièce, il livre ce repli plutôt que de boucler indéfiniment. Le résultat est une garantie pragmatique : jamais de victoire gratuite, presque toujours un vrai chemin vers le but, et jamais une attente infinie du niveau « parfait ». 1
Prenez du recul et le fabricant de niveaux de Contraption apparaît comme un exemple limpide d'un schéma de résolution de problèmes très ancien et très général : générer et tester. Proposez un candidat au hasard ; confrontez-le à vos exigences ; gardez-le s'il passe, jetez-le et proposez-en un autre s'il échoue. C'est la boucle derrière le contenu procédural d'innombrables jeux, derrière une grande part du calcul évolutionnaire et, pour peu qu'on plisse les yeux, derrière la sélection naturelle elle-même : la mutation propose, l'environnement teste, les survivants persistent. 4
Ce qui rend la version de Contraption élégante, c'est que le test n'est pas une approximation bon marché de la jouabilité : il est la jouabilité. Le générateur n'estime pas l'équité d'un niveau en comptant les rebords ou en mesurant des distances. Il joue littéralement le niveau, deux fois : une fois à vide pour s'assurer que ce n'est pas un cadeau, et une fois avec une rampe exploratoire pour s'assurer qu'un chemin existe. La même physique déterministe qui jugera votre solution est celle qui a validé le casse-tête. Il n'existe pas de « validateur de niveau » distinct qui pourrait se désynchroniser des vraies règles, car il n'y a qu'un seul jeu de règles, utilisé pour tout.
Et parce que tout est déterministe, l'audit est exempt de chance dans les deux sens. La graine du jour ne peut pas comploter pour offrir à un joueur un terrain plus facile qu'à un autre, et elle ne peut en offrir un injuste à personne, car avant d'être un défi, le niveau a déjà survécu à une partie jouée par le jeu contre lui-même.