Hello,
This morning I decided to gather my various works on Pokémon games and start a documentation series.
The first topic I am presenting is Shiny Pokémon generation.
It is often said that the chance of encountering a shiny Pokémon is 1 in 4096, but do you know where this number comes from?
The analysis was performed on Pokémon Alpha Sapphire v1.4, based on disassembled code.
This document aims to educate shiny hunters and debunk myths, such as: “Fishing near a rock will increase your chances of finding a shiny.”
1) Random Number Generation
The game uses the TinyMT32 PRNG algorithm.
The function generating random numbers is located at address 0x00164714.
I will not explain how this algorithm works; we will just use the following function from now on.
2) Checking if a Pokémon is Shiny
The corresponding function is at address 0x00168F48.
The game does not use a boolean isShiny. Instead, it uses two identifiers: ID and PID (see PKHeX: PK6.cs).
Both are 32-bit random numbers generated using the algorithm described above.
Here is the function translated into C for those who are not familiar with ARM assembly.
For the rest of this document, I will only provide the C code that I have rewritten - no more assembly code - but I am keeping the addresses in case you want to decompile the game yourself.
For the condition r < 16 to be satisfied, r must look like this in binary:
r = 0b000000000000XXXX, where each X can be 0 or 1.
The probability that bit 15 is 0 is 1/2, bit 14 is also 1/2, and so on down to bit 4.
Therefore, the total probability is:
(1/2) x ... x (1/2) = (1/2)^12 = 1/4096 ≈ 0.00024414062 ≈ 0.024%
This is the chance of encountering a shiny Pokémon.
3) Forcing a Pokémon Not to Be Shiny
Function at 0x00168FCC.
By toggling the correct bit, the condition above is no longer satisfied:
4) Forcing a Pokémon to Be Shiny
Function at 0x00168F6C.
Here, we use the property X ^ X = 0 (where ^ = XOR).
Since d = a ^ b ^ c, the condition becomes:
r = a ^ b ^ c ^ d = (a ^ b ^ c) ^ (a ^ b ^ c) = 0 < 16
5) Masuda Method: Increasing Shiny Chance via Breeding
The function that initializes the egg is located at address 0x003B1784, and the portion handling shiny generation is at 0x003B1D38.
- If no conditions are met, n = 0 → no additional draws are performed, and the PID generated initially is kept.
This gives a base chance of 1/4096.
- If the parents have different languages, 6 draws are performed.
Since these draws are independent, the total probability becomes 1/4096 + ... + 1/4096 = 6/4096 ≈ 0.00146484375 ≈ 0.146%.
- With the Shiny Charm, the total increases to 8/4096 ≈ 0.001953125 ≈ 0.195%.
6) Regular Encounters + Fishing Method
The relevant portion of the function is at 0x007D8BD8, and the second part at 0x0014EC00.
For fishing, the game uses a counter that starts at 0 and caps at 20.
- Each successful cast increments the counter by 1.
- The counter resets if the encountered Pokémon is shiny, or if the rod is cast too early or too late, resulting in a failure.
Nothing else affects it—no conditions based on the player’s position, etc.
As above, all draws are independent.
Examples :
- With the Shiny Charm and the first fishing attempt (fishingCounter = 0), n = 3, giving a 3/4096 chance of encountering a shiny.
- If fishingCounter = 20, the probability becomes (3+2∗20)/4096 = 43/4096 ≈ 0.010498 ≈ 1.05%.
7) DexNav
The portion of code we are interested in is at address 0x007D891C within the function at 0x007D8728.
I am not able to tell you what the variable v represents or why they chose to perform this calculation.
If you have any questions or notice anything missing, feel free to ask.
Bonus Section
Since I have provided the function that checks whether a Pokémon is shiny or not,
you can simply always return true so that all Pokémon in the game (starters, legendaries, opposing players’ Pokémon, etc.) are shiny.
In assembly: MOV R0, #1; BX LR
This gives the following Action Replay code:
This morning I decided to gather my various works on Pokémon games and start a documentation series.
The first topic I am presenting is Shiny Pokémon generation.
It is often said that the chance of encountering a shiny Pokémon is 1 in 4096, but do you know where this number comes from?
The analysis was performed on Pokémon Alpha Sapphire v1.4, based on disassembled code.
This document aims to educate shiny hunters and debunk myths, such as: “Fishing near a rock will increase your chances of finding a shiny.”
1) Random Number Generation
The game uses the TinyMT32 PRNG algorithm.
The function generating random numbers is located at address 0x00164714.
I will not explain how this algorithm works; we will just use the following function from now on.
C:
/*
* Returns a uniformly distributed random value.
* Each bit has a 50% chance of being 0 or 1.
*/
u32 GetRandomU32();
2) Checking if a Pokémon is Shiny
The corresponding function is at address 0x00168F48.
The game does not use a boolean isShiny. Instead, it uses two identifiers: ID and PID (see PKHeX: PK6.cs).
Both are 32-bit random numbers generated using the algorithm described above.
Code:
IsShiny:
UXTH R3, R0
EOR R0, R3, R0, LSR #16
UXTH R2, R1
EOR R0, R0, R1, LSR #16
EOR R0, R0, R2
CMP R0, #0x10
MOVCC R0, #1
MOVCS R0, #0
BX LR
Here is the function translated into C for those who are not familiar with ARM assembly.
For the rest of this document, I will only provide the C code that I have rewritten - no more assembly code - but I am keeping the addresses in case you want to decompile the game yourself.
C:
bool IsShiny(u32 id, u32 pid)
{
u16 a = (u16)id; // low 16 bits of ID
u16 b = (u16)(id >> 16); // high 16 bits of ID
u16 c = (u16)pid; // low 16 bits of PID
u16 d = (u16)(pid >> 16);// high 16 bits of PID
u16 r = a ^ b ^ c ^ d; // XOR all parts
return r < 16; // shiny if the top 12 bits are all 0
}
For the condition r < 16 to be satisfied, r must look like this in binary:
r = 0b000000000000XXXX, where each X can be 0 or 1.
The probability that bit 15 is 0 is 1/2, bit 14 is also 1/2, and so on down to bit 4.
Therefore, the total probability is:
(1/2) x ... x (1/2) = (1/2)^12 = 1/4096 ≈ 0.00024414062 ≈ 0.024%
This is the chance of encountering a shiny Pokémon.
3) Forcing a Pokémon Not to Be Shiny
Function at 0x00168FCC.
By toggling the correct bit, the condition above is no longer satisfied:
C:
void SetNoShiny(u32 id, u32 *pid)
{
if (IsShiny(id, *pid))
{
*pid ^= 0x10000000;
}
}
4) Forcing a Pokémon to Be Shiny
Function at 0x00168F6C.
Here, we use the property X ^ X = 0 (where ^ = XOR).
Since d = a ^ b ^ c, the condition becomes:
r = a ^ b ^ c ^ d = (a ^ b ^ c) ^ (a ^ b ^ c) = 0 < 16
C:
void SetShiny(u32 id, u32 *pid)
{
if (IsShiny(id, *pid))
return;
u16 a = (u16)id;
u16 b = (u16)(id >> 16);
u16 c = (u16)*pid;
u16 d = a ^ b ^ c;
*pid = (d << 16) | c;
}
5) Masuda Method: Increasing Shiny Chance via Breeding
The function that initializes the egg is located at address 0x003B1784, and the portion handling shiny generation is at 0x003B1D38.
- If no conditions are met, n = 0 → no additional draws are performed, and the PID generated initially is kept.
This gives a base chance of 1/4096.
- If the parents have different languages, 6 draws are performed.
Since these draws are independent, the total probability becomes 1/4096 + ... + 1/4096 = 6/4096 ≈ 0.00146484375 ≈ 0.146%.
- With the Shiny Charm, the total increases to 8/4096 ≈ 0.001953125 ≈ 0.195%.
C:
void ShinyEgg(u32 id, u32 *pid, u8 motherLanguage, u8 fatherLanguage, bool hasShinyCharm)
{
u32 n = 0; // number of random draws
if (motherLanguage != fatherLanguage)
n += 6;
if (hasShinyCharm)
n += 2;
for (u32 k = 0; k < n; k++)
{
u32 randomPid = GetRandomU32();
if (IsShiny(id, randomPid))
{
*pid = randomPid;
break;
}
}
}
6) Regular Encounters + Fishing Method
The relevant portion of the function is at 0x007D8BD8, and the second part at 0x0014EC00.
For fishing, the game uses a counter that starts at 0 and caps at 20.
- Each successful cast increments the counter by 1.
- The counter resets if the encountered Pokémon is shiny, or if the rod is cast too early or too late, resulting in a failure.
Nothing else affects it—no conditions based on the player’s position, etc.
As above, all draws are independent.
Examples :
- With the Shiny Charm and the first fishing attempt (fishingCounter = 0), n = 3, giving a 3/4096 chance of encountering a shiny.
- If fishingCounter = 20, the probability becomes (3+2∗20)/4096 = 43/4096 ≈ 0.010498 ≈ 1.05%.
C:
void ShinyEncounter(u32 id, u32 *pid, bool hasShinyCharm, u32 fishingCounter, u32 type)
{
static const u32 MUST_BE_NO_SHINY = 0x1FFFFFFFF;
static const u32 MUST_BE_SHINY = 0x2FFFFFFFF;
static const u32 NO_CONSTRAINT = 0x3FFFFFFFF;
u32 n = 1 + (hasShinyCharm ? 2 : 0);
n += fishingCounter * 2;
if (type == MUST_BE_NO_SHINY)
{
SetNoShiny(id, pid);
}
else if (type == MUST_BE_SHINY)
{
SetShiny(id, pid);
}
else if (type == NO_CONSTRAINT)
{
for (u32 k = 0; k < n; k++)
{
u32 randomPid = GetRandomU32();
if (IsShiny(id, randomPid))
{
*pid = randomPid;
break;
}
}
}
}
7) DexNav
The portion of code we are interested in is at address 0x007D891C within the function at 0x007D8728.
I am not able to tell you what the variable v represents or why they chose to perform this calculation.
C:
/**
* @type: Pointer to the encounter type (may be set to MUST_BE_SHINY)
* @consecutiveEncountersNum: Number of consecutive encounters (not necessarily the same Pokémon)
* @encountNum: Total number of encounters for this Pokémon
* @hasShinyCharm: True if the player possesses the Shiny Charm
* @isDexNavSpecial: True if the Pokémon is a special DexNav encounter
*/
void ShinyDexNav(u32 *type, u32 consecutiveEncountersNum, u32 encountNum, bool hasShinyCharm, bool isDexNavSpecial)
{
double p = 0;
u32 v = encountNum;
if (v > 200)
{
p = v - 200;
v = 200;
}
if (v > 100)
{
p += 2 * v - 200;
v = 100;
}
if (v > 0)
{
p += 6 * v;
}
u32 n = 1 + (hasShinyCharm ? 2 : 0);
if (isDexNavSpecial)
n += 4;
if (consecutiveEncountersNum == 50)
n += 5;
else if (consecutiveEncountersNum == 100)
n += 10;
for (u32 k = 0; k < n; k++)
{
if (GetRandomU32() % 10000 < p * 0.01)
{
*type = MUST_BE_SHINY;
break;
}
}
}
If you have any questions or notice anything missing, feel free to ask.
Bonus Section
Since I have provided the function that checks whether a Pokémon is shiny or not,
you can simply always return true so that all Pokémon in the game (starters, legendaries, opposing players’ Pokémon, etc.) are shiny.
In assembly: MOV R0, #1; BX LR
This gives the following Action Replay code:
Code:
[ALWAYS SHINY]
00168F48 E3A00001
00168F4C E12FFF1E







