Aller au contenu principal
Tous les articles
Fonctionnement

La roue sait déjà

Pourquoi le gagnant est tiré au sort avant le début de l'animation, et ce que cela vous apporte.

Appuyez sur Lancer et la roue tourne pendant dix secondes. On a l'impression que le résultat se décide quelque part au cours de ces dix secondes — dans la décélération, dans le dernier demi-tour, ou selon que le pointeur franchit tout juste un séparateur de plus.

Ce n'est pas le cas. Le gagnant est tiré au sort en premier, en quelques microsecondes, puis l'animation est ciblée sur lui.

Pourquoi cet ordre

L'alternative évidente consiste à faire tourner la roue et à lire l'endroit exact où elle s'arrête. Cela semble plus équitable — personne n'a choisi, la physique s'en est chargée — et c'est pourtant le choix le plus difficile à défendre, car cela transforme chaque question sur l'équité en une question sur l'animation.

Peut-on donner une impulsion plus forte pour changer le résultat ? Le fait de cliquer en cours de rotation pour abréger le tour introduit-il un biais ? Que se passe-t-il pour une personne naviguant avec l'option prefers-reduced-motion activée, qui ne voit jamais l'animation ?

En tirant au sort d'abord, ces trois questions reçoivent la même réponse : non, car le tirage a déjà eu lieu. L'impulsion vigoureuse ajoute des tours supplémentaires. Le clic prématuré raccourcit le trajet. L'option d'animation réduite ignore totalement le trajet et annonce le résultat immédiatement. Trois parcours différents à travers un seul et même morceau de code, plutôt que trois branches dont la fiabilité devrait être justifiée séparément.

Ce qui effectue le tirage

L'entropie provient de crypto.getRandomValues — le générateur cryptographique natif du navigateur — et jamais de Math.random.

Obtenir un nombre uniforme nécessite une étape de plus qu'il n'y paraît. La formule évidente draw % count est biaisée dès que count ne divise pas 2³² de manière exacte : les premiers éléments reçoivent chacun une chance supplémentaire sur quatre milliards. C'est minime, mais c'est un poids sur la balance, et une roue qui se veut équitable ne doit pas en comporter. Les tirages qui tombent dans ce reste non uniforme sont donc rejetés, et un nouveau tirage est effectué.

const limit = Math.floor(2 ** 32 / max) * max;

let draw = crypto.getRandomValues(new Uint32Array(1))[0];
while (draw >= limit) {
  draw = crypto.getRandomValues(new Uint32Array(1))[0];
}

return draw % max;

Cela coûte parfois un tirage supplémentaire, mais garantit un résultat parfaitement uniforme. Le même code figure sur la page d'équité, où il constitue le texte explicatif lui-même plutôt qu'une simple illustration.

Les poids sont des probabilités, pas des favoris

Donnez à une entrée un poids de 3 et elle reçoit trois parts du tirage au lieu d'une. La roue l'affiche sous la forme d'un secteur plus large, et la liste indique le pourcentage réel que ce poids représente, afin que personne n'ait à faire de division à la main.

Deux entrées portant le même texte restent deux entrées distinctes. Chacune conserve sa propre chance, et la liste indique le nombre de répétitions trouvées plutôt que de les fusionner discrètement — réduire de moitié les chances de quelqu'un sans le prévenir est le genre de service que personne n'a demandé.

À quoi sert encore l'animation

Une animation ciblée n'est pas une fausse animation. La roue effectue réellement son trajet jusqu'au secteur sur lequel elle s'arrête, les cliquetis sonores sont parfaitement synchronisés avec les séparateurs qu'elle franchit, et l'hésitation du presque-gagné — lorsqu'elle est activée — s'attarde sur un voisin du gagnant plutôt que sur un secteur aléatoire, avant de poursuivre vers l'élément tiré au sort avant même que tout cela ne commence.

Le suspense est réel. Le résultat est simplement antérieur aux apparences.