- Title: Game Maker randomness unveiled
- Description: Exactly how Game Maker's randomness works
- GM Version: GM8.0
- Registered: unnecessary
- File Type: -
- File Size: -
- File Link: -
- Required Extensions: -
- Required DLLs: -
This tutorial will briefly introduce readers to Game Maker randomness, and the basic provided functions. It then goes into depth to explain how Game Maker's random system works and how it can be implemented and used flexibly. Two scripts are also provided, which this tutorial thoroughly explains, namely random_step_back, and random_step_forward. They serve as demonstrations of how to take advantage of Game Maker's randomness once it is fully understood.
It is strongly suggested that readers have at least a basic understanding of binary and bitwise operators, 32-bit integers, and hexadecimal notation, as some information could be rather confusing otherwise.
Here's a good tutorial to help explain binary and bitwise operators: http://gmc.yoyogames...howtopic=473795
INTRODUCTION: Game Maker's random functions
Game Maker has always provided a system for generating random numbers. According to the help manual, these functions are as follows:
random(x) Returns a random real number between 0 and x. The number is always smaller than x.
random_range(x1,x2) Returns a random real number between x1 (inclusive) and x2 (exclusive).
irandom(x) Returns a random integer number between 0 and x (inclusive when x is an integer).
irandom_range(x1,x2) Returns a random real number between x1 (inclusive) and x2 (inclusive). Both x1 and x2 must be integer values (otherwise they are rounded down).
random_set_seed(seed) Sets the seed (an integer) that is used for the random number generation. Can be used to repeat the same random sequence. (Note though that also some actions and the system itself uses random numbers.)
random_get_seed() Returns the current seed.
randomize() Sets the seed to a random number.
choose(val1,val2,val3,...) Returns one of the arguments choosen randomly. The function can have up to 16 arguments.
Each time one of these functions is called for any particular input(s), the outcome is unpredictable. So, for instance, the first call of random(64) will be one number (with some long fractional portion as well), and the next call will be another, totally unpredictable number.
However, to be technically honest, a computer cannot generate truly random numbers. Every number generated has to be based off something. So, then, how are these numbers generated so ... randomly?
MY JOURNEY: Cracking Game Maker's random number generator.
When I was younger, and when I was using Game Maker 6.1, I made the assumption that the randomness was generated by throwing completely random things together (i.e. the previous value used in the code, user inputs at certain points, etc.) and mixing them all up with various mathematical operators. Would this work? Perhaps, but it is far off from how randomness actually works.
When Game Maker 8.0 arrived, it came with two new functions: random_set_seed(seed), and random_get_seed(). This addition made me have to abandon my whole old conception of the methods behind randomness.
After a while of inspecting more closely the random number system Game Maker provides (and especially making use of the random_set_seed(seed) and random_get_seed() functions), I was finally able to crack the system and gain full control over the functionality of it, beyond what Game Maker provides.
LOOKING DEEPER: The basic functionality of the random number generator.
- How does the random number generator initialize?
- So, then, now that it has a seed, what does it do with it?
That's the general way Game Maker handles random numbers.
RANDOM SEEDS: The internal seed, and scaling seeds to values.
- So that's the process it goes through in general, but how does it actually work in full depth?
- Before I continue, I must provide a basic explanation of signed and unsigned integers (if you already have an understanding of this, you can briefly skim over this section)
Note, (232-1) = 4294967295
-498103 (...11111111111111111111111110000110011001001001) & 4294967295 (...00000000000011111111111111111111111111111111) = 4294469193 (...00000000000011111111111110000110011001001001) [1s chopped]
- So, now, where does the random number come in? Where does this 76.98 come from?
RANDOMNESS UNVEILED: The key to randomness is 134775813.
- So, by now, everyone must be thinking, that still doesn't explain anything about the randomness! Finally, now that the ground-work and the basic internal designs have been explained, I shall officially explain the randomness.
So, first why does this seem to make the numbers random?
The simple fact is, since multiplication is not linear, it provides a seemingly random jump-around while not being too calculation-intensive.
- Then, what's the randomly thrown in +1 for?
- And why the magical 134775813?
Now, the first thing you probably want to do is predict the next seed, and see if it came out right. Basically, you could run in debug mode and check the expression, (random_get_seed()*134775813+1)&$FFFFFFFF to get the next seed (note that I didn't first convert the seed to an unsigned integer, but it actually makes no difference because the binary data is the same; the only important part is that I chopped off the extra data after completing the calculation). Then you could execute the code, show_message(random($100000000)) to display the next seed ($100000000 is hex for 4294967296 whereas $FFFFFFFF is hex for 4294967295).
- Did you get the same number? No?
So, is implementing Game Maker's randomness impossible then?
Not at all. Although it can't be done as simply as x*134775813+1, it is still possible to implement. In fact I wrote a function to implement it:
random_step_forward(seed):
// takes a step forward in the randomness; returns the seed following the argument0 seed
var seed_in, key, seed_out, bit, bit_cur;
// prepare variables
seed_in = argument0; // the input seed
key = 134775813; // the magical randomness key
seed_out = 0; // the output seed
bit_cur = 1; /* a number containing the current bit,
e.g. if "bit" is 5, this number will be in binary "100000" */
// "bit" is the bit index; we're doing a manual multiplication
for(bit = 0; bit < 32; bit += 1) // cycle through all the bits
{
// if the input seed has the 'current bit', add the key shifted to that bit; i.e. implement binary multiplication:
if (seed_in & bit_cur) seed_out += key;
bit_cur = (bit_cur << 1); // left bit-shift the current bit
key = ((key << 1) & $FFFFFFFF); // left bit-shift the key value and trim off anything that exceeds the 32-bit limit
}
return (seed_out & $FFFFFFFF) + 1; // return the new seed plus the extra 1So, what this does is simply manually calculates a binary product, limiting it to the 32-bit field. Binary multiplication is very similar to regular multiplication in arithmetic. To calculate a binary multiplication problem, take one multiplier and list it under the other multiplier, up-shifted to every bit in the other multiplier that's a one. Then just add these together, i.e.:
10010101 x 110110010:
10010101
--------
110110010
110110010
110110010
110110010
----------------
1111110010011010
Notice that under each 1 bit in the top number is the other number bit-shifted to its place
10010101
|--|-|-|
1|01|0|10
110|10|10
11011|010
110110010
So, the code basically just implements this. It cycles through and checks for whatever bits are 1. Once one is, the other number (in this case the key) is added to the output shifted over to that bit-place. Thus we left bit-shift the key in each cycle so that it's already prepared to be simply added to the output. The only difference in the script's multiplication from regular binary is that we're limiting the field to 4294967296. So, we trim off the end of the key as the multiplication progresses so that it doesn't become too big and corrupt the output's precision again (since the problem is, Game Maker's number precision to the full integer only continues a little beyond 32 bits). This is called modulated multiplication. Then, at the end we trim off the excess bits in the output, and add 1 to complete the algorithm. Note again that the input wasn't trimmed. It isn't necessary, though, in this case either because all we're doing with the input is checking which of the first 32 bits are ones.
- So, predicting the seed isn't that exciting. That is, what can we do with it when all we have to do to get the next seed is run random(1) and then call random_get_seed()?
To reverse the seed, we simply subtract one, and then divide by 134775813. Of course, straight division by 134775813 won't yield us the previous seed, but rather some number with a fractional portion. Now the question is, if the previous number was multiplied, and then gained bits beyond the limited field, but then those bits were cut off, how can we reverse division if we lost those bits? In another way, isn't it possible that two numbers could be multiplied by 134775813, and when limited to the field 4294967296, both yield the same number? In any normal modulated multiplication, it is definitely possible that two numbers can be multiplied by another number, and when looped back, yield the same number. However, as I explained before, the random seed must cover all the 4294967296 seeds before coming back again. Therefore, in this special case, no two numbers will multiply to produce the same number. So, it is possible to reverse this exceptional modulated multiplication algorithm. Now the question is, how?
Well, simply by reversing the multiplication method used for the forward step. The following code produces the previous seed:
random_step_backward(seed):
// takes a step backward in the randomness; i.e. returns the seed that comes before the argument0 seed
var seed_in, key, seed_out, bit, bit_cur;
// prepare variables
seed_in = argument0 - 1; // the input seed; subtract the one first
key = 134775813; // the magical randomness key
seed_out = 0; // the output seed
bit_cur = 1; /* the number containing the current bit, as before,
e.g. if "bit" is 5, this number will be in binary "100000" */
// "bit" is the bit index; we're doing division
for(bit = 0; bit < 32; bit += 1) // cycle through bits; we start at the first again
{
/* if the input seed has the 'current bit', remove the key shifted to that bit,
and assume it was part of the original multiplier: */
if (seed_in & bit_cur)
{
seed_in -= key; // remove the key shifted to the bit
seed_out |= bit_cur; // add that bit to the output seed; uses the bitwise or (|) operator to add bits
}
bit_cur = (bit_cur << 1); // left bit-shift the current bit
f = ((f << 1) & $FFFFFFFF); // left bit-shift the key value and trim off anything that exceeds the 32-bit limit
}
return seed_out; // return the previous seed; no trimming is necessary since one bit was added at a timeWhat happens here is basically the reverse of the multiplication; starting with the first bit, each bit is checked in the input seed. Once a 1 is found, it is added to the output seed, and then the key bit-shifted to that bit is subtracted from the input seed. Technically, this division is backwards, but that's what keeps it from getting some fractional number for an answer, because if it were calculating forward, it would begin to affect the fractional-place bits. And this division calculator also assumes it has any carry-down bits it needs, thus making it suitable for a modulated field. Remember, though, it only works because no two numbers will multiply by one other to the same number (i.e. if a != b then a * x != b * x).
WRAP UP: Good bye! I hoped you learned something you can use!
So, that wraps up my tutorial of Game Maker's random number system.
If anyone has any questions or suggestions, or notices any bugs, just PM me, and I'll get to it asap.
And for the purposes of those who'd like to use the two previously provided functions, here they are in a much more compact form (which would not have passed for a tutorial):
random_seed_backward(seed)
// returns the random seed previous to argument0
var n,f,o,b,ni;n=argument0-1
f=134775813;o=0;b=0;ni=1
for(b=0;b<32;b+=1)
{if n&ni{n-=f;o|=ni}
ni=(ni<<1);f=((f<<1)&$FFFFFFFF)}
return o&$FFFFFFFFrandom_seed_forward(seed)
// returns the random seed following argument0
var n,f,o,b,ni;n=argument0
f=134775813;o=0;b=0;ni=1
for(b=0;b<32;b+=1)
{if n&ni o+=f;ni=(ni<<1)
f=((f<<1)&$FFFFFFFF)}
return (o&$FFFFFFFF)+1And then here are functions that actually set Game Maker's seed forward and backward:
random_step_backward()
// takes a step back in the randomness
var n,f,o,b,ni;n=random_get_seed()-1
f=134775813;o=0;b=0;ni=1
for(b=0;b<32;b+=1)
{if n&ni{n-=f;o|=ni}
ni=(ni<<1);f=((f<<1)&$FFFFFFFF)}
random_set_seed(o&$FFFFFFFF)random_step_forward()
// takes a step forward in the randomness
var n,f,o,b,ni;n=random_get_seed()
f=134775813;o=0;b=0;ni=1
for(b=0;b<32;b+=1)
{if n&ni o+=f;ni=(ni<<1)
f=((f<<1)&$FFFFFFFF)}
random_set_seed((o&$FFFFFFFF)+1)And just for the record,
the random seed right before seed 0 is 649090867.
- Agamer



Find content
Male

