/// <summary> /// Generates <see cref="SeedFrame"/> nodes until the traversal is ended by an interrupt frame that matches the <see cref="prior"/>. /// </summary> /// <param name="ctr">Starting frame for the traversal.</param> /// <param name="current">Current lock criteria to satisfy. Used to find valid <see cref="SeedFrame"/> results to yield.</param> /// <param name="prior">Prior lock criteria. Used for determining when the traversal stops.</param> /// <returns>List of possible locks for the provided input.</returns> /// <remarks> /// An "interrupt" signals the end of the traversal. /// Any <see cref="SeedFrame"/> afterwards (when generated forward from the CPU Trainer) will use the interrupt rather than the previous <see cref="SeedFrame"/> that was found for the <see cref="prior"/> lock. /// </remarks> private IEnumerable <SeedFrame> GetAllLocks(int ctr, NPCLock current, NPCLock prior) { // Since the prior(next) lock is generated 7+2*n frames after, the worst case break is 7 frames after the PID. // Continue reversing until a sequential generation case is found. int start = ctr; // Track if we ever require the CPU Trainer Shiny Value to be a value for a shiny skip. // We need to un-set this flag if future frames don't pan out. bool forcedOT = false; while (true) { int p7 = ctr - 7; if (p7 > start) { // check for interrupting cpu team cases var upper = Cache[p7 + 1]; var lower = Cache[p7]; uint cid = upper << 16 | lower; var sv = (upper ^ lower) >> 3; if (current.Shadow && sv == TSV) // shiny shadow mon, only ever true for XD. { // This interrupt is ignored! The result is shiny. } else if (prior.MatchesLock(cid)) // lock matched cpu mon { if (RCSV != NOT_FORCED) // CPU shiny value is required for a previous lock { if (sv != RCSV) { if (forcedOT) // current call to this method had forced the OT; clear the forced OT before breaking. { RCSV = NOT_FORCED; } yield break; // Since we can't skip this interrupt, we're done. } } else // No CPU shiny value forced yet. Lets try to skip this lock by requiring the eventual OT to get this shiny. { RCSV = (int)sv; forcedOT = true; // don't break } } } uint pid = Cache[ctr + 1] << 16 | Cache[ctr]; if (current.MatchesLock(pid)) { yield return new SeedFrame { FrameID = ctr + (current.Seen ? 5 : 7), PID = pid } } ; ctr += 2; } }
/// <summary> /// Returns a single <see cref="SeedFrame"/> as the <see cref="current"/> lock must match precisely. /// </summary> /// <param name="ctr">Starting frame for the traversal.</param> /// <param name="current">Current lock criteria to satisfy. Used to find valid <see cref="SeedFrame"/> results to yield.</param> /// <returns></returns> private IEnumerable <SeedFrame> GetSingleLock(int ctr, NPCLock current) { uint pid = Cache[ctr + 1] << 16 | Cache[ctr]; if (current.MatchesLock(pid)) { yield return(new SeedFrame(pid, ctr + (current.Seen ? 5 : 7))); } }
/// <summary> /// Generates a list of frames the <see cref="current"/> lock data can be generated at. /// </summary> /// <param name="ctr">Starting frame for the traversal.</param> /// <param name="current">Current lock criteria to satisfy. Used to find valid <see cref="SeedFrame"/> results to yield.</param> /// <param name="prior">Prior lock criteria. Used for determining when the traversal stops.</param> /// <returns>List of possible locks for the provided input.</returns> private IEnumerable <SeedFrame> GetPossibleLocks(int ctr, NPCLock current, NPCLock?prior) { if (prior?.Shadow != false) { return(GetSingleLock(ctr, current)); } return(GetAllLocks(ctr, current, prior)); }
/// <summary> /// Returns a single <see cref="SeedFrame"/> as the <see cref="current"/> lock must match precisely. /// </summary> /// <param name="ctr">Starting frame for the traversal.</param> /// <param name="current">Current lock criteria to satisfy. Used to find valid <see cref="SeedFrame"/> results to yield.</param> /// <returns></returns> private IEnumerable <SeedFrame> GetSingleLock(int ctr, NPCLock current) { uint pid = Cache[ctr + 1] << 16 | Cache[ctr]; if (current.MatchesLock(pid)) { yield return(new SeedFrame(pid, ctr + (current.Seen ? 5 : 7))); } else { yield break; } // Reaching here means the single lock didn't cut it. Maybe the frame before it was an anti-shiny reroll? // Track if we ever require the CPU Trainer Shiny Value to be a value for a shiny skip. // We need to un-set this flag if future frames don't pan out. bool forcedOT = false; int start = 2; while (true) { var upper = Cache[start + 1]; var lower = Cache[start]; // uint cid = upper << 16 | lower; var sv = (upper ^ lower) >> 3; if (sv == TSV) // XD shiny checks all opponent PKM, even non-shadow. { // Anti-shiny rerolled! This is a possible frame. } else if (RCSV != NOT_FORCED) // CPU shiny value is required for a previous lock { if (sv != RCSV) { if (forcedOT) // current call to this method had forced the OT; clear the forced OT before breaking. { RCSV = NOT_FORCED; } yield break; // Since we can't skip this interrupt, we're done. } else // No CPU shiny value forced yet. Lets try to skip this lock by requiring the eventual OT to get this shiny. { RCSV = (int)sv; forcedOT = true; // don't break } } // Yield the final rerolled pid instead of the bad anti-shiny (metadata/validation). yield return(new SeedFrame(pid, start + (current.Seen ? 5 : 7))); start += 2; } }
private static bool MatchesLock(NPCLock k, uint PID, int Gender) { if (k.Nature != null && k.Nature != PID % 25) { return(false); } if (k.Gender != null && k.Gender != Gender) { return(false); } return(true); }
/// <summary> /// Depth-first search traversal which finds a possible origin for the <see cref="Specifications"/>. /// </summary> /// <param name="frame">Frame at which the search starts/continues at.</param> /// <param name="prior">Prior <see cref="NPCLock"/> data. If this is the last lock in the CPU Team, this is null.</param> /// <returns>True if the <see cref="Specifications"/> are valid.</returns> private bool FindLockSeed(int frame = 0, NPCLock prior = null) { if (Locks.Count == 0) // full team reverse-generated { return(VerifyNPC(frame)); } var current = Locks.Pop(); var locks = GetPossibleLocks(frame, current, prior); foreach (var l in locks) { Team.Push(l); // possible match if (FindLockSeed(l.FrameID, current)) { return(true); // all locks are satisfied } Team.Pop(); // no match, remove } Locks.Push(current); // return the lock, lock is impossible return(false); }
// Recursively iterates to visit possible locks until all locks (or none) are satisfied. public static bool FindLockSeed(uint seed, RNG RNG, Stack <NPCLock> Locks, NPCLock prior, Stack <uint> PIDs, bool XD, out uint origin) { if (Locks.Count == 0) { return(VerifyNPC(seed, RNG, PIDs, XD, out origin)); } var l = Locks.Pop(); foreach (var poss in FindPossibleLockFrames(seed, RNG, l, prior)) { PIDs.Push(poss.PID); // possible match if (FindLockSeed(poss.Seed, RNG, Locks, l, PIDs, XD, out origin)) { return(true); // all locks are satisfied } PIDs.Pop(); // no match, remove } Locks.Push(l); // return the lock, lock is impossible origin = seed; return(false); }
private static IEnumerable <SeedFrame> GetComplexLockFrame(FrameCache cache, int ctr, NPCLock l, NPCLock prior) { // Since the prior(next) lock is generated 7+2*n frames after, the worst case break is 7 frames after the PID. // Continue reversing until a sequential generation case is found. // Check int start = ctr; while (true) { int p7 = ctr - 7; if (p7 > start) { uint cid = cache[p7 + 1] << 16 | cache[p7]; if (MatchesLock(prior, cid, PKX.GetGenderFromPID(prior.Species, cid))) { yield break; } } uint pid = cache[ctr + 1] << 16 | cache[ctr]; if (MatchesLock(l, pid, PKX.GetGenderFromPID(l.Species, pid))) { yield return new SeedFrame { FrameID = ctr + 6, PID = pid } } ; ctr += 2; } }
private static IEnumerable <SeedFrame> GetSingleLockFrame(FrameCache cache, int ctr, NPCLock l) { uint pid = cache[ctr + 1] << 16 | cache[ctr]; if (MatchesLock(l, pid, PKX.GetGenderFromPID(l.Species, pid))) { yield return new SeedFrame { FrameID = ctr + 6, PID = pid } } ; }
private static IEnumerable <SeedFrame> FindPossibleLockFrames(FrameCache cache, int ctr, NPCLock l, NPCLock prior) { if (prior == null || prior.Shadow) { return(GetSingleLockFrame(cache, ctr, l)); } return(GetComplexLockFrame(cache, ctr, l, prior)); }
// Recursively iterates to visit possible locks until all locks (or none) are satisfied. private static bool FindLockSeed(FrameCache cache, int ctr, Stack <NPCLock> Locks, NPCLock prior, Stack <uint> PIDs, bool XD, out int originFrame) { if (Locks.Count == 0) { return(VerifyNPC(cache, ctr, PIDs, XD, out originFrame)); } var l = Locks.Pop(); foreach (var poss in FindPossibleLockFrames(cache, ctr, l, prior)) { PIDs.Push(poss.PID); // possible match if (FindLockSeed(cache, poss.FrameID, Locks, l, PIDs, XD, out originFrame)) { return(true); // all locks are satisfied } PIDs.Pop(); // no match, remove } Locks.Push(l); // return the lock, lock is impossible originFrame = 0; return(false); }
// Restriction Checking private static IEnumerable <SeedPID> FindPossibleLockFrames(uint seed, RNG RNG, NPCLock l, NPCLock prior) { // todo: check for premature breaks do { // todo: generate PKM for checking uint pid = 0; int gender = 0; int abil = 0; uint origin = 0; // possible to defer calc to yield? if (prior == null) { if (MatchesLock(l, pid, gender, abil)) { yield return new SeedPID { Seed = origin, PID = pid } } ; yield break; } if (MatchesLock(prior, pid, gender, abil)) { yield break; // prior lock breaks our chain! } if (MatchesLock(l, pid, gender, abil)) { yield return new SeedPID { Seed = origin, PID = pid } } ; } while (true); }
private static IEnumerable <SeedFrame> GetSingleLockFrame(FrameCache cache, int ctr, NPCLock l) { uint pid = cache[ctr + 1] << 16 | cache[ctr]; if (l.MatchesLock(pid)) { yield return new SeedFrame { FrameID = ctr + 6, PID = pid } } ; }