mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-12 23:37:07 +00:00
Improve hidden power mutation logic
No more heap algo span copy mutation. Finish distilling the problem down to bits.
This commit is contained in:
parent
81487af9ba
commit
10613fd070
1 changed files with 48 additions and 65 deletions
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -67,8 +66,8 @@ public static class HiddenPower
|
|||
/// <returns>True if the Hidden Power of the <see cref="IVs"/> is obtained, with or without modifications</returns>
|
||||
public static bool SetTypeGB(int hiddenPowerType, Span<int> IVs)
|
||||
{
|
||||
IVs[1] = (IVs[1] & ~3) | (hiddenPowerType >> 2);
|
||||
IVs[2] = (IVs[2] & ~3) | (hiddenPowerType & 3);
|
||||
IVs[1] = (IVs[1] & 0b1100) | (hiddenPowerType >> 2);
|
||||
IVs[2] = (IVs[2] & 0b1100) | (hiddenPowerType & 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -109,76 +108,56 @@ public static class HiddenPower
|
|||
}
|
||||
|
||||
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
|
||||
Span<int> scratch = stackalloc int[IVs.Length];
|
||||
Span<int> result = stackalloc int[IVs.Length];
|
||||
var success = GetSuggestedHiddenPowerIVs(hpVal, IVs, scratch, result);
|
||||
if (!success)
|
||||
var bits = GetSuggestedHiddenPowerIVs(hpVal, IVs);
|
||||
if (bits == NoResult)
|
||||
return false; // can't force hidden power?
|
||||
|
||||
// set IVs back to array
|
||||
result.CopyTo(IVs);
|
||||
ForceLowBits(IVs, bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Non-recursive https://en.wikipedia.org/wiki/Heap%27s_algorithm
|
||||
private static bool GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> original, Span<int> ivs, Span<int> best)
|
||||
private const byte NoResult = byte.MaxValue;
|
||||
|
||||
private static byte GetSuggestedHiddenPowerIVs(int hpVal, ReadOnlySpan<int> IVs)
|
||||
{
|
||||
const int max = 31;
|
||||
// Iterate through all bit combinations that yield our Hidden Power Type.
|
||||
// There's at most 5 we need to check, so brute force is fine.
|
||||
// Prefer the least amount of IVs changed (31 -> 30).
|
||||
|
||||
// Get a list of indexes that can be mutated
|
||||
Span<int> indexes = stackalloc int[original.Length];
|
||||
int flaw = 0;
|
||||
for (int i = 0; i < original.Length; i++)
|
||||
{
|
||||
if (original[i] == max)
|
||||
indexes[flaw++] = i;
|
||||
}
|
||||
indexes = indexes[..flaw];
|
||||
Span<int> c = stackalloc int[indexes.Length];
|
||||
// Get the starting index from our 64 possible bit states.
|
||||
int index = SixBitType.IndexOf((byte)hpVal);
|
||||
if (index == -1)
|
||||
return NoResult;
|
||||
|
||||
int mutated = c.Length + 1; // result tracking
|
||||
for (int i = 1; i < c.Length;)
|
||||
var bestIndex = NoResult;
|
||||
var bestIndexFlaws = 6;
|
||||
do
|
||||
{
|
||||
ref int ci = ref c[i];
|
||||
if (i <= ci) // Reset the state and simulate popping the stack by incrementing the pointer.
|
||||
{
|
||||
ci = 0;
|
||||
++i;
|
||||
var flaws = GetFlawedBitCount(IVs, index);
|
||||
if (flaws >= bestIndexFlaws)
|
||||
continue;
|
||||
}
|
||||
|
||||
var x = (i & 1) * ci; // if lowest bit set, ci : 0 (branch-less)
|
||||
Swap(ref indexes[i], ref indexes[x]);
|
||||
|
||||
// Inlined continuance check
|
||||
original.CopyTo(ivs);
|
||||
var q = Math.Min(indexes.Length, mutated);
|
||||
for (var j = 0; j < q; j++)
|
||||
{
|
||||
ivs[indexes[j]] ^= 1;
|
||||
if (hpVal != GetType(ivs))
|
||||
continue;
|
||||
|
||||
var ct = j + 1;
|
||||
if (ct >= mutated)
|
||||
break; // any further flaws are always worse
|
||||
|
||||
mutated = ct;
|
||||
ivs.CopyTo(best);
|
||||
if (j == 0) // nothing will be better than only 1 flaw
|
||||
return true;
|
||||
break; // any further flaws are always worse
|
||||
}
|
||||
|
||||
ci++;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
return mutated <= c.Length; // did we actually find a suitable result?
|
||||
bestIndex = (byte)index;
|
||||
bestIndexFlaws = flaws;
|
||||
} while (++index < SixBitType.Length && SixBitType[index] == hpVal);
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap<T>(ref T a, ref T b) => (a, b) = (b, a);
|
||||
private static int GetFlawedBitCount(ReadOnlySpan<int> ivs, int bitValue)
|
||||
{
|
||||
const int max = 31;
|
||||
int flaws = 0;
|
||||
for (int i = 0; i < ivs.Length; i++)
|
||||
{
|
||||
var iv = ivs[i];
|
||||
if ((iv & 1) == (bitValue & (1 << i)))
|
||||
continue; // ok
|
||||
if (iv != max)
|
||||
return NoResult;
|
||||
flaws++;
|
||||
}
|
||||
return flaws;
|
||||
}
|
||||
|
||||
/// <summary>Calculate the Hidden Power Type of the entered IVs.</summary>
|
||||
/// <param name="type">Hidden Power Type</param>
|
||||
|
@ -188,17 +167,21 @@ public static class HiddenPower
|
|||
{
|
||||
if (context.Generation() <= 2)
|
||||
{
|
||||
ivs[1] = (ivs[1] & ~3) | (type >> 2);
|
||||
ivs[2] = (ivs[2] & ~3) | (type & 3);
|
||||
ivs[1] = (ivs[1] & 0b1100) | (type >> 2);
|
||||
ivs[2] = (ivs[2] & 0b1100) | (type & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bits = DefaultLowBits[type];
|
||||
for (int i = 0; i < 6; i++)
|
||||
ivs[i] = (ivs[i] & 0x1E) + ((bits >> i) & 1);
|
||||
ForceLowBits(ivs, DefaultLowBits[type]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ForceLowBits(Span<int> ivs, byte bits)
|
||||
{
|
||||
for (int i = 0; i < ivs.Length; i++)
|
||||
ivs[i] = (ivs[i] & 0b11110) | ((bits >> i) & 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hidden Power IV values (even or odd) to achieve a specified Hidden Power Type
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue