Comment les jeux inactifs gèrent-ils de si grands nombres?

31

Je me demande simplement comment des jeux tels que Tap titans et Cookie Clicker gèrent de si grands nombres.

J'essaie d'implémenter un jeu inactif, mais le plus grand format numérique pris en charge par C # est décimal.

Je cherche à prendre en charge jusqu'à 10 ^ 500; qui doit être en virgule flottante

Comment pourrais-je gérer cela?

PS, il doit être multiplateforme, c'est-à-dire pc, mac, ios, android et compatible avec Unity

MathHelp
la source
2
Si je me souviens bien, vous avez accès au code de Cookie Clicker, vous pouvez donc simplement vérifier que ...
Vaillancourt
Non, j'avais déjà bloqué Cookie Clicker dans mon esprit!
Arturo Torres Sánchez du
2
J'ai regardé la source décompilée de TimeClickers , un jeu Unity. Ils utilisent juste double. Si ça avait été moi, j'aurais utilisé BigInteger .
BlueRaja - Danny Pflughoeft
1
@VioletGiraffe: Je suis en quelque sorte d'accord, et je voudrais concrétiser mes (nos?) Doutes (pour aider éventuellement le PO à clarifier la question): Qu'est-ce qu'un "jeu inactif" par rapport à la question (qui rend ce genre de jeu un cas particulier pour les grands nombres)? À quels types de nombres «de si grands nombres» (surlignés par moi-même) font-ils référence? Quelle taille voulez-vous que les nombres soient?
OR Mapper du
vous pouvez toujours stocker un si grand nombre dans une chaîne et implémenter facilement une fonction pour effectuer la sommation sur la chaîne.
dev-masih

Réponses:

40

Si vous voulez simplement stocker des nombres massifs sans précision complète, par exemple si vous allez afficher 12 567 000 000 000 comme 12,567 T (trillions), vous pouvez simplement utiliser des valeurs flottantes / décimales standard et afficher les premiers x chiffres significatifs, avec un suffixe approprié comme ça. Lorsque vous êtes dans les octillions, avez-vous vraiment besoin de vous soucier de chaque incrément entier individuel?

Ross Taylor-Turner
la source
4
C'est probablement la réponse la plus sensée, et je suis presque sûr que Cookie Clicker utilise simplement un flotteur standard en JavaScript - cas où je l'ai "piraté" (lire: foiré avec la console) et lorsque le nombre a atteint 1e308, il a sauté à "Infinity" et je pourrais acheter tout ce que je voulais XD
Niet the Dark Absol
1
Génial! Le fait que le texte passe à "Infinity" donne l'impression que le développeur codait également sur cette éventualité. De plus, la simplicité est reine.
Ross Taylor-Turner du
19
@ RossTurner - Il n'y avait probablement pas de codage impliqué pour lui faire afficher le texte "Infinity" - l'infini est juste une valeur qu'un flotteur peut avoir, et JavaScript sait comment l'afficher, comme le font de nombreuses autres langues.
Jibb Smart
Les flottants standard (double précision, 8 octets) ne permettront pas de représenter des nombres jusqu'à 10 ^ 500 comme l'exige la question d'origine, ils iront jusqu'à ~ 10 ^ 300. Si cela ne suffit pas, il faut utiliser des flotteurs de précision quad qui peuvent être non triviaux selon la langue.
Peteris
1
Ce que tout programmeur doit savoir sur les nombres à virgule flottante
Steve
20

Vous pouvez utiliser quelque chose comme BigInteger , qui n'est disponible dans .net 4.0 que si je ne me trompe pas (non pris en charge par toutes les plates-formes de construction d'unité).

Certaines bibliothèques tentent cependant d'apporter cette fonctionnalité sans l'exigence de .net4.0. Par exemple ici .

Alternativement, vous pouvez utiliser des nombres plus petits pour représenter un plus grand, en gardant une trace du multiplicateur. Par exemple:

double value = 9;
enum Multiplier {
   Hundred,
   Thousand,
   Million,
   Billion,
   Trillion
}

Maintenant, même si vous avez une valeur de 9, si vous l'associez à votre multiplicateur, vous pouvez effectivement représenter ce 9 comme 9 trillions (soit en mettant "trillion" ou en écrivant quelque chose qui ajoutera les zéros à la fin de votre valeur ).

jgallant
la source
Il convient de noter que des classes telles que l' BigIntegerutilisation d'une représentation String ou Byte [] du nombre - en fait, on pourrait créer leur propre classe qui ajoute et soustrait simplement deux "nombres sous forme de chaînes ou d'octets" - ( généralement ce type de jeu ne fait pas pas besoin, mais vous pouvez également prendre en charge la multiplication ou la division ou d'autres fonctions plus avancées ) - mais ils doivent garder à l'esprit qu'il existe toujours une limite physique où ils peuvent manquer de mémoire.
DoubleDouble
1
Chaque octet multiplie la taille de votre nombre par 256. Si vous manquez de mémoire en essayant de représenter un nombre, votre numéro n'avait de toute façon aucune valeur réelle. 256 ^ (2 milliards) = 2 ^ 8 ^ (2 milliards) = 2 ^ (16 milliards) ~ = 10 ^ (5 milliards) (wolframalpha s'est cassé en essayant de faire cette dernière conversion). Vous pouvez spécifier des volumes Planck individuels dans l'univers observable en utilisant comme 150 chiffres (<500 bits).
MichaelS
11

Vous auriez probablement besoin d'écrire votre propre classe pour l'utiliser dans Unity, mais ce ne serait pas particulièrement difficile.

En interne, il peut s'agir d'une liste d'entiers (comme a List<int>), chaque élément de la liste correspondant à un groupe de 9 chiffres. Chaque entier aurait une plage de 0 à 999 999 999. Un entier peut prendre en charge un peu plus de 2 milliards, et le doubler s'il n'est pas signé, mais vous voudrez que chaque "chiffre" déborde 1 000 000 000, car vous voudrez également pour pouvoir facilement convertir votre grand nombre en une chaîne à afficher. Il est plus facile de concaténer des chiffres déjà décimaux ( return group[i].ToString() + group[i-1].ToString() and so on) que de déterminer comment afficher la somme des groupes qui se trouvent en dehors de la plage de tout type de données standard.

Ainsi, le premier entier de votre liste représenterait 1s. Le prochain serait le nombre de milliards. Le suivant, le nombre de quadrillions, et ainsi de suite.

L'addition et la soustraction fonctionneraient exactement comme l'addition et la soustraction au stylo et papier, où vous devez surveiller le débordement et le reporter au "chiffre" suivant, mais au lieu de chiffres avec une plage de 0 à 9, vos chiffres vont de 0 à 999 999 999.

Jibb Smart
la source
10

Pour gérer de grands nombres, je regarderais ce que je pense être un bon exemple comme Tower of Hero . Coin supérieur gauche:

1

2
(source: mzstatic.com )

Sans entrer dans le gameplay, la façon dont il gère les nombres est relativement simple: vous voyez deux groupes de nombres. Lorsque vous montez plus haut dans la tour et que vous faites plus "d'or", les deux seaux représentent simplement de plus grands nombres.

120 
120M320K - 120 Million
120B631M - 120 Billion
120T134B - 120 Trillion

Une fois que le jeu passe T, il se déplace en a, b, c ... z, aa, ab, ...

56aa608z

En procédant de cette façon, cela vous permet toujours de savoir combien d'or vous avez "gagné" ... tout en n'embourbant pas le jeu dans les détails.

Vous souciez-vous vraiment des millions quand votre nombre est passé des milliards?

Conserve-t-il le nombre en Int, Big Int, Float, Double, Decimal, ...? Tableau personnalisé? Lorsque vous gérez des nombres de manière si "floue", je ne pense pas que cela soit important ...

Tout ce qui compte probablement, ce sont les parties les plus importantes - dans ce cas, les 6 premiers ... Après cela, PEUT-ÊTRE les 3 ou 6 suivants - puisque gagner quelques centaines de K peut se transformer en millions - mais il y a un point où gagner quelques centaines de K ne vous affecteront pas lorsque vous frapperez T ... encore moins aa et au-delà.

Votre kilométrage variera (en fonction de ce que vous voulez / besoin) ... Je pensais simplement que je mettrais mon 2c sur ce que je pense être un bon / simple exemple.

Modifier:

Réflexions supplémentaires sur la façon dont j'implémenterais le système de numérotation: j'aurais un nombre avec 3 parties significatives: XXXX.YYY (...) xZZZ.

X is the most significant digits, 
Y trailing 
Z the multiplier (multiple of 3).

Donc 120.365x1 serait 120k365 ... 120.365x2 serait 120M365K ... etc. Appuyez sur le 4 en tête (1200.365x2), puis faites simplement pivoter les nombres 1.200365 (...) x3. Bam. Vous avez 1B200M.

XY s'intégrerait facilement dans une décimale ou un flottant ... avec Z assis à côté comme un int / non signé.

Avec un flotteur, vous seriez en mesure de conserver un nombre important - mais de plus en plus insignifiant - de chiffres après le point.

Z représenterait facilement un bloc de nombres facilement compréhensible:

K = 1
M = 2
B = 3
T = 4
a = 5 
...
z = 31 (I may be off on this)
aa = 32
...
az = 58
ba = 59
...
...
WernerCD
la source
1

Et un moyen facile de gérer de grands nombres est simplement d'avoir plus d'une valeur INTEGER puis de TRANSPORTER tout débordement. Si vous avez une valeur INT 16 bits (0 à 65535) et que vous souhaitez en avoir plus, utilisez deux valeurs INT 16 bits consécutives. Pensez-y comme ayant une valeur BYTE (0 à 255) mais en l'utilisant uniquement jusqu'à 99 chiffres. Une fois qu'il atteint 100, passez-le à la prochaine valeur d'octet supérieure pour autant de chiffres que vous trouvez utiles. Avec les ordinateurs GHZ d'aujourd'hui, même un codage bâclé comme ça est bien.

Bien sûr, il existe une alternative un peu plus rapide.
Si vous ajoutez 18 à plusieurs reprises à un nombre, il est plus rapide de simplement soustraire 2 et d'ajouter 20. 18, 36, 54, 72, 90, 108, ... 18 = 20 + (- 2).
Cela fonctionne aussi en binaire. (Mêmes valeurs décimales en binaire) 10010, 100100, 110110, 1001000
DEC (18) = BIN (10010)
À l'exception d'une analyse binaire plus facile, vous devez penser à 18 = 16 + 2
DEC (16 + 2) = BIN (10000 + 00010). Si la valeur était 15, alors considérez-la comme l'ajout de 16 en binaire et la soustraction de 1 (10000-00001).

De cette façon, vous pouvez limiter le nombre de morceaux à des limites gérables par valeur.
Si vous utilisez la méthode de codage bâclée pour limiter une valeur INT 16 bits de base (0 à 65535) à une limite décimale à 4 chiffres (0 à 9999), alors tout ce que vous avez à faire est lorsque la valeur dépasse la limite 9999, soustrayez-en 9999 et portez-le au bloc de valeur suivant (puisque vous faites essentiellement des «ajouts et sous-marins» avec des nombres par rapport à un vrai calcul binaire).

Idéalement, vous utiliseriez simplement SYMBOLIC MATH que vous avez appris à l'école primaire. Comment cela marche-t-il. Si vous avez le symbole décimal de 1 et que vous l'ajoutez à 1, vous obtenez le symbole décimal de 2. La raison pour laquelle j'appelle cela un symbole est que vous pouvez utiliser n'importe quelle série de symboles avec une table de correspondance de "SI symbole X (1) est ajouté au symbole X (4) ALORS symbole de sortie X (5) ". Le symbole X (1) pourrait être une image d'un chat et le symbole X (4) pourrait être le signe PERCENT, mais cela n'a pas d'importance. Vous avez vos symboles, un livre de règles de base de ce qui se passe, puis ces symboles sont combinés (un peu comme les tables de multiplication que vous avez mémorisées en tant qu'enfant) et quel symbole doit résulter dans le cadre de cette opération. En utilisant Symbolic Math, vous pouvez ajouter un nombre infini de chiffres sans jamais vraiment appeler les limites numériques de votre processeur.

Une façon de le faire dans un codage facile est d'avoir chaque chiffre représenté avec lequel vous souhaitez travailler comme une seule unité dans un tableau de grande dimension. Si vous souhaitez représenter 4096 chiffres décimaux (ne dépassant pas 2 chiffres par boîte d'unité), vous allouez 2 ensembles d'emplacements de tableau 4096 BYTE. Le stockage de la valeur de 1098874 utiliserait la matrice comme (1) (0) (9) (8) (8) (7) (4). Si vous y ajoutez la valeur de 7756, vous la convertissez en (7) (7) (5) (6) puis ajoutez. Le résultat serait (1) (0) (9) (15) (15) (12) (10) que vous devez ensuite soustraire de droite à gauche jusqu'à ce que toutes les cases de chiffres soient normalisées pour être la valeur de (0 à 9). La valeur la plus à droite (10) aurait 10 soustrait et la valeur résultante serait zéro (0) et cela porterait la valeur (1) à la case de gauche suivante de (12) pour en faire (13) qui aurait alors 10 soustrait pour en faire (3).

Gridlock
la source
7
-1 pour TOO MUCH YELLING
Slipp D. Thompson
5
Hé @Gridlock, je viens de remarquer que vous êtes un nouvel utilisateur - bienvenue. Je dois préciser que mon downvote n'est pas permanent; il s'agit d'une critique constructive. La réponse que vous avez fournie a de la valeur et je serai plus qu'heureux de la changer en vote positif une fois que vous aurez apporté la correction que je demande. Gardez également à l'esprit que SE n'est pas un forum; une bonne réponse n'est pas lue, puis oubliée, mais verse plutôt des dividendes aux représentants au fil du temps. La révision de vos réponses ici et là aide la communauté et votre représentant. J'espère que je ne vous ai pas effrayé; encore une fois, bienvenue et j'espère que vous utiliserez mes conseils.
Slipp D. Thompson
1
La seule autre suggestion que je proposerai est d'utiliser un formatage Markdown de base au lieu de majuscules. Texte d'emballage dans les apostrophes inverses ( `text`text) , il sera mono-espacés, appropriés pour des morceaux de code, les noms de fonction, les données, etc. Texte d'emballage en underscores ou astérisques il sera en italique ( _text_ou *text*texte ), et double-underscores ou double les astérisques le mettront en gras ( __text__ou **text**texte ).
Slipp D. Thompson du
0

Ce que vous faites est de combiner plusieurs variables, puis vous contrôlez vous-même l'ajout et le débordement entre elles. Vous pouvez avoir n'importe quel nombre de chiffres de cette façon. Combinez 10 000 entiers 32 bits, et vous avez un nombre avec 32 000 bits, si c'est ce dont vous avez besoin. Il n'y a aucune limite dans aucun langage de programmation. La seule limite est ce que vous pouvez comprendre.

Franc
la source
Pour ceux qui ont donné des votes négatifs, pourriez-vous expliquer pourquoi? Même si ce n'est pas une solution populaire, ou qui ne nécessite aucun effort, c'est toujours une solution, et qui pourrait vous permettre de faire de même, même sur les langues / plates-formes qui ne prennent pas en charge doubleou BigInteger.
TomTsagk