mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-10-01 12:36:40 -05:00
Pokewalker: Handle 539 stroll, revise signatures
Was missing the worst case scenario; ran a bruteforce script to find a 539 after adding the final logic. We try to do the least amount of cpu instructions so this is probably the better arrangement anyway. Revise the method signatures to exclude the scratchspace span reuse (not-obvious behavior). Just stackalloc a new span. Add seed indication to legality formatting. #4416
This commit is contained in:
@@ -116,7 +116,7 @@ private uint GetIV32(EncounterCriteria criteria)
|
||||
if (criteria.IsSpecifiedIVsAll()) // Don't trust that the requirements are valid
|
||||
{
|
||||
criteria.GetCombinedIVs(out var iv1, out var iv2);
|
||||
var seed = PokewalkerRNG.GetFirstSeed(iv1, iv2);
|
||||
var seed = PokewalkerRNG.GetLeastEffortSeed(iv1, iv2);
|
||||
if (seed.Type != PokewalkerSeedType.None)
|
||||
return criteria.GetCombinedIVs();
|
||||
}
|
||||
@@ -142,11 +142,11 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsMatchSeed(PKM pk)
|
||||
private static bool IsMatchSeed(PKM pk)
|
||||
{
|
||||
Span<int> ivs = stackalloc int[6];
|
||||
pk.GetIVs(ivs);
|
||||
var seed = PokewalkerRNG.GetFirstSeed(ivs);
|
||||
var seed = PokewalkerRNG.GetLeastEffortSeed(ivs);
|
||||
return seed.Type != PokewalkerSeedType.None;
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ public EncounterMatchRating GetMatchRating(PKM pk)
|
||||
return EncounterMatchRating.Match;
|
||||
}
|
||||
|
||||
private bool IsMatchPartial(PKM pk) => pk.Ball != (byte)Ball.Poke || !IsMatchSeed(pk);
|
||||
private static bool IsMatchPartial(PKM pk) => pk.Ball != (byte)Ball.Poke || !IsMatchSeed(pk);
|
||||
#endregion
|
||||
|
||||
public bool IsCompatible(PIDType type, PKM pk)
|
||||
|
||||
@@ -117,7 +117,17 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
}
|
||||
lines.Add(type);
|
||||
if (pidiv.NoSeed)
|
||||
{
|
||||
if (pidiv.Type is PIDType.Pokewalker)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
var result = PokewalkerRNG.GetLeastEffortSeed((uint)pk.IV_HP, (uint)pk.IV_ATK, (uint)pk.IV_DEF, (uint)pk.IV_SPA, (uint)pk.IV_SPD, (uint)pk.IV_SPE);
|
||||
var line = string.Format(L_FOriginSeed_0, result.Seed.ToString("X8"));
|
||||
line += $" [{result.Type} @ {result.PriorPoke}]";
|
||||
lines.Add(line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pidiv.IsSeed64())
|
||||
{
|
||||
|
||||
@@ -99,29 +99,32 @@ public static bool IsSeedFormatNoStroll(uint seed)
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first valid seed for the given Pokéwalker IVs.
|
||||
/// Finds an initial seed for the Pokéwalker IVs with the least amount of capture-IVs advances needed.
|
||||
/// </summary>
|
||||
public static PokewalkerSeedResult GetFirstSeed(Span<int> ivs)
|
||||
/// <param name="ivs">IVs in order (speed last).</param>
|
||||
public static PokewalkerSeedResult GetLeastEffortSeed(Span<int> ivs)
|
||||
{
|
||||
var tmp = MemoryMarshal.Cast<int, uint>(ivs);
|
||||
return GetFirstSeed(tmp, tmp[0], tmp[1], tmp[2], tmp[4], tmp[5], spe: tmp[3]);
|
||||
return GetLeastEffortSeed(tmp[0], tmp[1], tmp[2], tmp[4], tmp[5], spe: tmp[3]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetFirstSeed(Span{int})"/>
|
||||
public static PokewalkerSeedResult GetFirstSeed(Span<uint> tmpIVs,
|
||||
uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
|
||||
/// <inheritdoc cref="LCRNGReversal.GetSeedsIVs(Span{uint},uint, uint)"/>
|
||||
public static PokewalkerSeedResult GetLeastEffortSeed(uint hp, uint atk, uint def, uint spa, uint spd, uint spe)
|
||||
{
|
||||
Span<uint> result = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
uint first = (hp | (atk << 5) | (def << 10)) << 16;
|
||||
uint second = (spe | (spa << 5) | (spd << 10)) << 16;
|
||||
return GetFirstSeed(tmpIVs, first, second);
|
||||
return GetLeastEffortSeed(result, first, second);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetFirstSeed(Span{int})"/>
|
||||
public static PokewalkerSeedResult GetFirstSeed(uint first, uint second)
|
||||
=> GetFirstSeed(stackalloc uint[LCRNG.MaxCountSeedsIV], first, second);
|
||||
/// <inheritdoc cref="GetLeastEffortSeed(Span{int})"/>
|
||||
/// <inheritdoc cref="LCRNGReversal.GetSeedsIVs(Span{uint},uint, uint)"/>
|
||||
public static PokewalkerSeedResult GetLeastEffortSeed(uint first, uint second)
|
||||
=> GetLeastEffortSeed(stackalloc uint[LCRNG.MaxCountSeedsIV], first, second);
|
||||
|
||||
/// <inheritdoc cref="GetFirstSeed(Span{int})"/>
|
||||
public static PokewalkerSeedResult GetFirstSeed(Span<uint> result, uint first, uint second)
|
||||
/// <inheritdoc cref="GetLeastEffortSeed(Span{int})"/>
|
||||
/// <inheritdoc cref="LCRNGReversal.GetSeedsIVs(Span{uint},uint, uint)"/>
|
||||
public static PokewalkerSeedResult GetLeastEffortSeed(Span<uint> result, uint first, uint second)
|
||||
{
|
||||
// When generating a set of Pokéwalker Pokémon (and their IVs), the game does the following logic:
|
||||
// If the player begins a stroll, generate an initial seed based on seconds elapsed in the day (< 86400) and 3 slots.
|
||||
@@ -146,15 +149,25 @@ public static PokewalkerSeedResult GetFirstSeed(Span<uint> result, uint first, u
|
||||
// Check the [no-stroll] case.
|
||||
if (IsSeedFormatNoStroll(s))
|
||||
return new(s, priorPoke, PokewalkerSeedType.NoStroll);
|
||||
s = seed = LCRNG.Prev(seed);
|
||||
s = LCRNG.Prev(s);
|
||||
|
||||
// Check the [stroll] case.
|
||||
// Due to this backtracking algorithm, the first time we check won't be a valid (needs 3 advancements)
|
||||
if (priorPoke != 0 && IsSeedFormatStroll(s)) // don't check species; can be disassociated from slots.
|
||||
return new(s, --priorPoke, PokewalkerSeedType.Stroll); // decrement priorPoke back to 0-indexed
|
||||
seed = LCRNG.Prev(seed); // prep for next loop
|
||||
seed = LCRNG.Prev(s); // prep for next loop
|
||||
}
|
||||
}
|
||||
|
||||
// The above logic for Stroll checks for [0,n-1) due to the backtracking nature of the algorithm.
|
||||
// Check the last-empty-slot for the Stroll case.
|
||||
// That's catching 540 'mons... quite unlikely! But still possible, as not all spreads are obtainable.
|
||||
foreach (ref var seed in result)
|
||||
{
|
||||
var s = LCRNG.Prev(seed);
|
||||
if (IsSeedFormatStroll(s)) // don't check species; can be disassociated from slots.
|
||||
return new(s, boxCapacity - 1, PokewalkerSeedType.Stroll); // decrement priorPoke back to 0-indexed
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,11 +157,11 @@ public void PIDIVPokeSpotTest()
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(30, 31, 31, 14, 31, 31, 0x28070031, 24, (int)Species.Pikachu, PokewalkerCourse4.YellowForest, PokewalkerSeedType.NoStroll)]
|
||||
public void PokewalkerIVTest(uint hp, uint atk, uint def, uint spA, uint spD, uint spE, uint seed, ushort expect, ushort species, PokewalkerCourse4 course, PokewalkerSeedType type)
|
||||
[InlineData(30, 31, 31, 14, 31, 31, 0x28070031, 024, PokewalkerSeedType.NoStroll)]
|
||||
[InlineData(00, 00, 00, 05, 03, 09, 0x00011434, 539, PokewalkerSeedType.Stroll)]
|
||||
public void PokewalkerIVTest(uint hp, uint atk, uint def, uint spA, uint spD, uint spE, uint seed, ushort expect, PokewalkerSeedType type)
|
||||
{
|
||||
Span<uint> tmp = stackalloc uint[LCRNG.MaxCountSeedsIV];
|
||||
var result = PokewalkerRNG.GetFirstSeed(tmp, hp, atk, def, spA, spD, spE);
|
||||
var result = PokewalkerRNG.GetLeastEffortSeed(hp, atk, def, spA, spD, spE);
|
||||
result.Type.Should().Be(type);
|
||||
result.PriorPoke.Should().Be(expect);
|
||||
result.Seed.Should().Be(seed);
|
||||
|
||||
Reference in New Issue
Block a user