EncDB: Fix colo starter yield

Improve enc->ck3 creation, inline the shiny/gender reroll logic instead of trying again outside the loop.
Rewrite the pidiv logic to be more obvious

colo starters now show up in encdb list
This commit is contained in:
Kurt 2023-10-26 01:07:20 -07:00
parent 325f75e3d3
commit 16aba45371
4 changed files with 92 additions and 52 deletions

View File

@ -66,7 +66,8 @@ public bool MoveNext()
Index = 0; State = YieldState.StaticColoStarters; goto case YieldState.StaticColoStarters;
case YieldState.StaticColoStarters:
if (TryGetNext(Encounters3Colo.Starters))
Index = 0; State = YieldState.StaticColoGift; goto case YieldState.StaticColoGift;
return true;
Index = 0; State = YieldState.StaticColoGift; goto case YieldState.StaticColoGift;
case YieldState.StaticColoGift:
if (TryGetNext(Encounters3Colo.Gifts))
return true;

View File

@ -62,6 +62,15 @@ public static class XDRNG
private const uint Add9 = unchecked((Add8 * Mult) + Add); // 0xA8D2826B
private const uint rAdd9 = unchecked((rAdd8 * rMult) + rAdd);// 0x46C51ED9
private const uint rMult10 = unchecked(rMult9 * rMult); // 0xC6169599
private const uint rAdd10 = unchecked((rAdd9 * rMult) + rAdd);// 0x3E86BD4E
private const uint rMult11 = unchecked(rMult10 * rMult);
private const uint rAdd11 = unchecked((rAdd10 * rMult) + rAdd);
private const uint rMult12 = unchecked(rMult11 * rMult);
private const uint rAdd12 = unchecked((rAdd11 * rMult) + rAdd);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next (uint seed) => (seed * Mult ) + Add ;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next2(uint seed) => (seed * Mult2) + Add2;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next3(uint seed) => (seed * Mult3) + Add3;
@ -81,6 +90,31 @@ public static class XDRNG
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev7(uint seed) => (seed * rMult7) + rAdd7;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev8(uint seed) => (seed * rMult8) + rAdd8;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev9(uint seed) => (seed * rMult9) + rAdd9;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev10(uint seed) => (seed * rMult10) + rAdd10;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev11(uint seed) => (seed * rMult11) + rAdd11;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev12(uint seed) => (seed * rMult12) + rAdd12;
/// <summary>
/// Gets the next 16 bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Next16(ref uint seed)
{
seed = Next(seed);
return seed >> 16;
}
/// <summary>
/// Gets the next 16 bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Next15(ref uint seed)
{
seed = Next(seed);
return (seed >> 16) & 0x7FFF;
}
/// <summary>
/// Advances the RNG seed to the next state value a specified amount of times.

View File

@ -62,41 +62,39 @@ public static bool IsXDStarterValid(uint seed, uint TID16, uint SID16)
/// Checks if the Colosseum starter correlation can be obtained with the trainer's IDs.
/// </summary>
/// <param name="species">Species of the starter, to indicate Espeon vs Umbreon</param>
/// <param name="seed">Seed the PID/IV is generated with</param>
/// <param name="origin">Seed the PID/IV is generated with</param>
/// <param name="TID16">Trainer ID of the trainer</param>
/// <param name="SID16">Secret ID of the trainer</param>
/// <param name="pkPID">PID of the entity</param>
/// <param name="IV1">First 3 IVs combined</param>
/// <param name="IV2">Last 3 IVs combined</param>
public static bool IsColoStarterValid(ushort species, ref uint seed, ushort TID16, ushort SID16, uint pkPID, uint IV1, uint IV2)
public static bool IsColoStarterValid(ushort species, ref uint origin, ushort TID16, ushort SID16, uint pkPID, uint IV1, uint IV2)
{
// reverse the seed the bare minimum
uint SIDf = species == (int)Species.Espeon
? XDRNG.Prev9(seed)
: XDRNG.Prev2(seed);
// Input seed is right after the TID/SID and 2x fake rolls. Reverse the seed to the first possible SID seed value.
var seed = species == (int)Species.Espeon
? XDRNG.Prev12(origin)
: XDRNG.Prev3(origin);
// reverse until we find the TID16/SID16, then run the generation forward to see if it matches our inputs.
// Reverse until we find the TID16/SID16, then run the generation forward to see if it matches our inputs.
const int arbitraryLookback = 8;
int ctr = 0;
uint temp;
while ((temp = XDRNG.Prev(SIDf)) >> 16 != TID16 || SIDf >> 16 != SID16)
while (true)
{
SIDf = temp;
if (ctr > 32) // arbitrary
return false;
ctr++;
if (seed >> 16 == SID16 && XDRNG.Prev(seed) >> 16 == TID16)
{
origin = XDRNG.Prev2(seed);
break; // result!
}
if (++ctr == arbitraryLookback)
return false; // no valid seed found
seed = XDRNG.Prev2(seed);
}
var next = XDRNG.Next(SIDf);
// generate Umbreon
var PIDIV = GenerateValidColoStarterPID(ref next, TID16, SID16);
var PIDIV = GenerateValidColoStarter(ref seed, TID16, SID16);
if (species == (int)Species.Espeon) // need Espeon, which is immediately next
PIDIV = GenerateValidColoStarterPID(ref next, TID16, SID16);
if (!PIDIV.Equals(pkPID, IV1, IV2))
return false;
seed = XDRNG.Prev2(SIDf);
return true;
PIDIV = GenerateValidColoStarter(ref seed, TID16, SID16);
return PIDIV.Equals(pkPID, IV1, IV2);
}
private readonly record struct PIDIVGroup(uint PID, uint IV1, uint IV2)
@ -104,36 +102,31 @@ public static bool IsColoStarterValid(ushort species, ref uint seed, ushort TID1
public bool Equals(uint pid, uint iv1, uint iv2) => PID == pid && IV1 == iv1 && IV2 == iv2;
}
private static PIDIVGroup GenerateValidColoStarterPID(ref uint uSeed, ushort TID16, ushort SID16)
{
uSeed = XDRNG.Next2(uSeed); // skip fakePID
var IV1 = (uSeed >> 16) & 0x7FFF;
uSeed = XDRNG.Next(uSeed);
var IV2 = (uSeed >> 16) & 0x7FFF;
uSeed = XDRNG.Next(uSeed);
uSeed = XDRNG.Next(uSeed); // skip ability call
var PID = GenerateStarterPID(ref uSeed, TID16, SID16);
public static void SkipValidColoStarter(ref uint seed, ushort TID16, ushort SID16) => GenerateValidColoStarter(ref seed, TID16, SID16);
uSeed = XDRNG.Next2(uSeed); // PID calls consumed
private static PIDIVGroup GenerateValidColoStarter(ref uint seed, ushort TID16, ushort SID16)
{
seed = XDRNG.Next2(seed); // skip fakePID
var IV1 = XDRNG.Next15(ref seed);
var IV2 = XDRNG.Next15(ref seed);
seed = XDRNG.Next(seed); // ability call
var PID = GenerateStarterPID(ref seed, TID16, SID16);
return new PIDIVGroup(PID, IV1, IV2);
}
private static bool IsShiny(ushort TID16, ushort SID16, uint PID) => ((PID >> 16) ^ TID16 ^ SID16 ^ (PID & 0xFFFF)) < 8;
private static uint GenerateStarterPID(ref uint uSeed, ushort TID16, ushort SID16)
public static uint GenerateStarterPID(ref uint seed, ushort TID16, ushort SID16)
{
uint PID;
const byte ratio = 0x1F; // 12.5% F (can't be female)
while (true)
{
var next = XDRNG.Next(uSeed);
PID = (uSeed & 0xFFFF0000) | (next >> 16);
var first = seed = XDRNG.Next(seed); // first PID roll
var second = seed = XDRNG.Next(seed); // second PID roll
var PID = (first & 0xFFFF0000) | (second >> 16);
if ((PID & 0xFF) >= ratio && !IsShiny(TID16, SID16, PID))
break;
uSeed = XDRNG.Next(next);
return PID;
}
return PID;
}
}

View File

@ -81,26 +81,38 @@ private static void SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed)
private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
{
switch (pk.Species)
var species = pk.Species;
switch (species)
{
case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee
pk.TID16 = (ushort)((seed = XDRNG.Next(seed)) >> 16);
pk.SID16 = (ushort)((seed = XDRNG.Next(seed)) >> 16);
seed = XDRNG.Next2(seed); // PID calls consumed
pk.TID16 = (ushort)XDRNG.Next16(ref seed);
pk.SID16 = (ushort)XDRNG.Next16(ref seed);
seed = XDRNG.Next2(seed); // fake PID
break;
case (int)Species.Espeon: // Colo Espeon
pk.TID16 = (ushort)((seed = XDRNG.Next(seed)) >> 16);
pk.SID16 = (ushort)((seed = XDRNG.Next(seed)) >> 16);
seed = XDRNG.Next9(seed); // PID calls consumed, skip over Umbreon
var tid = pk.TID16 = (ushort)XDRNG.Next16(ref seed);
var sid = pk.SID16 = (ushort)XDRNG.Next16(ref seed);
LockFinder.SkipValidColoStarter(ref seed, tid, sid);
seed = XDRNG.Next2(seed); // fake PID
break;
}
var A = XDRNG.Next(seed); // IV1
var B = XDRNG.Next(A); // IV2
var C = XDRNG.Next(B); // Ability?
var D = XDRNG.Next(C); // PID
var E = XDRNG.Next(D); // PID
var C = XDRNG.Next(B); // Ability
if (species is (int)Species.Umbreon or (int)Species.Espeon)
{
// Reuse existing logic.
pk.PID = LockFinder.GenerateStarterPID(ref C, pk.TID16, pk.SID16);
}
else
{
// Generate PID.
var D = XDRNG.Next(C); // PID
var E = XDRNG.Next(D); // PID
pk.PID = (D & 0xFFFF0000) | (E >> 16);
}
pk.PID = (D & 0xFFFF0000) | (E >> 16);
Span<int> IVs = stackalloc int[6];
MethodFinder.GetIVsInt32(IVs, A >> 16, B >> 16);
pk.SetIVs(IVs);