mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-08-17 09:27:39 -05:00
More refinements/impl of lattice reversals
#4844 not yet implemented: xoroshiro algorithmic improvements overflow counterexample for channel IV reversal regarding s32 vs s64 noted in the github issue Co-Authored-By: StarfBerry <65578604+StarfBerry@users.noreply.github.com>
This commit is contained in:
@@ -49,9 +49,9 @@ public static int GetSeedsIVs(Span<uint> result, uint hp, uint atk, uint def, ui
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||
{
|
||||
ulong tmp = (ulong)(((first - (second * RMult)) >> 16) & 0xFFFF) * RLag0;
|
||||
var lo = (uint)((tmp + RLower) >> 16);
|
||||
var up = (uint)((tmp + RUpper) >> 16);
|
||||
uint tmp = ((first - (second * RMult)) >> 16) * RLag0;
|
||||
uint lo = (tmp + RLower) >> 16;
|
||||
uint up = (tmp + RUpper) >> 16;
|
||||
if (lo != up) // true in around 10% of cases
|
||||
return 0;
|
||||
|
||||
@@ -76,23 +76,23 @@ public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeedsIVs(Span<uint> result, uint first, uint second)
|
||||
{
|
||||
long diff = ((long)LCRNG.Mult * first) - second;
|
||||
ulong tmp = (ulong)((diff >> 16) & 0xFFFF) * Lag1;
|
||||
ulong tmp = (ulong)(((LCRNG.Mult * first) - second >> 16) & 0xFFFF) * Lag1;
|
||||
var lo = (uint)(((tmp + Lower) >> 15) * Lag0);
|
||||
var mi = lo + Lag0;
|
||||
var up = (uint)(((tmp + Upper) >> 15) * Lag0);
|
||||
|
||||
int ctr = 0;
|
||||
// around 2.70 iterations in average
|
||||
AddSeeds(result, lo % Lag1, first, second, ref ctr);
|
||||
AddSeeds(result, mi % Lag1, first, second, ref ctr);
|
||||
AddSeeds(result, lo, first, second, ref ctr);
|
||||
AddSeeds(result, mi, first, second, ref ctr);
|
||||
if (mi != up) // true in around 12% of cases
|
||||
AddSeeds(result, up % Lag1, first, second, ref ctr);
|
||||
AddSeeds(result, up, first, second, ref ctr);
|
||||
return ctr;
|
||||
}
|
||||
|
||||
private static void AddSeeds(Span<uint> result, uint low, uint first, uint second, ref int ctr)
|
||||
{
|
||||
low %= Lag1;
|
||||
// at most 3 iterations
|
||||
do
|
||||
{
|
||||
|
||||
@@ -46,9 +46,9 @@ public static int GetSeedsIVs(Span<uint> result, uint hp, uint atk, uint def, ui
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeeds(Span<uint> result, uint first, uint third)
|
||||
{
|
||||
ulong tmp = (ulong)(((first - (third * RMult2)) >> 16) & 0xFFFF) * RLag0;
|
||||
var lo = (uint)((tmp + RLowerPID) >> 16);
|
||||
var up = (uint)((tmp + RUpper) >> 16);
|
||||
uint tmp = ((first - (third * RMult2)) >> 16) * RLag0;
|
||||
uint lo = (tmp + RLowerPID) >> 16;
|
||||
uint up = (tmp + RUpper) >> 16;
|
||||
if (lo != up) // true in around 35% of cases
|
||||
return 0;
|
||||
|
||||
@@ -73,9 +73,9 @@ public static int GetSeeds(Span<uint> result, uint first, uint third)
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeedsIVs(Span<uint> result, uint first, uint third)
|
||||
{
|
||||
ulong tmp = (ulong)(((first - (third * RMult2)) >> 16) & 0xFFFF) * RLag0;
|
||||
var lo = (uint)((tmp + RLowerIVs) >> 15);
|
||||
var up = (uint)((tmp + RUpper) >> 15);
|
||||
uint tmp = ((first - (third * RMult2)) >> 16) * RLag0;
|
||||
uint lo = (tmp + RLowerIVs) >> 15;
|
||||
uint up = (tmp + RUpper) >> 15;
|
||||
|
||||
int ctr = 0;
|
||||
AddSeeds(result, (lo * RLag1IVs) % RLag0, first, third, ref ctr);
|
||||
|
||||
@@ -29,9 +29,9 @@ public static int GetSeedsIVs(Span<uint> result, uint hp, uint atk, uint def, ui
|
||||
/// </summary>
|
||||
public static int GetSeedsIVs(Span<uint> result, uint first, uint third)
|
||||
{
|
||||
ulong tmp = (ulong)(((first - (third * RMult2)) >> 16) & 0xFFFF) * RLag0;
|
||||
var lo = (uint)((tmp + RLower) >> 15);
|
||||
var up = (uint)((tmp + RUpper) >> 15);
|
||||
uint tmp = ((first - (third * RMult2)) >> 16) * RLag0;
|
||||
uint lo = (tmp + RLower) >> 15;
|
||||
uint up = (tmp + RUpper) >> 15;
|
||||
|
||||
int ctr = 0;
|
||||
AddSeeds(result, (lo * RLag1IVs) % RLag0, first, third, ref ctr);
|
||||
|
||||
@@ -243,6 +243,18 @@ public static int GetSeeds(Span<uint> result, uint hp, uint atk, uint def, uint
|
||||
return GetSeedsIVs(result, first, second);
|
||||
}
|
||||
|
||||
// RNG IVs Constants (bounding the second variable)
|
||||
// https://github.com/StarfBerry/PokeRNG/blob/1e9b9ddf2494837c7d6704c7b8a3831f644bdea9/Recovery/LCG_Recovery.py#L229
|
||||
private const uint Lag0 = 0xE8D1; // 59601
|
||||
private const uint Lag1 = 0x5F47; // -35210 mod 59601
|
||||
private const uint RLower = 0x55FF8537; // ((-0x92D27AC8F311 + 0xffff_ffff) >> 16) + (59601 << 16)
|
||||
private const uint RUpper = 0x55FFBC6D; // (-0x92D14392F311 >> 16) + (59601 << 16)
|
||||
|
||||
private const uint Lag0IVs = 0x44C5; // 17605
|
||||
private const uint Lag1IVs = 0xE8D1; // 59601
|
||||
private const uint RLowerIVs = 0x1E694392; // (0x1E68C392F311 + 0x7fff_ffff) >> 16
|
||||
private const uint RUpperIVs = 0x1E69FAC8; // (0x1E69FAC8F311 >> 16)
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the origin seeds for two 16 bit rand() calls
|
||||
/// </summary>
|
||||
@@ -252,15 +264,33 @@ public static int GetSeeds(Span<uint> result, uint hp, uint atk, uint def, uint
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||
{
|
||||
ulong t = second - (first * Mult) - Sub;
|
||||
ulong kmax = (Base - t) >> 32;
|
||||
// `tmp` must be 64bit to avoid overflow via addition with the LOWER and UPPER constants
|
||||
ulong tmp = ((first - (second * rMult)) >> 16) * Lag0;
|
||||
uint lo = (uint)((tmp + RLower) >> 16);
|
||||
uint up = (uint)((tmp + RUpper) >> 16);
|
||||
|
||||
int ctr = 0;
|
||||
for (ulong k = 0; k <= kmax; k++, t += 0x1_0000_0000) // at most 4 iterations
|
||||
|
||||
// each loop performs at most 2 iterations
|
||||
uint low = (lo * Lag1) % Lag0;
|
||||
do
|
||||
{
|
||||
if (t % Mult < 0x1_0000)
|
||||
result[ctr++] = Prev(first | (ushort)(t / Mult));
|
||||
}
|
||||
uint seed = Prev(second | low);
|
||||
if ((seed & 0xffff0000) == first)
|
||||
result[ctr++] = Prev(seed);
|
||||
} while ((low += Lag0) < 0x1_0000);
|
||||
|
||||
if (lo == up)
|
||||
return ctr;
|
||||
|
||||
// true in around 22% of cases
|
||||
low = (up * Lag1) % Lag0;
|
||||
do
|
||||
{
|
||||
uint seed = Prev(second | low);
|
||||
if ((seed & 0xffff0000) == first)
|
||||
result[ctr++] = Prev(seed);
|
||||
} while ((low += Lag0) < 0x1_0000);
|
||||
return ctr;
|
||||
}
|
||||
|
||||
@@ -273,19 +303,50 @@ public static int GetSeeds(Span<uint> result, uint first, uint second)
|
||||
/// <returns>Count of results added to <see cref="result"/></returns>
|
||||
public static int GetSeedsIVs(Span<uint> result, uint first, uint second)
|
||||
{
|
||||
ulong t = (second - (first * Mult) - Sub) & 0x7FFF_FFFF;
|
||||
ulong kmax = (Base - t) >> 31;
|
||||
ulong tmp = ((((rMult * second) - first) >> 16) & 0xFFFF) * Lag1IVs;
|
||||
|
||||
var lo = (uint)((tmp + RLowerIVs) >> 15) * Lag0IVs;
|
||||
var mi = lo + Lag0IVs;
|
||||
var up = (uint)((tmp + RUpperIVs) >> 15) * Lag0IVs;
|
||||
|
||||
int ctr = 0;
|
||||
for (ulong k = 0; k <= kmax; k++, t += 0x8000_0000) // at most 7 iterations
|
||||
// each loop performs at most 2 iterations
|
||||
uint low = lo % Lag1IVs;
|
||||
do
|
||||
{
|
||||
if (t % Mult < 0x1_0000)
|
||||
{
|
||||
var s = Prev(first | (ushort)(t / Mult));
|
||||
result[ctr++] = s;
|
||||
result[ctr++] = s ^ 0x8000_0000; // top bit flip
|
||||
}
|
||||
}
|
||||
uint seed = Prev(second | low);
|
||||
if ((seed & 0x7fff0000) != first)
|
||||
continue;
|
||||
seed = Prev(seed);
|
||||
result[ctr++] = seed;
|
||||
result[ctr++] = seed ^ 0x80000000;
|
||||
} while ((low += Lag1IVs) < 0x1_0000);
|
||||
|
||||
low = mi % Lag1IVs;
|
||||
do
|
||||
{
|
||||
uint seed = Prev(second | low);
|
||||
if ((seed & 0x7fff0000) != first)
|
||||
continue;
|
||||
seed = Prev(seed);
|
||||
result[ctr++] = seed;
|
||||
result[ctr++] = seed ^ 0x80000000;
|
||||
} while ((low += Lag1IVs) < 0x1_0000);
|
||||
|
||||
if (mi == up)
|
||||
return ctr;
|
||||
|
||||
// true in around 43% of cases
|
||||
low = up % Lag1IVs;
|
||||
do
|
||||
{
|
||||
uint seed = Prev(second | low);
|
||||
if ((seed & 0x7fff0000) != first)
|
||||
continue;
|
||||
seed = Prev(seed);
|
||||
result[ctr++] = seed;
|
||||
result[ctr++] = seed ^ 0x80000000;
|
||||
} while ((low += Lag1IVs) < 0x1_0000);
|
||||
return ctr;
|
||||
}
|
||||
|
||||
@@ -297,12 +358,12 @@ public static int GetSeedsChannel(Span<uint> result, uint hp, uint atk, uint def
|
||||
{
|
||||
// https://github.com/StarfBerry/PokeRNG/blob/main/Recovery/LCG_Recovery.py
|
||||
// First row of the BKZ-reduced matrix
|
||||
const long r0 = -002_528_644;
|
||||
const long r1 = -024_142_902;
|
||||
const long r2 = 052_961_366;
|
||||
const long r3 = 007_565_619;
|
||||
const long r4 = 024_945_956;
|
||||
const long r5 = -099_942_057;
|
||||
const int r0 = -002_528_644;
|
||||
const int r1 = -024_142_902;
|
||||
const int r2 = 052_961_366;
|
||||
const int r3 = 007_565_619;
|
||||
const int r4 = 024_945_956;
|
||||
const int r5 = -099_942_057;
|
||||
|
||||
// Constants to bound the variables in the linear combinations for calculating potential solutions
|
||||
const long lower0 = 0x2A_B966_D1C2;
|
||||
@@ -318,6 +379,8 @@ public static int GetSeedsChannel(Span<uint> result, uint hp, uint atk, uint def
|
||||
const long upper4 = 0x10_9800_0000;
|
||||
const long upper5 = -0x07_E800_0000;
|
||||
|
||||
// IVs 20/12/6/0/0/3 overflows x5Max `int`, so all are kept as `long` as a precaution.
|
||||
|
||||
long f0 = ((-10L * hp) + (23L * atk) - def - (15L * spe) + (52L * spa) - (53L * spd)) << 27;
|
||||
long x0Min = ((f0 + upper0) >> 32) * r0; // LOWER and UPPER are inverted relative to xmin and xmax because R0 is negative (same with R1 and R5)
|
||||
long x0Max = ((f0 + lower0) >> 32) * r0;
|
||||
|
||||
@@ -307,6 +307,7 @@ private static bool GetChannelMatch(Span<uint> seeds, PKM pk, uint top, uint bot
|
||||
if (version is not (R or S))
|
||||
return GetNonMatch(out pidiv);
|
||||
|
||||
// failed implementation of the shiny lock due to operator precedence
|
||||
var undo = (top >> 16) ^ 0x8000;
|
||||
if ((undo > 7 ? 0 : 1) != ((bot >> 16) ^ pk.SID16 ^ 40122))
|
||||
top = (undo << 16);
|
||||
|
||||
@@ -304,7 +304,7 @@ public void MRNGReversalRanchRecovery(uint iv32, int recovered)
|
||||
[InlineData(0x00000000, 1, false)]
|
||||
[InlineData(0x00000001, 7)]
|
||||
[InlineData(0x00000087, 9)]
|
||||
[InlineData(0x00000888, 10, false)]
|
||||
[InlineData(0x00000888, 10)]
|
||||
[InlineData(0x0000F525, 11)]
|
||||
[InlineData(0x00019994, 12)]
|
||||
public void ChannelLatticeRecovery(uint iv32, int recovered, bool isObtainable = true)
|
||||
@@ -327,7 +327,11 @@ public void ChannelLatticeRecovery(uint iv32, int recovered, bool isObtainable =
|
||||
var seed = ChannelJirachi.SkipToIVs(origin);
|
||||
var result = XDRNG.GetSequentialIV32(seed);
|
||||
result.Should().Be(iv32);
|
||||
possible |= ChannelJirachi.IsPossible(seed);
|
||||
|
||||
// check if any of the PID/IV origin seeds can unroll to a menu seed for the player to hit
|
||||
var check = ChannelJirachi.GetPossible(origin);
|
||||
if (check.Pattern != ChannelJirachiRandomResult.None)
|
||||
possible = true;
|
||||
}
|
||||
possible.Should().Be(isObtainable);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user