1

Dernière mise à jour: l’effondrement de la médiane

L’aléatoire est délicat à gérer pour qu’il soit équilibré, tant en terme d’implémentation (déséquilibres dû à la façon de l’avoir codé) que de gameplay (déséquilibres de jeu amenés par cette nouvelle composante).

Les bogues classiques d’implémentation

Le modulo

Quand on veut mettre de l’aléatoire dans un jeu, on a souvent besoin de le borner. Pour cela, la plupart des développeurs courent vers des implémentations trop simplistes, qui s’avèrent alors non-équiprobables, c’est à dire que certains nombres ont plus de chances que d’autres d’être tirés, ce qui peut amener de graves déséquilibres dans le gameplay.

Prenons, par exemple, l’implémentation du modulo. Un développeur veut un nombre entier aléatoire dans [0..1000[, et il va donc utiliser un simple RAND()%1000. Or, ce calcul n’est pas nécessairement équiprobable. En effet, la fonction RAND() (du C++ par exemple) renvoie un nombre aléatoire dans [0 .. INT_MAX[. Imaginons que INT_MAX = 3000, alors le tirage sera équiprobable, car chaque résultat dans [0..1000[ pourra provenir d’exactement 3 nombres aléatoires différents (par exemple, 123 peut provenir de RAND() = 123|1123|2123). Mais, si INT_MAX = 2500, alors certains résultats proviendront d’exactement 3 nombres aléatoires (123|1123|2123) alors que d’autres ne pourront provenir que de deux nombres aléatoires (800 ne peut provenir que des tirages 800|1800, car 2800 est hors de l’intervalle de RAND()). Il y a donc un déséquilibre qui se crée. Néanmoins, Plus INT_MAX sera grand par rapport à la taille de l’intervalle du résultat (ici, 1000), plus l’effet sera atténué, au point d’être parfois négligeable dans votre jeu.

L’arrondis

Une autre problématique de certains aléatoires réside dans l’arrondis. En effet, dans certains langages (comme MySQL), RAND() retourne un nombre décimal dans [0..1[. On pourrait ergoter sur la non-équiprobabilité de chaque nombre décimal, mais l’effet est vraiment négligeable sur un jeu web. En revanche, un autre effet est très souvent important: l’arrondis de ce nombre décimal aléatoire. Bien souvent, pour obtenir un nombre à deux chiffres dans [0,5..0.9], les développeurs qui recourent à un RAND() au résultat décimal effectue un arrondis: 0.50 + ROUND(RAND()*0.4, 1). Néanmoins, cet arrondis défavorisera les valeurs extrêmes de l’intervalle: 0.50 et 0.90 sortiront moins souvent. En effet, pour sortir 0.50, il faut que le nombre décimal aléatoire soit dans [0.50 .. 0.505[, alors que pour sortir 0.60 (qui n’est pas une borne de l’intervalle désiré), il faut que le nombre décimal aléatoire soit dans [0.595 .. 0.605[: l’intervalle est deux fois plus grand! 0.5 aura donc deux fois moins de chances de sortir (et il en va de même pour l’autre borne de l’intervalle, 0.90).

Résultats
Résultat: les bornes sont moins souvent tirées que l’intérieur de l’intervalle

Cet effet est parfois négligeable, mais il faut bien en être conscient car plus le nombre de valeurs autorisées dans l’intervalle désiré sera petit, plus l’effet sera important: l’effet sera négligeable si on veut des décimaux à 4 chiffres dans [0 .. 100], mais il sera important si on veut des nombres décimaux à 1 chiffre dans [0..1].

Conséquences inhérente: l’effet cumulatif de l’aléatoire

Même en implémentant correctement votre aléatoire, il y a des risques, principalement celui d’une « dégénérescence » du jeu. En effet, si un paramètre aléatoire est récurrent à chaque tour de jeu et cumulatif par rapport aux tours précédents, alors vous risquez de voir un effondrement du jeu: sa diversité va dégringoler, et un composant de jeu (ou un joueur) écrasera les autres.

Exemple

Prenons un exemple d’ECLERD. Dans ce jeu, la population se renouvelle régulièrement. Supposons qu’elle soit, au départ, de 100 personnes et qu’à chaque tour de jeu (disons, chaque mois), elle peut varier de -1% à +1% (nombres décimaux inclus). L’effet est cumulatif et on garde la fraction décimale: si la population au mois 0 est de 100 et que le tirage aléatoire donne +0.5%, alors la population au mois 1 sera de 100.5 et si le tirage suivant donne à nouveau +0.5%, alors la population sera de 101.0025 personnes.

Ce qu’on attend

En procédant ainsi, je m’attendais à ce que la population oscille autour de la valeur initiale de 100 habitants (et les actions du joueurs créeront des tendances à la hausse ou à la baisse). En effet, comme le tirage est équiprobable (autant de chances de tirer -1% que +1%) et qu’il est centré sur 0 (-1%..+1% donne une moyenne de 0%), alors j’espérais que la population globale tournerait autour de 100. J’estimais également la probabilité qu’elle double à pow(2,70) = 1e21 car il faut environ 70 fois « +1% » pour atteindre 200.

Les résultats

Différents essais donnant le même résultat: en 1000 mois, on est loin de la valeur initiale de 100!

Le résultat n’est pas du tout ce à quoi je m’attendais (ce qui explique d’ailleurs que le comportement de jeu d’ECLERD soit aussi anarchique): les effets de l’aléatoire ne se compensent pas, mais se cumulent, car faire +1% suivit de -1% ne ramène pas à la valeur initiale (100 +1% → 101 – 1 % → 99.99). En pratique, il arrivera donc un moment où la courbe de population va complètement décrocher de sa valeur de base, et le jeu apparaitra anarchique aux joueurs s’ils ne peuvent pas compenser cet effet cumulatif. Et ce n’est pas un « manque de bol »: les différents courbes sont toutes atteintes de la même « dégénérescence ».

L’effondrement de la médiane

Comme présenté sur JeuWeb, un autre problème intervient: l’effondrement de la médiane. Cela signifie que, au terme de ces 1000 mois, vous aurez plus de pays qui auront une population inférieure à la valeur initiale que de pays ayant une population supérieure! Cela vous semble paradoxal? Pas tant que cela: si vous rajoutez un pourcentage à quelque chose et que vous le retirez ensuite, alors vous aurez une valeur finale inférieure à la valeur initiale. Par exemple, 100 + 5% = 105, 105 – 5% = 99.75 <100. Pour cette raison, la moyenne restera à 100 (car RAND(-5%..5%) est centré sur 0) mais la médiane va dégringoler au fil du temps.

Partant de la valeur 100, la médiane des 5000 simulations tombe à 85 au terme de 1000 tours

Extension

Ce modèle peut s’appliquer à de très nombreux éléments d’un jeu web: la population d’un pays, les ressources d’un joueur, ses points de vie, ceux de ses ennemis, l’équipement ou l’XP du joueur, et même, le plaisir de jouer ou la difficulté du jeu: il suffit de considérer qu’un « mois » dans l’exemple précédent (ou un « tour ») correspond à un choix du joueur, et la base 100 correspond à l’équilibre d’un jeu: l’aléatoire va faire dévier cet équilibre, jusqu’à ce que le jeu soit trop simple ou trop difficile.

Les solutions

Pas d’effet cumulatif

On peut résoudre ce problème en supprimant l’effet cumulatif: l’aléatoire est alors intégré à des évènements totalement indépendants (par exemple, ajouter de l’aléatoire au résultat d’un combat s’il n’influe pas les combats suivants) ou en intégrant une valeur aléatoire absolue (un nombre d’habitants) et non relative (un pourcentage).

Rendre marginal l’aléatoire

L’aléatoire peut également être rendu très marginal par rapport aux actions du joueur, en réduisant le pourcentage précédent à 0.01% par exemple. Néanmoins, cette réduction risque de rendre l’aléatoire purement et simplement inutile, car sans réelle conséquence.

Pas d’aléatoire

Cela revient à pousser la solution précédente à l’extrême, en laissant tomber totalement l’aléatoire.

Effets sur le gameplay

Prédictibilité

Souvent, la raison invoquée pour rajouter l’aléatoire dans un jeu est de « pimenter » le jeu, ou de faire en sorte que le plan établi par le joueur ne soit pas exactement ce que la réalité du jeu lui proposera, forçant le joueur à s’adapter (ce qui est soi-disant « réaliste »). Pour ce faire, un gamedesigner peut être tenté d’ajouter une part d’aléatoire à une composante du jeu, en tentant de ne pas tomber dans les écueils ci-dessus.

Pour ma part, je pense que c’est une mauvaise approche car, à mon sens, l’aléatoire sert uniquement à combler un gameplay trop simple: autant jouer aux dés. La réelle solution consiste alors à étoffer ce gameplay, en rajoutant de nouvelles composantes à prendre en compte, ou de simplement accepter le fait d’avoir un gameplay très simple et prédictif. Les créateurs du jeu peuvent alors miser sur d’autres points, comme « être le premier joueur à … » ou « faire des parties très courtes », ou « considérer que l’imprévisibilité du jeu viendra des autres joueurs », etc.

1