mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 22:54:14 +00:00
Add PIDIV matching
Includes 1 test for each pkm pidiv type, haven't added absolutely every method possible but it's enough for now
This commit is contained in:
parent
2f0eedbe7c
commit
82375ca464
7 changed files with 350 additions and 8 deletions
|
@ -18,9 +18,9 @@ namespace PKHeX.Core
|
|||
public static string V189 {get; set;} = "Analysis not available for this Pokémon.";
|
||||
/// <summary>Format text for exporting a legality check result.</summary>
|
||||
public static string V196 {get; set;} = "{0}: {1}";
|
||||
/// <summary>Format text for exporting a legality check result for an invalid Move.</summary>
|
||||
/// <summary>Format text for exporting a legality check result for a Move.</summary>
|
||||
public static string V191 {get; set;} = "{0} Move {1}: {2}";
|
||||
/// <summary>Format text for exporting a legality check result for an invalid Relearn Move.</summary>
|
||||
/// <summary>Format text for exporting a legality check result for a Relearn Move.</summary>
|
||||
public static string V192 {get; set;} = "{0} Relearn Move {1}: {2}";
|
||||
/// <summary>Format text for exporting the type of Encounter that was matched for the the <see cref="PKM"/></summary>
|
||||
public static string V195 {get; set;} = "Encounter Type: {0}";
|
||||
|
|
236
PKHeX/Legality/RNG/MethodFinder.cs
Normal file
236
PKHeX/Legality/RNG/MethodFinder.cs
Normal file
|
@ -0,0 +1,236 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing logic to obtain a PKM's PIDIV method.
|
||||
/// </summary>
|
||||
public static class MethodFinder
|
||||
{
|
||||
/// <summary>
|
||||
/// Analyzes a <see cref="PKM"/> to find a matching PIDIV method.
|
||||
/// </summary>
|
||||
/// <param name="pk">Input <see cref="PKM"/>.</param>
|
||||
/// <returns><see cref="PIDIV"/> object containing seed and method info.</returns>
|
||||
public static PIDIV Analyze(PKM pk)
|
||||
{
|
||||
if (pk.Format < 3)
|
||||
return AnalyzeGB(pk);
|
||||
var pid = pk.PID;
|
||||
var top = pid >> 16;
|
||||
var bot = pid & 0xFFFF;
|
||||
|
||||
var iIVs = pk.IVs;
|
||||
var IVs = new uint[6];
|
||||
for (int i = 0; i < 6; i++)
|
||||
IVs[i] = (uint)iIVs[i];
|
||||
|
||||
PIDIV pidiv;
|
||||
if (getLCRNGMatch(top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (getXDRNGMatch(top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
|
||||
// Special cases
|
||||
if (getChannelMatch(top, bot, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (getMG4Match(pid, IVs, out pidiv))
|
||||
return pidiv;
|
||||
if (getModifiedPID(pid, out pidiv))
|
||||
return pidiv;
|
||||
if (pid <= 0xFF && getCuteCharmMatch(pk, pid, out pidiv))
|
||||
return pidiv;
|
||||
|
||||
return pidiv; // no match
|
||||
}
|
||||
|
||||
private static bool getLCRNGMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv)
|
||||
{
|
||||
var reg = getSeedsFromPID(RNG.LCRNG, top, bot);
|
||||
foreach (var seed in reg)
|
||||
{
|
||||
// A and B are already used by PID
|
||||
var B = RNG.LCRNG.Advance(seed, 2);
|
||||
|
||||
// Method 1/2/4 can use 3 different RNG frames
|
||||
var C = RNG.LCRNG.Next(B);
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
|
||||
if (getIVs(C >> 16, D >> 16).SequenceEqual(IVs)) // ABCD
|
||||
{
|
||||
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_1};
|
||||
return true;
|
||||
}
|
||||
|
||||
var E = RNG.LCRNG.Next(D);
|
||||
if (getIVs(D >> 16, E >> 16).SequenceEqual(IVs)) // ABDE
|
||||
{
|
||||
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_2};
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getIVs(C >> 16, E >> 16).SequenceEqual(IVs)) // ABCE
|
||||
{
|
||||
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_4};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
private static bool getXDRNGMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv)
|
||||
{
|
||||
var xdc = getSeedsFromPID(RNG.XDRNG, bot, top);
|
||||
foreach (var seed in xdc)
|
||||
{
|
||||
var C = RNG.XDRNG.Reverse(seed, 3);
|
||||
|
||||
var D = RNG.XDRNG.Next(C);
|
||||
var E = RNG.XDRNG.Next(D);
|
||||
|
||||
if (!getIVs(D >> 16, E >> 16).SequenceEqual(IVs))
|
||||
continue;
|
||||
|
||||
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.XDRNG, Type = PIDType.XDC};
|
||||
return true;
|
||||
}
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
private static bool getChannelMatch(uint top, uint bot, uint[] IVs, out PIDIV pidiv)
|
||||
{
|
||||
var channel = getSeedsFromPID(RNG.XDRNG, bot, top ^ 0x8000);
|
||||
foreach (var seed in channel)
|
||||
{
|
||||
var E = RNG.XDRNG.Advance(seed, 5);
|
||||
if (!getIVs(RNG.XDRNG, E).SequenceEqual(IVs))
|
||||
continue;
|
||||
|
||||
pidiv = new PIDIV {OriginSeed = RNG.XDRNG.Prev(seed), RNG = RNG.XDRNG, Type = PIDType.Channel};
|
||||
return true;
|
||||
}
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
private static bool getMG4Match(uint pid, uint[] IVs, out PIDIV pidiv)
|
||||
{
|
||||
uint mg4Rev = RNG.ARNG.Prev(pid);
|
||||
var mg4 = getSeedsFromPID(RNG.LCRNG, mg4Rev >> 16, mg4Rev & 0xFFFF);
|
||||
foreach (var seed in mg4)
|
||||
{
|
||||
var B = RNG.LCRNG.Advance(seed, 2);
|
||||
var C = RNG.LCRNG.Next(B);
|
||||
var D = RNG.LCRNG.Next(C);
|
||||
if (!getIVs(C >> 16, D >> 16).SequenceEqual(IVs))
|
||||
continue;
|
||||
|
||||
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.G4AntiShiny};
|
||||
return true;
|
||||
}
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
private static bool getModifiedPID(uint pid, out PIDIV pidiv)
|
||||
{
|
||||
// generation 5 shiny PIDs
|
||||
// todo
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
private static bool getCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv)
|
||||
{
|
||||
int genderValue = pk.Gender;
|
||||
switch (genderValue)
|
||||
{
|
||||
case 2: break; // can't cute charm a genderless pkm
|
||||
case 0: // male
|
||||
var gr = pk.PersonalInfo.Gender;
|
||||
if (254 <= gr) // no modification for PID
|
||||
break;
|
||||
if (pk.PID < gr)
|
||||
break;
|
||||
if (pk.PID >= gr + 25)
|
||||
break;
|
||||
|
||||
pidiv = new PIDIV { OriginSeed = 0, RNG = RNG.LCRNG, Type = PIDType.CuteCharm };
|
||||
return true;
|
||||
case 1: // female
|
||||
if (pk.PID >= 25)
|
||||
break; // nope
|
||||
if (254 <= pk.PersonalInfo.Gender) // no modification for PID
|
||||
break;
|
||||
|
||||
pidiv = new PIDIV { OriginSeed = 0, RNG = RNG.LCRNG, Type = PIDType.CuteCharm };
|
||||
return true;
|
||||
}
|
||||
pidiv = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PIDIV AnalyzeGB(PKM pk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<uint> getSeedsFromPID(RNG method, uint top, uint bot)
|
||||
{
|
||||
uint cmp = top << 16;
|
||||
uint start = bot << 16;
|
||||
uint end = start | 0xFFFF;
|
||||
for (uint i = start; i <= end; i++)
|
||||
if ((method.Next(i) & 0xFFFF0000) == cmp)
|
||||
yield return method.Prev(i);
|
||||
}
|
||||
private static IEnumerable<uint> getSeedsFromIVs(RNG method, uint top, uint bot)
|
||||
{
|
||||
uint cmp = top << 16 & 0x7FFF0000;
|
||||
uint start = bot << 16 & 0x7FFF0000;
|
||||
uint end = start | 0xFFFF;
|
||||
for (uint i = start; i <= end; i++)
|
||||
if ((method.Next(i) & 0x7FFF0000) == cmp)
|
||||
yield return method.Prev(i);
|
||||
|
||||
start |= 0x80000000;
|
||||
end |= 0x80000000;
|
||||
for (uint i = start; i <= end; i++)
|
||||
if ((method.Next(i) & 0x7FFF0000) == cmp)
|
||||
yield return method.Prev(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates IVs from 2 RNG calls using 15 bits of each to generate 6 IVs (5bits each).
|
||||
/// </summary>
|
||||
/// <param name="r1">First rand frame</param>
|
||||
/// <param name="r2">Second rand frame</param>
|
||||
/// <returns>Array of 6 IVs</returns>
|
||||
private static uint[] getIVs(uint r1, uint r2)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
r1 & 31,
|
||||
r1 >> 5 & 31,
|
||||
r1 >> 10 & 31,
|
||||
r2 & 31,
|
||||
r2 >> 5 & 31,
|
||||
r2 >> 10 & 31,
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Generates an IV for each RNG call using the top 5 bits of frame seeds.
|
||||
/// </summary>
|
||||
/// <param name="method">RNG advancement method</param>
|
||||
/// <param name="seed">RNG seed</param>
|
||||
/// <returns>Array of 6 IVs</returns>
|
||||
private static uint[] getIVs(RNG method, uint seed)
|
||||
{
|
||||
uint[] ivs = new uint[6];
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
seed = method.Next(seed);
|
||||
ivs[i] = seed >> 27;
|
||||
}
|
||||
return ivs;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,5 +7,8 @@
|
|||
|
||||
/// <summary> The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first). </summary>
|
||||
public uint OriginSeed;
|
||||
|
||||
/// <summary> Type of PIDIV correlation </summary>
|
||||
public PIDType Type;
|
||||
}
|
||||
}
|
46
PKHeX/Legality/RNG/PIDType.cs
Normal file
46
PKHeX/Legality/RNG/PIDType.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public enum PIDType
|
||||
{
|
||||
/// <summary> No match </summary>
|
||||
None,
|
||||
|
||||
/// <summary> Method 1 Variants (H1/J/K) </summary>
|
||||
Method_1,
|
||||
/// <summary> Method H2 </summary>
|
||||
Method_2,
|
||||
/// <summary> Method H4 </summary>
|
||||
Method_4,
|
||||
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID restricted to 16bit Origin Seed
|
||||
/// </summary>
|
||||
BACD_R,
|
||||
/// <summary>
|
||||
/// Event Reversed Order PID without Origin Seed restrictions
|
||||
/// </summary>
|
||||
BACD_U,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Cute Charm forced 8 bit
|
||||
/// </summary>
|
||||
CuteCharm,
|
||||
/// <summary>
|
||||
/// Generation 4 Chained Shiny
|
||||
/// </summary>
|
||||
ChainShiny,
|
||||
|
||||
// XDRNG Based
|
||||
XDC,
|
||||
Channel,
|
||||
|
||||
// ARNG Based
|
||||
G4AntiShiny,
|
||||
|
||||
// Formulaic
|
||||
G5AntiShiny,
|
||||
|
||||
// Specified
|
||||
Static,
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
public static readonly RNG ARNG = new RNG(0x6C078965, 0x00000001, 0x9638806D, 0x69C77F93);
|
||||
|
||||
private readonly uint Mult, Add, rMult, rAdd;
|
||||
protected RNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
|
||||
private RNG(uint f_mult, uint f_add, uint r_mult, uint r_add)
|
||||
{
|
||||
Mult = f_mult;
|
||||
Add = f_add;
|
||||
|
@ -16,15 +16,15 @@
|
|||
}
|
||||
|
||||
public uint Next(uint seed) => seed * Mult + Add;
|
||||
private uint Prev(uint seed) => seed * rMult + rAdd;
|
||||
public uint Prev(uint seed) => seed * rMult + rAdd;
|
||||
|
||||
private uint Advance(uint seed, int frames)
|
||||
public uint Advance(uint seed, int frames)
|
||||
{
|
||||
for (int i = 0; i < frames; i++)
|
||||
seed = Next(seed);
|
||||
return seed;
|
||||
}
|
||||
private uint Reverse(uint seed, int frames)
|
||||
public uint Reverse(uint seed, int frames)
|
||||
{
|
||||
for (int i = 0; i < frames; i++)
|
||||
seed = Prev(seed);
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
<Compile Include="Legality\Structures\IRibbonSet.cs" />
|
||||
<Compile Include="Legality\Structures\Learnset.cs" />
|
||||
<Compile Include="Legality\Structures\Nature.cs" />
|
||||
<Compile Include="Legality\Structures\PIDIV.cs" />
|
||||
<Compile Include="Legality\RNG\PIDIV.cs" />
|
||||
<Compile Include="Legality\Structures\SlotType.cs" />
|
||||
<Compile Include="Legality\Structures\TradebackType.cs" />
|
||||
<Compile Include="Legality\Structures\ValidEncounterMoves.cs" />
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using PKHeX.Core;
|
||||
|
||||
namespace PKHeX.Tests.PKM
|
||||
{
|
||||
[TestClass]
|
||||
public class PKMTests
|
||||
{
|
||||
const string DateTestCategory = "PKM Date Tests";
|
||||
private const string DateTestCategory = "PKM Date Tests";
|
||||
private const string PIDIVTestCategory = "PKM PIDIV Matching Tests";
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory(DateTestCategory)]
|
||||
|
@ -125,5 +127,60 @@ namespace PKHeX.Tests.PKM
|
|||
Assert.AreEqual(now.Month, pk.EggMetMonth, "Egg_Month was not correctly set");
|
||||
Assert.AreEqual(now.Year - 2000, pk.EggMetYear, "Egg_Year was not correctly set");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory(PIDIVTestCategory)]
|
||||
public void PIDIVMatchingTest()
|
||||
{
|
||||
// IVs are stored HP/ATK/DEF/SPE/SPA/SPD
|
||||
var pk1 = new PK3
|
||||
{
|
||||
PID = 0xE97E0000,
|
||||
IVs = new[] {17, 19, 20, 16, 13, 12}
|
||||
};
|
||||
Assert.AreEqual(PIDType.Method_1, MethodFinder.Analyze(pk1)?.Type, "Unable to match PID to Method 1 spread");
|
||||
var pk2 = new PK3
|
||||
{
|
||||
PID = 0x5271E97E,
|
||||
IVs = new[] {02, 18, 03, 12, 22, 24}
|
||||
};
|
||||
Assert.AreEqual(PIDType.Method_2, MethodFinder.Analyze(pk2)?.Type, "Unable to match PID to Method 2 spread");
|
||||
var pk4 = new PK3
|
||||
{
|
||||
PID = 0x31B05271,
|
||||
IVs = new[] {02, 18, 03, 05, 30, 11}
|
||||
};
|
||||
Assert.AreEqual(PIDType.Method_4, MethodFinder.Analyze(pk4)?.Type, "Unable to match PID to Method 4 spread");
|
||||
|
||||
var pk3 = new PK3
|
||||
{
|
||||
PID = 0x0985A297,
|
||||
IVs = new[] {06, 01, 00, 07, 17, 07}
|
||||
};
|
||||
Assert.AreEqual(PIDType.XDC, MethodFinder.Analyze(pk3)?.Type, "Unable to match PID to XDC spread");
|
||||
|
||||
var pkC = new PK3
|
||||
{
|
||||
PID = 0x9E27D2F6,
|
||||
IVs = new[] {04, 15, 21, 14, 18, 29}
|
||||
};
|
||||
Assert.AreEqual(PIDType.Channel, MethodFinder.Analyze(pkC)?.Type, "Unable to match PID to Channel spread");
|
||||
|
||||
var pkCC = new PK4
|
||||
{
|
||||
PID = 0x00000037,
|
||||
IVs = new[] {16, 13, 12, 02, 18, 03},
|
||||
Species = 1,
|
||||
Gender = 0,
|
||||
};
|
||||
Assert.AreEqual(PIDType.CuteCharm, MethodFinder.Analyze(pkCC)?.Type, "Unable to match PID to Cute Charm spread");
|
||||
|
||||
var pkASR = new PK4
|
||||
{
|
||||
PID = 0x07578CB7, // 0x5271E97E rerolled
|
||||
IVs = new[] {16, 13, 12, 02, 18, 03},
|
||||
};
|
||||
Assert.AreEqual(PIDType.G4AntiShiny, MethodFinder.Analyze(pkASR)?.Type, "Unable to match PID to Antishiny4 spread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue