RNG: Difference between revisions

5,504 bytes added ,  19 April
no edit summary
(lots of data and math)
No edit summary
 
(11 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{stub}}
''[[Super Mario 64]]'' uses a ([[wikipedia:Pseudorandom number generator|pseudo-]])'''random number generator''' function, or (P)'''RNG''', to randomize elements of gameplay. The game usually calls the RNG function to make decisions in enemy AI, remove visible patterns from visuals and animation timing, and give the appearance of a chaotic physics system when scattering coins and particles.
'''RNG''' or '''Random Number Generation''' is the game's method of generating a random short. By doing this, the game can make things like dust movement seem random<ref>https://www.youtube.com/watch?v=MiuLeTE2MeQ</ref>.
 
Informally, "RNG" also refers to the randomness or luck that results from the RNG function.
 
==Technical description==
==Technical description==
The RNG variable is a short, which is a number from 0 to 65535 inclusive. When an [[object]], [[snow]], or something else wants to call RNG, it takes the current RNG value and runs it through the '''RNG function''', below. It uses the result in its own calculations and also stores the result to the RNG variable. Thus, the RNG variable is equal to the last used RNG value. Every [[frame]], every object is iterated through in order of [[object slot]]s and may choose whether to call RNG once, multiple times, or not at all.
The RNG consists of two parts: the evolution function (also known as the RNG function), and the [[wikipedia:Random seed|seed]]. Whenever something (usually an [[object]]) wants to use the RNG, it calls the evolution function. The evolution function will read the seed, perform a series of mathematical operations on it, and return the new value of the seed. This means that the seed always reflects the last value that the RNG function returned.
===The RNG function===
 
Because each object can load, unload, and use RNG multiple times per frame, the RNG function is often called an unpredictable number of times every frame. This makes the game's randomness often unpredictable even if you know the RNG function.
 
===RNG function===
Here is [[wikipedia:Pseudocode|pseudocode]] of the RNG function. It relies heavily on [[wikipedia:Bitwise operation|bitwise/binary operations]], specifically the [[wikipedia:Exclusive or|XOR]] operation.
 
<nowiki>set RNGSeed to 0
 
function RNG:
    if RNGSeed is 22026:
        set RNGSeed to 0
 
    set value A to (the last byte of RNGSeed) shifted to the left by 1 byte
    set value A to (value A) XOR (RNGSeed)
   
    set RNGSeed to value A
    swap the bytes of RNGSeed
   
    set value A to (the last byte of value A) shifted to the left by 1 bit
    set value A to (value A) XOR (RNGSeed)
    set value B to (value A) shifted to the right by 1 bit
    set value B to (value B) XOR 1111111110000000 (binary)
   
    if the last bit of value A is 0:
        if value B is 43605:
            set RNGSeed to 0
        else:
            set RNGSeed to (value B) XOR 0001111111110100 (binary)
    else:
        set RNGSeed to (value B) XOR 1000000110000000 (binary)
   
    return RNGSeed</nowiki>
 
===Walkthrough===
We begin with the seed set to, say, 64917. This is 1111110110010101 in [[wikipedia:Binary number|binary]].
 
First, this seed is not equal to 22026, so we don't set it to 0.
 
Each [[wikipedia:Byte|byte]] is 8 [[wikipedia:Bit|bits]] (1s or 0s), so the first byte of the seed is 11111101 and the last byte is 10010101. We set value A to the last byte 1001010, [[wikipedia:Logical shift|shifted to the left]] 1 byte. This means 1 byte of 0s are added onto the end. Value A now has the value 1001010100000000 in binary.
 
{|class="wikitable" style="float:right;margin-left:1em;text-align:center;"
|+XOR [[wikipedia:Truth table|truth table]]
|-
!style="width:2em;"|x
!style="width:2em;"|y
!style="border-left-width:2px;width:4em;"|x ⊕ y
|-
|0
|0
|style="border-left-width:2px;"|0
|-
|0
|style="background:#9EFF9E;"|1
|style="background:#9EFF9E;border-left-width:2px;"|1
|-
|style="background:#9EFF9E;"|1
|0
|style="background:#9EFF9E;border-left-width:2px;"|1
|-
|style="background:#9EFF9E;"|1
|style="background:#9EFF9E;"|1
|style="border-left-width:2px;"|0
|}
 
Now we XOR value A with the seed. ''XOR'', or ''[[wikipedia:Exclusive or|exclusive or]]'', is a bitwise operation that acts on each bit in the numbers. It takes the corresponding pairs of bits in each number, and checks if they are different or not. If they are different, a 1 is put into a new number at the location the pair of bits both had in their original numbers; if they are the same, a 0 is put there.
 
  <nowiki>
  <nowiki>
unsigned short rng_function (unsigned short input) {
  11111101 10010101 - seed
    if (input == 0x560A) input = 0;
⊕ 10010101 00000000 - value A
    unsigned short S0 = (unsigned char)input << 8;
-------------------
    S0 = S0 ^ input;
  01101000 10010101 - new number
    input = ((S0 & 0xFF) << 8) | ((S0 & 0xFF00) >> 8);
        ^       ^ 1 and 0 are different, so the new number has a 1 in this place.
    S0 = ((unsigned char)S0 << 1) ^ input;
        1 and 1 are the same, so the new number has a 0 in this place.
    short S1 = (S0 >> 1) ^ 0xFF80;
    if ((S0 & 1) == 0) {
        if (S1 == 0xAA55) input = 0;
        else input = S1 ^ 0x1FF4;
    }
    else input = S1 ^ 0x8180;
    return (unsigned short)input;
}
</nowiki>
</nowiki>
===Description===
 
[[File:RNG Graph.png|500px|right|A graph showing notable RNG values and indeces in SM64.]]
Value A is set to this new number. Then the bytes of value A are swapped and the seed is set to that, so the seed becomes 1001010101101000.
The function has 65536 possible inputs and 65536 possible outputs. It is a bijection, meaning that every input maps to exactly one output and none repeat or are left out. The function forms two loops, one of length 65534 and one of length 2, but one of the if statements causes the cycle of 2 to lead back to the cycle of 65534. Oddly, the RNG value of 21674 at index 65113 loops back to index 0. This may be because this index's S1 value is equal to the previous RNG value for the first time, but that is no reason to prematurely end the loop. Indeces 65114-65533, as well as values 22026 and 58704 (the loop of 2,  which are not given indeces) are considered [https://www.youtube.com/redirect?v=MiuLeTE2MeQ&redir_token=SymUNd8wBHOCXi28TSwbDD5ZZ9N8MTUzOTI1NzkyM0AxNTM5MTcxNTIz&event=video_description&q=https%3A%2F%2Fdrive.google.com%2Ffile%2Fd%2F0B3JCRPC09lDtcHRjdU9pSjJCX1U%2Fview%3Fusp%3Dsharing|'''impossible RNG values'''] and cannot be reached without hacking. All other RNG values are considered [https://www.youtube.com/redirect?v=MiuLeTE2MeQ&redir_token=SymUNd8wBHOCXi28TSwbDD5ZZ9N8MTUzOTI1NzkyM0AxNTM5MTcxNTIz&event=video_description&q=https%3A%2F%2Fdrive.google.com%2Ffile%2Fd%2F0B3JCRPC09lDtOExmOW1hclhiNHc%2Fview%3Fusp%3Dsharing|'''possible RNG values''']. The RNG index and value of 0 is set when the game powers on.
 
The current state is:
* Seed: 1001010101101000
* Value A: 0110100010010101
 
The last byte of value A is 10010101, and shifting this to the left by 1 bit gives 100101010. Value A is set to that, so it becomes 0000000100101010. It's XORed with the seed again, and value A is set to the new value:
 
<nowiki>
  10010101 01101000 - seed
⊕ 00000001 00101010 - value A
-------------------
  10010100 01000010 - new value A
</nowiki>
 
Now we declare value B, and set it to value A but [[wikipedia:Logical shift|shifted to the right]] one bit. Value A is 1001010001000010, so value B becomes 0100101000100001.
 
Value B is now XORed with the number 1111111110000000, which is a so-called "[[wikipedia:Magic number (programming)|magic number]]": its purpose is not clear in the code.
 
<nowiki>
  01001010 00100001 - value B
⊕ 11111111 10000000 - magic number
-------------------
  10110101 10100001 - new value B
</nowiki>
 
The current state is:
* Seed: 1001010101101000
* Value A: 1001010001000010
* Value B: 1011010110100001
 
The last bit of value A is 0, so we look at value B. In decimal, it is 46497, not 43605, so the seed will be set to value B XOR 0001111111110100. This is another magic number.
 
<nowiki>
  10110101 10100001 - value B
⊕ 00011111 11110100 - magic number
-------------------
  10101010 01010101 - new seed
</nowiki>
 
Now we return the seed. 1010101001010101 in decimal is 43605, which is the expected result.
 
===Properties===
[[File:RNG Graph.png|500px|thumb|A graph showing notable RNG values and indices in SM64.]]
The RNG function has 65536 possible inputs and 65536 possible outputs. It is a [[wikipedia:Bijection|bijection]], meaning that every input maps to exactly one output, and none repeat or are left out.
 
The function forms two loops, one of length 65534 and one of length 2, but one of the [[wikipedia:If statement|if statements]] causes the cycle of 2 to lead back to the cycle of 65534.
 
Oddly, the RNG value of 21674 at index 65113 loops back to index 0. This may be because this index's S1 value is equal to the previous RNG value for the first time, but that is no reason to prematurely end the loop.
 
The RNG index and value of 0 is set when the game powers on.
 
===Impossible RNG values===
Many RNG values would function within the game, but are impossible to reach from a valid gameplay state due to how the RNG values cycle.
 
1 set of these is a 2-value RNG loop (values 22026 and 58704) that has no lead-ins from the main loop. If you entered this loop somehow, it would send you back to the main loop almost immediately.
 
The other set (indices 65114&ndash;65533) is impossible to reach because of a manual check that resets the primary loop early. This leaves the latter part of the loop, after the reset, unreachable.
 
Any other RNG is considered possible.
 
==Cycling RNG==
==Cycling RNG==
Because RNG indeces loop from 0 to 65113 and back to 0 again, by looping through every RNG index Mario can reach any reachable RNG value. By making [[dust]], Mario can advance RNG 4 times per frame, which takes 9 minutes to loop. If objects are calling RNG, this time will be much faster.
Because RNG indices loop from 0 to 65113 and back to 0 again, Mario can reach any valid RNG value by looping through every RNG index. By making [[dust]], Mario can advance RNG 4 times per frame, which takes 9 minutes to loop. If other objects are calling RNG as well, this time will be much faster.
 
==Objects that call RNG==
==Objects that call RNG==
The following objects always call RNG under the given circumstances:<ref>https://www.youtube.com/watch?v=MiuLeTE2MeQ</ref>
* Dust calls RNG to determine its resultant [[angle]] and [[horizontal speed]]. Note that the dust itself calls RNG, not the [[dust spawner]].
* Dust calls RNG to determine its resultant [[angle]] and [[horizontal speed]]. Note that the dust itself calls RNG, not the [[dust spawner]].
* [[Goomba]]s call RNG to determine which direction to move when they do not see Mario.
* [[Goomba]]s call RNG to determine which direction to move when they do not see Mario.
* [[Bob-omb]]s call RNG almost every frame to determine whether to [[blink]]. If the value that they receive is less than 656, they blink. Otherwise, they don't. Taking into account impossible RNG, the chance of a Bob-omb blinking on a given frame is 0.0099785.
* [[Bob-omb]]s call RNG almost every frame to determine whether to [[blink]]. If the value that they receive is less than 656, they blink. Otherwise, they don't. Taking into account impossible RNG, the chance of a Bob-omb blinking on a given frame is 0.0099785.
* Although not an object, [[snow]] also calls RNG.
* Although not an object, [[snow]] also calls RNG.
* [[Coin]]s from Bob-ombs, Goombas, [[Big Cork Box]]es, [[Cork Box]]es, [[Piranha Plant]]s, [[Bully|Bullies]], [[Chuckya]]s, [[Fly Guy]]s, [[Snowman|Snowmen]], etc... you name it, it calls RNG 3 times to determine angle, horizontal speed, and [[vertical speed]]. Since angles are shorts just like RNG, the RNG value is used directly for the angle. Horizontal speed is set to (RNG / 65536) * 20, and vertical speed is set to (RNG / 65536) * 40 + 17. Since these RNG calls must be contiguous, there are still only 65114 possible coin trajectories.
* [[Coin]]s from Bob-ombs, Goombas, [[Big Cork Box]]es, [[Cork Box]]es, [[Piranha Plant]]s, [[Bully|Bullies]], [[Chuckya]]s, [[Fly Guy]]s, [[Snowman|Snowmen]], etc.&hellip; you name it, it calls RNG 3 times to determine angle, horizontal speed, and [[vertical speed]]. Since angles are shorts just like RNG, the RNG value is used directly for the angle. Horizontal speed is set to <math display="inline">\frac{\mathrm{RNG}}{65536} \times 20</math>, and vertical speed is set to <math display="inline">\frac{\mathrm{RNG}}{65536} \times 40 + 17</math>. Since these RNG calls must be contiguous, there are still only 65114 possible coin trajectories.
* [[Bowser]] calls RNG to determine which attack to do next.
* [[Bowser]] calls RNG to determine which attack to do next.
* [[Spinner]]s call RNG twice when they stop spinning on the random setting. The first call determines direction. If the result is less than 32768, the spinner spins clockwise, and otherwise counterclockwise. The chance of a spinner choosing to spin clockwise on a given frame is 0.50016893. Spinners calculate their intended angular displacement by the formula (RNG % 4) * 30 + 30. Thus, the chance of spinning 30 [[angle unit]]s is 0.24982339, the chance of spinning 60 angle units is 0.24999232, the chance of spinning 90 angle units is 0.25008447, and the chance of spinning 120 angle units is 0.25009982.
* And many, many, many more.
* And many, many, many, many, many more.
 
===Tick Tock Clock random setting only===
When [[Tick Tock Clock]] is entered with the hand near 6:00 (the random setting), the following objects call RNG under the given circumstances:
* [[Spinner]]s: See [[Spinner#Random Setting|Spinner &sect; Random Setting]].
* [[Cog]]s call RNG twice when they reach their target [[angular velocity]] (TAV)<ref>https://www.youtube.com/watch?v=S0q3_toE3b4</ref>. The cog approaches its TAV with an angular acceleration of <math display="inline">\pm 50\ \frac{\text{angular unit}}{\text{frame}^{2}}</math>. The first call determines the cog's next TAV. Because of the way the formula is structured, there is a possibility that the TAV is 0. If this occurs multiple times, the cog can stand still. The TAV is calculated by the following formula, where % is the [[wikipedia:Modulo|modulo]] operator:<math display="block">\text{target angular velocity} = \left(\mathrm{RNG}\ \%\ 7\right) \times 200</math>The second RNG call determines the sign of the TAV. If the result is less than 32768, the sign is -1, and 1 otherwise.
 
The following table summarizes this information:
{| class="wikitable"
|-
! RNG1 % 7 !! 0 (14.313358%) !! 1 (14.265749%) !! 2 (14.278035%) !! 3 (14.281107%) !! 4 (14.282643%) !! 5 (14.284179%) !! 6 (14.284929%)  
|-
| '''RNG2 < 32768 (50.016893%)''' || 0 (7.1551433%) || -200 (7.1274995%) || -400 (7.1413214%) || -600 (7.1490002%) || -800 (7.1443929%) || -1000 (7.1505360%) || -1200 (7.1490002%)
|-
| '''RNG2 ≥ 32768 (49.983107%)''' || 0 (7.1582148%) || 200 (7.1382498%) || 400 (7.1367141%) || 600 (7.1321068%) || 800 (7.1382498%) || 1000 (7.1336425%) || 1200 (7.1459287%)
|}
 
==References==
==References==
<references />
<references />
[[Category:Mechanics]]
confirmed_editors
16

edits