Improve RNG frame detection for gen4

stupid bruteforce for slot4->pk4
This commit is contained in:
Kurt 2022-09-30 19:26:59 -07:00
parent 65baf92608
commit 0bc7eeb5f5
5 changed files with 166 additions and 75 deletions

View file

@ -46,6 +46,22 @@ public sealed record EncounterSlot4 : EncounterSlot, IMagnetStatic, INumberedSlo
return base.GetMatchRating(pk);
}
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
int ctr = 0;
do
{
base.SetPINGA(pk, criteria);
var pidiv = MethodFinder.Analyze(pk);
var frames = FrameFinder.GetFrames(pidiv, pk);
foreach (var frame in frames)
{
if (frame.IsSlotCompatibile(this, pk))
return;
}
} while (ctr++ < 10_000);
}
private Ball GetRequiredBallValue()
{
if (Type is SlotType.BugContest)

View file

@ -48,17 +48,21 @@ public sealed class Frame
/// <returns>Slot number for this frame &amp; lead value.</returns>
public bool IsSlotCompatibile<T>(T slot, PKM pk) where T : EncounterSlot, IMagnetStatic, INumberedSlot, ISlotRNGType
{
if (FrameType != FrameType.MethodH && slot.Type is not (SlotType.HoneyTree or SlotType.BugContest)) // gen3 always does level rand
// The only level rand type slots are Honey Tree and National Park BCC
// Gen3 always does level rand, but the level ranges are same min,max.
if (FrameType != FrameType.MethodH)
{
bool hasLevelCall = slot.IsRandomLevel;
if (Lead.NeedsLevelCall() != hasLevelCall)
return false;
}
// Level is before Nature, but usually isn't varied. Check ESV calc first.
int s = GetSlot(slot);
if (s != slot.SlotNumber)
return false;
if (slot.Type is not (SlotType.HoneyTree))
{
int calcSlot = GetSlot(slot);
if (calcSlot != slot.SlotNumber)
return false;
}
// Check Level Now
int lvl = SlotRange.GetLevel(slot, Lead, RandLevel);

View file

@ -60,7 +60,6 @@ public static class FrameFinder
// Level
// Nature
// Current Seed of the frame is the Level Calc (frame before nature)
var list = new List<Frame>();
foreach (var f in frames)
{
bool noLead = !info.AllowLeads && f.Lead != LeadRequired.None;
@ -77,13 +76,11 @@ public static class FrameFinder
// Generate frames for other slots after the regular slots
if (info.AllowLeads && (f.Lead is LeadRequired.CuteCharm or LeadRequired.None))
list.Add(f);
}
foreach (var f in list)
{
var leadframes = GenerateLeadSpecificFrames3(f, info);
foreach (var frame in leadframes)
yield return frame;
{
var leadframes = GenerateLeadSpecificFrames3(f, info);
foreach (var frame in leadframes)
yield return frame;
}
}
}
@ -160,7 +157,6 @@ public static class FrameFinder
private static IEnumerable<Frame> RefineFrames4(IEnumerable<Frame> frames, FrameGenerator info)
{
var list = new List<Frame>();
foreach (var f in frames)
{
// Current Seed of the frame is the ESV.
@ -170,23 +166,25 @@ public static class FrameFinder
f.OriginSeed = LCRNG.Prev(f.Seed);
yield return f;
if (f.Lead == LeadRequired.None)
{
var leadframes = GenerateLeadSpecificFrames4(f, info);
foreach (var frame in leadframes)
yield return frame;
}
// Create a copy for level; shift ESV and origin back
var esv = f.OriginSeed >> 16;
var origin = LCRNG.Prev(f.OriginSeed);
var withLevel = info.GetFrame(f.Seed, f.Lead | LeadRequired.UsesLevelCall, esv, f.RandLevel, origin);
yield return withLevel;
if (f.Lead != LeadRequired.None)
continue;
// Generate frames for other slots after the regular slots
list.Add(f);
}
foreach (var f in list)
{
var leadframes = GenerateLeadSpecificFrames4(f, info);
foreach (var frame in leadframes)
yield return frame;
if (f.Lead == LeadRequired.None)
{
var leadframes = GenerateLeadSpecificFrames4(withLevel, info);
foreach (var frame in leadframes)
yield return frame;
}
}
}

View file

@ -0,0 +1,107 @@
namespace PKHeX.Core;
/// <summary>
/// Converts a slot random roll [0, 100) to a slot number.
/// </summary>
public static class SlotNumber
{
private const int Invalid = -1;
public static int GetHOldRod(uint roll) => roll switch
{
< 70 => 0, // 00,69 (70%)
<=99 => 1, // 70,99 (30%)
_ => Invalid,
};
public static int GetHGoodRod(uint roll) => roll switch
{
< 60 => 0, // 00,59 (60%)
< 80 => 1, // 60,79 (20%)
<=99 => 2, // 80,99 (20%)
_ => Invalid,
};
public static int GetHSuperRod(uint roll) => roll switch
{
< 40 => 0, // 00,39 (40%)
< 80 => 1, // 40,69 (40%)
< 95 => 2, // 70,94 (15%)
< 99 => 3, // 95,98 ( 4%)
99 => 4, // 99 ( 1%)
_ => Invalid,
};
public static int GetHSurf(uint roll) => roll switch
{
< 60 => 0, // 00,59 (60%)
< 90 => 1, // 60,89 (30%)
< 95 => 2, // 90,94 ( 5%)
< 99 => 3, // 95,98 ( 4%)
99 => 4, // 99 ( 1%)
_ => Invalid,
};
public static int GetHRegular(uint roll) => roll switch
{
< 20 => 0, // 00,19 (20%)
< 40 => 1, // 20,39 (20%)
< 50 => 2, // 40,49 (10%)
< 60 => 3, // 50,59 (10%)
< 70 => 4, // 60,69 (10%)
< 80 => 5, // 70,79 (10%)
< 85 => 6, // 80,84 ( 5%)
< 90 => 7, // 85,89 ( 5%)
< 94 => 8, // 90,93 ( 4%)
< 98 => 9, // 94,97 ( 4%)
< 99 => 10,// 98,98 ( 1%)
99 => 11,// 99 ( 1%)
_ => Invalid,
};
public static int GetJSuperRod(uint roll) => roll switch
{
< 40 => 0, // 00,39 (40%)
< 80 => 1, // 40,79 (40%)
< 95 => 2, // 80,94 (15%)
< 99 => 3, // 95,98 ( 4%)
99 => 4, // 99 ( 1%)
_ => Invalid,
};
public static int GetKSuperRod(uint roll) => roll switch
{
< 40 => 0, // 00,39 (40%)
< 70 => 1, // 40,69 (30%)
< 85 => 2, // 70,84 (15%)
< 95 => 3, // 85,94 (10%)
99 => 4, // 95 ( 5%)
_ => Invalid,
};
public static int GetKBCC(uint roll) => roll switch
{
>= 100 => Invalid,
>= 80 => 0, // 80,99 (20%)
>= 60 => 1, // 60,79 (20%)
>= 50 => 2, // 50,59 (10%)
>= 40 => 3, // 40,49 (10%)
>= 30 => 4, // 30,39 (10%)
>= 20 => 5, // 20,29 (10%)
>= 15 => 6, // 15,19 ( 5%)
>= 10 => 7, // 10,14 ( 5%)
>= 05 => 8, // 05,09 ( 5%)
>= 00 => 9, // 00,04 ( 5%)
};
public static int GetKHeadbutt(uint roll) => roll switch
{
< 50 => 0, // 00,49 (50%)
< 65 => 1, // 50,64 (15%)
< 80 => 2, // 65,79 (15%)
< 90 => 3, // 80,89 (10%)
< 95 => 4, // 90,94 ( 5%)
<=99 => 5, // 95,99 ( 5%)
_ => Invalid,
};
}

View file

@ -1,5 +1,5 @@
using System.Linq;
using static PKHeX.Core.SlotType;
using static PKHeX.Core.SlotNumber;
namespace PKHeX.Core;
@ -8,17 +8,6 @@ namespace PKHeX.Core;
/// </summary>
public static class SlotRange
{
private static readonly Range[] H_OldRod = GetRanges(70, 30);
private static readonly Range[] H_GoodRod = GetRanges(60, 20, 20);
private static readonly Range[] H_SuperRod = GetRanges(40, 40, 15, 4, 1);
private static readonly Range[] H_Surf = GetRanges(60, 30, 5, 4, 1);
private static readonly Range[] H_Regular = GetRanges(20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1);
private static readonly Range[] J_SuperRod = GetRanges(40, 40, 15, 4, 1);
private static readonly Range[] K_SuperRod = GetRanges(40, 30, 15, 10, 5);
private static readonly Range[] K_BCC = GetRanges(5,5,5,5, 10,10,10,10, 20,20).Reverse().ToArray();
private static readonly Range[] K_Headbutt = GetRanges(50, 15, 15, 10, 5, 5);
private const int Invalid = -1; // all slots are [0,X], unsigned. This will always result in a non-match.
/// <summary>
@ -43,12 +32,12 @@ public static class SlotRange
return type switch
{
Old_Rod => CalcSlot(ESV, H_OldRod),
Good_Rod => CalcSlot(ESV, H_GoodRod),
Super_Rod => CalcSlot(ESV, H_SuperRod),
Rock_Smash => CalcSlot(ESV, H_Surf),
Surf => CalcSlot(ESV, H_Surf),
_ => CalcSlot(ESV, H_Regular),
Old_Rod => GetHOldRod(ESV),
Good_Rod => GetHGoodRod(ESV),
Super_Rod => GetHSuperRod(ESV),
Rock_Smash => GetHSurf(ESV),
Surf => GetHSurf(ESV),
_ => GetHRegular(ESV),
};
}
@ -60,11 +49,11 @@ public static class SlotRange
var ESV = rand % 100;
return type switch
{
Rock_Smash or Surf => CalcSlot(ESV, H_Surf),
Old_Rod or Good_Rod or Super_Rod => CalcSlot(ESV, K_SuperRod),
BugContest => CalcSlot(ESV, K_BCC),
Headbutt or (Headbutt | Special) => CalcSlot(ESV, K_Headbutt),
_ => CalcSlot(ESV, H_Regular),
Rock_Smash or Surf => GetHSurf(ESV),
Old_Rod or Good_Rod or Super_Rod => GetKSuperRod(ESV),
BugContest => GetKBCC(ESV),
Headbutt or (Headbutt | Special) => GetKHeadbutt(ESV),
_ => GetHRegular(ESV),
};
}
@ -76,39 +65,16 @@ public static class SlotRange
uint ESV = rand / 656;
return type switch
{
Old_Rod or Rock_Smash or Surf => CalcSlot(ESV, H_Surf),
Good_Rod or Super_Rod => CalcSlot(ESV, J_SuperRod),
Old_Rod or Rock_Smash or Surf => GetHSurf(ESV),
Good_Rod or Super_Rod => GetJSuperRod(ESV),
HoneyTree => 0,
_ => CalcSlot(ESV, H_Regular),
_ => GetHRegular(ESV),
};
}
private readonly record struct Range(uint Min, uint Max);
private static Range[] GetRanges(params byte[] rates)
{
var len = rates.Length;
var arr = new Range[len];
uint sum = 0;
for (int i = 0; i < len; ++i)
arr[i] = new Range(sum, (sum += rates[i]) - 1);
return arr;
}
private static int CalcSlot(uint esv, Range[] ranges)
{
for (int i = 0; i < ranges.Length; ++i)
{
var (min, max) = ranges[i];
if (esv >= min && esv <= max)
return i;
}
return Invalid;
}
public static int GetLevel(EncounterSlot slot, LeadRequired lead, uint lvlrand)
{
if (lead == LeadRequired.PressureHustleSpirit)
if ((lead & LeadRequired.PressureHustleSpiritFail) == LeadRequired.PressureHustleSpirit)
return slot.LevelMax;
if (slot.IsFixedLevel)
return slot.LevelMin;