mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-17 05:48:44 +00:00
Improve RNG frame detection for gen4
stupid bruteforce for slot4->pk4
This commit is contained in:
parent
65baf92608
commit
0bc7eeb5f5
5 changed files with 166 additions and 75 deletions
|
@ -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)
|
||||
|
|
|
@ -48,17 +48,21 @@ public sealed class Frame
|
|||
/// <returns>Slot number for this frame & 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
107
PKHeX.Core/Legality/RNG/Frame/SlotNumber.cs
Normal file
107
PKHeX.Core/Legality/RNG/Frame/SlotNumber.cs
Normal 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,
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue