Some time ago, I had the opportunity to help out with developing an idle game. What is an idle game, you ask? Try playing Adventure Capitalist if this is the first time you've heard about this fast-growing genre, and there are some articles here on Gamasutra if you'd like to dive deeper.
One of the problems that any idle game developer would inevitably run into is how to make all those very large numbers fit in the screen, and more importantly how to make all those digits make sense to the player. In this article, we will look into how we can rewrite very large numbers as strings that idle game players are accustomed to reading. We can break this process down into two main parts:
First, we represent the number in scientific notation. Some idle games developers stop here, and some idle game players seem to prefer this. Wolfram MathWorld gives a very straightforward algorithm for this (and this is what we will be using), but there is another approach that you can take if you prefer bit twiddling. Having said that, we won't be talking much about this in this article. If you want to go with this for your game, have a look at the project folder download for this article, which includes a scientific notation script with simple usage:
ScientificNotation.FromDouble(12340000); // .significand == 1.234; .exponent == 7
After we find the scientific notation representation of the number, then we can find the name for its exponent. This is the main focus of this article.
Download the source code
The source code is available for download, ready to use with your own idle games. Usage is as simple as:
LargeNumber.ToString (12340000); // results in "12.34 million"
Also included in the download is a small example program that demonstrates how large numbers are converted to strings in-game (using the button scripts described in a previous article):
All this part of a grab bag of sample code that is intended to accompany some other articles I've written before, mostly from Unity networking tutorials. Be sure to download the sample code if you'd like to see for yourself how it all works!
Standard Numeric Format Strings
Numbers less than a million are small enough to not require much processing. We can rely on C#'s built-in number formatting capabilities to do most of the work for us:
double x = 987654.321 x.ToString ("N2"); // returns "987,654.32"
By adding the appropriate argument to the ToString method, we get group separators and limited decimal places without too much work. No need to mess around with additional divisions and floor functions.
Latin Prefixes For Small Large Numbers
When working with larger numbers in the millions, billions, trillions, etc., we know that the naming changes every three digits added. Given a number in scientific notation, we know that it's in the millions if its exponent is in the range 6 to 8, in the billions if its exponent is 9 to 11, in the trillions if its exponent is 12 to 14, etc. Generalizing this, given a number with scientific notation:
M x 10^N
We need to express N as follows:
N = 3(U+1) + V where V < 3
Then our resulting number string would be:
where Name(U) can be obtained from this table of Latin prefixes:
For example, if we're dealing with the number 123,000,000,000,000,000, we can express it in scientific notation as:
1.23 x 10^17, so M = 1.23 and N = 17
Rewriting N, we have:
17 = 3*(4+1) + 2, so U = 4 and V = 2
This means that our number is:
Simplifying, we have:
Combinations of Prefixes for Large Large Numbers
There is a helpful Wikipedia article that explains how we can name much larger numbers. I won't directly repeat what is said there, but instead give a description of how one might think about the algorithm and give a few examples.
Once we find a number's N value just like how we did it above for Latin-prefixed numbers, we need to break it down into its digits (ones, tens, hundreds), then formulate the name as follows:
(ones prefix) + (modifier) + (tens prefix) + (hundreds prefix) + (remove trailing "a") + "llion"
The ones, tens, and hundreds prefixes are given by the table in the Wikipedia article. In some cases, we will need to add a modifier if we put certain tens or hundreds prefixes beside particular ones prefixes, but in most cases we won't need a modifier. Let's try a few examples.
If N = 12, we have 2 as the ones digit, corresponding to "duo," and 1 as the tens digit, corresponding to "deci." No modifier is needed in the transition from "duo" to "deci." Following the formula above, we get "duodecillion."
If N = 27, we have 7 as the ones digit, corresponding to "septe," and 2 as the tens digit, corresponding to "viginti." According to the table, the transition from "septe" to "viginti" requires the modifier "m." Putting everything together, we get "septemvigintillion."
Finally, if N = 30, we have 3 as the tens digit, corresponding to "triginta," which has a trailing "a." So we remove it and we get "trigintillion."
If you're trying to figure out how this all plays out in code, it will be a combination of array lookups, a bunch of if statements, and some string concatenation or substitution. If you're lucky enough to work in a language that supports it, you can use pattern matching instead.
The source code download includes a working implementation that covers all cases up to N = 999 and has been tested with all the names listed in the large numbers table. I encourage you to download it if you're looking for something that you can easily drop into your own projects, ready to use, or even to cross-check and compare with your own implementation if you want to code it yourself.
What if N = 1000?
Large Numbers Are Not Large Enough
Double-precision floating-point allows us to work with numbers up to about 10^308, or N=101. If you need larger numbers for your game, you may need to store your numbers using arbitrary precision. That way, you can let your players become millinillionaires in your game.
But before you do that, ask yourself if this is something that you really want to do. Infinity gives us plenty of room to design our game, and we don't necessarily have to attempt to fill all the space it gives us. To paraphrase a college of professor of mine: "Think of the largest finite number that you can think of. Now use that same number as its own exponent, and raise that number by itself that many number of times. If you plot that number on the real number line, compared to infinity, that number will still be very, very, very close to zero."
Would going beyond the capabilities of double-precision floating-point make your game a lot more fun to play? For some games that answer could be yes, but in most cases, you would probably want to rethink your game's design and balance.
If you liked this article, don't forget to download the project folder! Hopefully you will find it helpful as you develop your own idle game. Thank you for reading!