PKHeX/PKHeX.Core/Legality/Encounters/EncounterFinder.cs
Kurt 858aa50689 Refactor encounter matching
exercise in deferred execution/state machine, only calculate possible
matches until a sufficiently valid match is obtained. Previous setup
would try to calculate the 'best match' and had band-aid workarounds in
cases where a subsequent check may determine it to be a false match.

There's still more ways to improve speed:
- precalculate relationships for Encounter Slots rather than iterating
over every area
- yielding individual slots instead of an entire area
- group non-egg wondercards by ID in a dict/hashtable for faster
retrieval

reworked some internals:
- EncounterMatch is always an IEncounterable instead of an object, for
easy pattern matching.
- Splitbreed checking is done per encounter and is stored in the
EncounterEgg result
- Encounter validation uses Encounter/Move/RelearnMove/Evolution to
whittle to the final encounter.

As a part of the encounter matching, a lazy peek is used to check if an
invalid encounter should be retained instead of discarded; if another
encounter has not been checked, it'll stop the invalid checks and move
on. If it is the last encounter, no other valid encounters exist so it
will keep the parse for the invalid encounter.

If no encounters are yielded, then there is no encountermatch. An
EncounterInvalid is created to store basic details, and the parse is
carried out.

Breaks some legality checking features for flagging invalid moves in
more detail, but those can be re-added in a separate check (if
splitbreed & any move invalid -> check for other split moves).

Should now be easier to follow the flow & maintain 😄
2017-05-27 21:17:57 -07:00

88 lines
3 KiB
C#

using System;
using System.Linq;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core
{
public static class EncounterFinder
{
public static LegalInfo verifyEncounter(PKM pkm)
{
LegalInfo info = new LegalInfo(pkm);
var encounters = EncounterGenerator.getEncounters(pkm, info);
using (var encounter = new PeekEnumerator<IEncounterable>(encounters.GetEnumerator()))
{
if (!encounter.PeekIsNext())
return verifyWithoutEncounter(pkm, info);
var EncounterValidator = getEncounterVerifier(pkm);
while (encounter.MoveNext())
{
var EncounterMatch = info.EncounterMatch = encounter.Current;
var e = EncounterValidator(pkm, EncounterMatch);
if (!e.Valid && encounter.PeekIsNext())
continue;
info.Parse.Add(e);
if (pkm.Format >= 6)
{
info.vRelearn = VerifyRelearnMoves.verifyRelearn(pkm, info);
if (info.vRelearn.Any(z => !z.Valid) && encounter.PeekIsNext())
continue;
}
else
for (int i = 0; i < 4; i++)
info.vRelearn[i] = new CheckResult(CheckIdentifier.RelearnMove);
info.vMoves = VerifyCurrentMoves.verifyMoves(pkm, info);
if (info.vMoves.Any(z => !z.Valid) && encounter.PeekIsNext())
continue;
var evo = VerifyEvolution.verifyEvolution(pkm, EncounterMatch);
if (!evo.Valid && encounter.PeekIsNext())
continue;
info.Parse.Add(evo);
// Encounter Passes
break;
}
return info;
}
}
private static LegalInfo verifyWithoutEncounter(PKM pkm, LegalInfo info)
{
info.EncounterMatch = new EncounterInvalid(pkm);
string hint; // hint why an encounter was not found
if (pkm.WasGiftEgg)
hint = V359;
else if (pkm.WasEventEgg)
hint = V360;
else if (pkm.WasEvent)
hint = V78;
else
hint = V80;
info.Parse.Add(new CheckResult(Severity.Invalid, hint, CheckIdentifier.Encounter));
info.vRelearn = VerifyRelearnMoves.verifyRelearn(pkm, info);
info.vMoves = VerifyCurrentMoves.verifyMoves(pkm, info);
return info;
}
private static Func<PKM, IEncounterable, CheckResult> getEncounterVerifier(PKM pkm)
{
switch (pkm.GenNumber)
{
case 1:
case 2:
return VerifyEncounter.verifyEncounterG12;
default:
return VerifyEncounter.verifyEncounter;
}
}
}
}