Move Legality check rewrite

Moved to checks
Returns why it's invalid
Added base egg move checks -- all eggs need level1 moves, can only be
pushed out. The move table acts as a sliding window; grab a list of
level1 moves, tack on relearn moves, then observe window view (last 4).
Added egg met level check
This commit is contained in:
Kaphotics 2016-03-12 09:16:41 -08:00
parent af5b2e09f9
commit 0ebbcbccb4
4 changed files with 226 additions and 142 deletions

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace PKHeX namespace PKHeX
@ -7,8 +6,6 @@ namespace PKHeX
public class LegalityAnalysis public class LegalityAnalysis
{ {
private readonly PK6 pk6; private readonly PK6 pk6;
public bool[] vMoves = new bool[4];
public bool[] vRelearn = new bool[4];
public LegalityAnalysis(PK6 pk) public LegalityAnalysis(PK6 pk)
{ {
pk6 = pk; pk6 = pk;
@ -17,106 +14,19 @@ namespace PKHeX
} }
public void updateRelearnLegality() public void updateRelearnLegality()
{ {
vRelearn = getRelearnValidity(pk6.RelearnMoves); try { vRelearn = LegalityCheck.verifyRelearn(pk6); }
catch { for (int i = 0; i < 4; i++) vRelearn[i] = new LegalityCheck(Severity.Invalid, "Internal error."); }
} }
public void updateMoveLegality() public void updateMoveLegality()
{ {
vMoves = getMoveValidity(pk6.Moves, pk6.RelearnMoves); try { vMoves = LegalityCheck.verifyMoves(pk6); }
} catch { for (int i = 0; i < 4; i++) vMoves[i] = new LegalityCheck(Severity.Invalid, "Internal error."); }
public bool[] getMoveValidity(int[] Moves, int[] RelearnMoves)
{
if (Moves.Length != 4)
return new bool[4];
bool[] res = { true, true, true, true };
if (!pk6.Gen6)
return res;
int[] validMoves = Legal.getValidMoves(pk6);
if (pk6.Species == 235)
{
for (int i = 0; i < 4; i++)
res[i] = !Legal.InvalidSketch.Contains(Moves[i]);
}
else
{
for (int i = 0; i < 4; i++)
res[i] = Moves[i] != Legal.Struggle && validMoves.Concat(RelearnMoves).Contains(Moves[i]);
}
if (Moves[0] == 0)
res[0] = false;
return res;
}
public bool[] getRelearnValidity(int[] Moves)
{
bool[] res = {true, true, true, true};
if (!pk6.Gen6)
goto noRelearn;
if (Moves.Length != 4)
return new bool[4];
if (pk6.WasLink)
{
if (pk6.FatefulEncounter) // Should NOT be Fateful
return new bool[4]; // False
int[] moves = Legal.getLinkMoves(pk6);
return moves.SequenceEqual(Moves) ? res : new bool[4];
}
if (pk6.WasEvent || pk6.WasEventEgg)
{
// Get WC6's that match
IEnumerable<WC6> vwc6 = Legal.getValidWC6s(pk6);
if (vwc6.Any(wc6 => wc6.RelearnMoves.SequenceEqual(Moves)))
return res; // all true
goto noRelearn; // No WC match
}
int[] relearnMoves = Legal.getValidRelearn(pk6, 0);
if (pk6.WasEgg)
{
if (Legal.SplitBreed.Contains(pk6.Species))
{
res = new bool[4];
for (int i = 0; i < 4; i++)
res[i] = relearnMoves.Contains(Moves[i]);
if (!res.Any(move => !move))
return res;
// Try Next Species up
Legal.getValidRelearn(pk6, 1);
for (int i = 0; i < 4; i++)
res[i] = relearnMoves.Contains(Moves[i]);
return res;
}
if (Legal.LightBall.Contains(pk6.Species))
relearnMoves = relearnMoves.Concat(new[] {344}).ToArray();
for (int i = 0; i < 4; i++)
res[i] &= relearnMoves.Contains(Moves[i]);
return res;
}
if (Moves[0] != 0) // DexNav only?
{
// Check DexNav
for (int i = 0; i < 4; i++)
res[i] &= Moves[i] == 0;
if (Legal.getDexNavValid(pk6))
res[0] = relearnMoves.Contains(Moves[0]);
return res;
}
// Should have no relearn moves.
noRelearn:
for (int i = 0; i < 4; i++)
res[i] = Moves[i] == 0;
return res;
} }
public LegalityCheck EC, Nickname, PID, IDs, IVs, EVs, Encounter; public LegalityCheck EC, Nickname, PID, IDs, IVs, EVs, Encounter;
public LegalityCheck[] vMoves = new LegalityCheck[4];
public LegalityCheck[] vRelearn = new LegalityCheck[4];
public string Report => getLegalityReport(); public string Report => getLegalityReport();
private string getLegalityReport() private string getLegalityReport()
{ {
@ -135,11 +45,11 @@ namespace PKHeX
string r = ""; string r = "";
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
if (!vMoves[i]) if (!vMoves[i].Valid)
r += $"Invalid: Move {i + 1}{Environment.NewLine}"; r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
if (!vRelearn[i]) if (!vRelearn[i].Valid)
r += $"Invalid: Relearn Move {i + 1}{Environment.NewLine}"; r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine;
if (r.Length == 0 && chks.All(chk => chk.Valid)) if (r.Length == 0 && chks.All(chk => chk.Valid))
return "Legal!"; return "Legal!";

View file

@ -1,4 +1,6 @@
using System.Linq; using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX namespace PKHeX
{ {
@ -97,7 +99,6 @@ namespace PKHeX
return new LegalityCheck(Severity.Fishy, "SID is zero."); return new LegalityCheck(Severity.Fishy, "SID is zero.");
return new LegalityCheck(); return new LegalityCheck();
} }
public static LegalityCheck verifyEncounter(PK6 pk) public static LegalityCheck verifyEncounter(PK6 pk)
{ {
if (!pk.Gen6) if (!pk.Gen6)
@ -122,15 +123,28 @@ namespace PKHeX
if (pk.WasEgg) if (pk.WasEgg)
{ {
// Check Hatch Locations // Check Hatch Locations
if (pk.Met_Level != 1)
return new LegalityCheck(Severity.Invalid, "Invalid met level, expected 1.");
if (pk.IsEgg)
{
var lc = pk.Met_Location == 0
? new LegalityCheck(Severity.Valid, "Valid un-hatched egg.")
: new LegalityCheck(Severity.Invalid, "Invalid location for un-hatched egg (expected ID:0)");
return lc;
}
if (pk.Version < 26) // XY if (pk.Version < 26) // XY
{ {
if (Legal.ValidMet_XY.Contains(pk.Met_Location)) var lc = Legal.ValidMet_XY.Contains(pk.Met_Location)
return new LegalityCheck(Severity.Valid, "Valid XY hatched egg."); ? new LegalityCheck(Severity.Valid, "Valid X/Y hatched egg.")
: new LegalityCheck(Severity.Invalid, "Invalid X/Y location for hatched egg.");
return lc;
} }
else if (pk.Version < 28) if (pk.Version < 28)
{ {
if (Legal.ValidMet_AO.Contains(pk.Met_Location)) var lc = Legal.ValidMet_AO.Contains(pk.Met_Location)
return new LegalityCheck(Severity.Valid, "Valid ORAS hatched egg."); ? new LegalityCheck(Severity.Valid, "Valid OR/AS hatched egg.")
: new LegalityCheck(Severity.Invalid, "Invalid OR/AS location for hatched egg.");
return lc;
} }
return new LegalityCheck(Severity.Invalid, "Invalid location for hatched egg."); return new LegalityCheck(Severity.Invalid, "Invalid location for hatched egg.");
} }
@ -142,5 +156,151 @@ namespace PKHeX
return new LegalityCheck(Severity.Invalid, "Not a valid encounter."); return new LegalityCheck(Severity.Invalid, "Not a valid encounter.");
} }
public static LegalityCheck[] verifyMoves(PK6 pk6)
{
int[] Moves = pk6.Moves;
LegalityCheck[] res = new LegalityCheck[4];
for (int i = 0; i < 4; i++)
res[i] = new LegalityCheck();
if (!pk6.Gen6)
return res;
var validMoves = Legal.getValidMoves(pk6).ToArray();
if (pk6.Species == 235)
{
for (int i = 0; i < 4; i++)
res[i] = Legal.InvalidSketch.Contains(Moves[i])
? new LegalityCheck(Severity.Invalid, "Invalid Sketch move.")
: new LegalityCheck();
}
else
{
int[] RelearnMoves = pk6.RelearnMoves;
for (int i = 0; i < 4; i++)
{
if (Moves[i] == Legal.Struggle)
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move: Struggle.");
else if (validMoves.Contains(Moves[i]))
res[i] = new LegalityCheck(Severity.Valid, "Level-up.");
else if (RelearnMoves.Contains(Moves[i]))
res[i] = new LegalityCheck(Severity.Valid, "Relearn Move.");
else
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move.");
}
}
if (Moves[0] == 0)
res[0] = new LegalityCheck(Severity.Invalid, "Invalid Move.");
return res;
}
public static LegalityCheck[] verifyRelearn(PK6 pk6)
{
LegalityCheck[] res = new LegalityCheck[4];
int[] Moves = pk6.RelearnMoves;
if (!pk6.Gen6)
goto noRelearn;
if (pk6.WasLink)
{
int[] moves = Legal.getLinkMoves(pk6);
for (int i = 0; i < 4; i++)
res[i] = moves[i] != Moves[i]
? new LegalityCheck(Severity.Invalid, $"Expected ID:{moves[i]}.")
: new LegalityCheck();
return res;
}
if (pk6.WasEvent || pk6.WasEventEgg)
{
// Get WC6's that match
IEnumerable<WC6> vwc6 = Legal.getValidWC6s(pk6);
foreach (var wc in vwc6)
{
int[] moves = wc.RelearnMoves;
for (int i = 0; i < 4; i++)
res[i] = moves[i] != Moves[i]
? new LegalityCheck(Severity.Invalid, $"Expected ID:{moves[i]}.")
: new LegalityCheck(Severity.Valid, $"Matched WC #{wc.CardID.ToString("0000")}");
if (res.All(r => r.Valid))
return res;
}
goto noRelearn; // No WC match
}
if (pk6.WasEgg)
{
const int games = 2;
bool checkAllGames = pk6.WasTradedEgg;
bool splitBreed = Legal.SplitBreed.Contains(pk6.Species);
int iterate = (checkAllGames ? games : 1) * (splitBreed ? 2 : 1);
for (int i = 0; i < iterate; i++)
{
int gameSource = !checkAllGames ? -1 : i % iterate / (splitBreed ? 2 : 1);
int skipOption = splitBreed && iterate / 2 <= i ? 1 : 0;
// Fetch moves - Sliding Window
List<int> eggMoves = new List<int>(Legal.getBaseEggMoves(pk6, skipOption, gameSource));
int eggCt = eggMoves.Count;
// Obtain Nonstandard moves
var relearn = pk6.RelearnMoves.Where(move => move != 0 && !eggMoves.Contains(move)).ToArray();
int relearnCt = relearn.Length;
eggMoves.AddRange(relearn);
int[] moves = eggMoves.Skip(eggCt + relearnCt - 4).Take(4).ToArray();
Array.Resize(ref moves, 4);
int req = relearnCt == 4
? 4
: (eggCt + relearnCt > 4
? 4 - eggCt
: eggCt);
// Movepool finalized! Check validity.
var relearnMoves = Legal.getValidRelearn(pk6, skipOption).ToArray();
if (Legal.LightBall.Contains(pk6.Species))
relearnMoves = relearnMoves.Concat(new[] { 344 }).ToArray();
int[] rl = pk6.RelearnMoves;
// Base Egg Move
for (int j = 0; j < req; j++)
res[j] = rl[j] != moves[j]
? new LegalityCheck(Severity.Invalid, $"Expected ID:{moves[j]}.")
: new LegalityCheck(Severity.Valid, "Base egg move.");
// Non-Base
for (int j = req; j < 4; j++)
res[j] = !relearnMoves.Contains(rl[j])
? new LegalityCheck(Severity.Invalid, "Not an expected relearn move.")
: new LegalityCheck(Severity.Valid, "Relearn move.");
if (res.All(r => r.Valid))
break;
}
return res;
}
if (Moves[0] != 0) // DexNav only?
{
// Check DexNav
if (!Legal.getDexNavValid(pk6))
goto noRelearn;
res[0] = !Legal.getValidRelearn(pk6, 0).Contains(Moves[0])
? new LegalityCheck(Severity.Invalid, "Not an expected DexNav move.")
: new LegalityCheck();
for (int i = 1; i < 4; i++)
res[i] = Moves[i] != 0
? new LegalityCheck(Severity.Invalid, "Expected no Relearn Move in slot.")
: new LegalityCheck();
return res;
}
// Should have no relearn moves.
noRelearn:
for (int i = 0; i < 4; i++)
res[i] = Moves[i] != 0
? new LegalityCheck(Severity.Invalid, "Expected no Relearn Moves.")
: new LegalityCheck();
return res;
}
} }
} }

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace PKHeX namespace PKHeX
@ -40,7 +41,7 @@ namespace PKHeX
IEnumerable<EncounterArea> locs = (alpha ? DexNavA : DexNavO).Where(l => l.Location == pk6.Met_Location); IEnumerable<EncounterArea> locs = (alpha ? DexNavA : DexNavO).Where(l => l.Location == pk6.Met_Location);
return locs.Select(loc => getValidEncounterSlots(pk6, loc, DexNav: true)).Any(slots => slots.Any()); return locs.Select(loc => getValidEncounterSlots(pk6, loc, DexNav: true)).Any(slots => slots.Any());
} }
internal static int[] getValidMoves(PK6 pk6) internal static IEnumerable<int> getValidMoves(PK6 pk6)
{ {
List<int> r = new List<int> {0}; List<int> r = new List<int> {0};
@ -56,14 +57,14 @@ namespace PKHeX
} }
return r.Distinct().ToArray(); return r.Distinct().ToArray();
} }
internal static int[] getValidRelearn(PK6 pk6, int skipOption) internal static IEnumerable<int> getValidRelearn(PK6 pk6, int skipOption)
{ {
List<int> r = new List<int> { 0 }; List<int> r = new List<int> { 0 };
int species = getBaseSpecies(pk6, skipOption); int species = getBaseSpecies(pk6, skipOption);
r.AddRange(getLVLMoves(species, 1)); r.AddRange(getLVLMoves(species, 1));
r.AddRange(getEggMoves(species)); r.AddRange(getEggMoves(species));
r.AddRange(getLVLMoves(species, 100)); r.AddRange(getLVLMoves(species, 100));
return r.Distinct().ToArray(); return r.Distinct();
} }
internal static int[] getLinkMoves(PK6 pk6) internal static int[] getLinkMoves(PK6 pk6)
{ {
@ -96,7 +97,42 @@ namespace PKHeX
vs.Any(dl => dl.Species == wc6.Species) && vs.Any(dl => dl.Species == wc6.Species) &&
wc6.OT == pk6.OT_Name); wc6.OT == pk6.OT_Name);
} }
internal static IEnumerable<EncounterArea> getEncounterSlots(PK6 pk6) internal static bool getWildEncounterValid(PK6 pk6)
{
var areas = getEncounterAreas(pk6);
bool dexNav = pk6.RelearnMove1 != 0;
return areas.Any(a => getValidEncounterSlots(pk6, a, dexNav).Any());
}
internal static IEnumerable<int> getBaseEggMoves(PK6 pk6, int skipOption, int gameSource)
{
int species = getBaseSpecies(pk6, skipOption);
if (gameSource == -1)
{
if (pk6.Version == 24 || pk6.Version == 25)
return LevelUpXY[species].getMoves(1);
// if (pk6.Version == 26 || pk6.Version == 27)
return LevelUpAO[species].getMoves(1);
}
if (gameSource == 0) // XY
return LevelUpXY[species].getMoves(1);
// if (gameSource == 1) // ORAS
return LevelUpAO[species].getMoves(1);
}
internal static int getBaseSpecies(PK6 pk6, int skipOption)
{
DexLevel[] evos = Evolves[pk6.Species].Evos;
switch (skipOption)
{
case -1: return pk6.Species;
case 1: return evos.Length <= 1 ? pk6.Species : evos[evos.Length - 1].Species;
default: return evos.Length <= 0 ? pk6.Species : evos.Last().Species;
}
}
private static IEnumerable<int> getLVLMoves(int species, int lvl)
{
return LevelUpXY[species].getMoves(lvl).Concat(LevelUpAO[species].getMoves(lvl));
}
private static IEnumerable<EncounterArea> getEncounterSlots(PK6 pk6)
{ {
switch (pk6.Version) switch (pk6.Version)
{ {
@ -111,29 +147,11 @@ namespace PKHeX
default: return new List<EncounterArea>(); default: return new List<EncounterArea>();
} }
} }
private static IEnumerable<EncounterArea> getEncounterAreas(PK6 pk6)
private static int getBaseSpecies(PK6 pk6, int skipOption)
{
DexLevel[] evos = Evolves[pk6.Species].Evos;
switch (skipOption)
{
case -1: return pk6.Species;
case 1: return evos.Length <= 1 ? pk6.Species : evos[evos.Length - 1].Species;
default: return evos.Length <= 0 ? pk6.Species : evos.Last().Species;
}
}
internal static bool getWildEncounterValid(PK6 pk6)
{
var areas = getEncounterAreas(pk6);
bool dexNav = pk6.RelearnMove1 != 0;
return areas.Any(a => getValidEncounterSlots(pk6, a, dexNav).Any());
}
internal static IEnumerable<EncounterArea> getEncounterAreas(PK6 pk6)
{ {
return getEncounterSlots(pk6).Where(l => l.Location == pk6.Met_Location); return getEncounterSlots(pk6).Where(l => l.Location == pk6.Met_Location);
} }
internal static IEnumerable<EncounterSlot> getValidEncounterSlots(PK6 pk6, EncounterArea loc, bool DexNav) private static IEnumerable<EncounterSlot> getValidEncounterSlots(PK6 pk6, EncounterArea loc, bool DexNav)
{ {
// Get Valid levels // Get Valid levels
IEnumerable<DexLevel> vs = getValidPreEvolutions(pk6); IEnumerable<DexLevel> vs = getValidPreEvolutions(pk6);
@ -186,10 +204,6 @@ namespace PKHeX
{ {
return EggMoveAO[species].Moves.Concat(EggMoveXY[species].Moves); return EggMoveAO[species].Moves.Concat(EggMoveXY[species].Moves);
} }
private static IEnumerable<int> getLVLMoves(int species, int lvl)
{
return LevelUpXY[species].getMoves(lvl).Concat(LevelUpAO[species].getMoves(lvl));
}
private static IEnumerable<int> getTutorMoves(int species) private static IEnumerable<int> getTutorMoves(int species)
{ {
PersonalInfo pkAO = PersonalAO[species]; PersonalInfo pkAO = PersonalAO[species];

View file

@ -1030,7 +1030,7 @@ namespace PKHeX
public void populateFields(PK6 pk, bool focus = true) public void populateFields(PK6 pk, bool focus = true)
{ {
pk6 = pk ?? new PK6(); pk6 = pk ?? new PK6();
if (fieldsInitialized & !PKX.verifychk(pk6.Data)) if (fieldsInitialized & !pk6.ChecksumValid)
Util.Alert("PKX File has an invalid checksum."); Util.Alert("PKX File has an invalid checksum.");
// Reset a little. // Reset a little.
@ -2115,7 +2115,7 @@ namespace PKHeX
Legality.updateRelearnLegality(); Legality.updateRelearnLegality();
PictureBox[] movePB = { PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4 }; PictureBox[] movePB = { PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4 };
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vRelearn[i]; movePB[i].Visible = !Legality.vRelearn[i].Valid;
} }
// else, Refresh Moves // else, Refresh Moves
{ {
@ -2123,7 +2123,7 @@ namespace PKHeX
Legality.updateMoveLegality(); Legality.updateMoveLegality();
PictureBox[] movePB = { PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4 }; PictureBox[] movePB = { PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4 };
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i]; movePB[i].Visible = !Legality.vMoves[i].Valid;
} }
} }
private void validateLocation(object sender, EventArgs e) private void validateLocation(object sender, EventArgs e)
@ -2155,10 +2155,10 @@ namespace PKHeX
// Refresh Move Legality // Refresh Move Legality
PictureBox[] movePB = {PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4}; PictureBox[] movePB = {PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4};
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i]; movePB[i].Visible = !Legality.vMoves[i].Valid;
PictureBox[] relPB = {PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4}; PictureBox[] relPB = {PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4};
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
relPB[i].Visible = !Legality.vRelearn[i]; relPB[i].Visible = !Legality.vRelearn[i].Valid;
} }
private void updateStats() private void updateStats()
{ {