From 0ebbcbccb40cdf2ce2b074ce85b6ce4708906bb5 Mon Sep 17 00:00:00 2001 From: Kaphotics Date: Sat, 12 Mar 2016 09:16:41 -0800 Subject: [PATCH] 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 --- Legality/Analysis.cs | 112 +++------------------------- Legality/Checks.cs | 174 +++++++++++++++++++++++++++++++++++++++++-- Legality/Core.cs | 72 ++++++++++-------- PKX/f1-Main.cs | 10 +-- 4 files changed, 226 insertions(+), 142 deletions(-) diff --git a/Legality/Analysis.cs b/Legality/Analysis.cs index f2f2ce2d8..ac3e3c08a 100644 --- a/Legality/Analysis.cs +++ b/Legality/Analysis.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; namespace PKHeX @@ -7,8 +6,6 @@ namespace PKHeX public class LegalityAnalysis { private readonly PK6 pk6; - public bool[] vMoves = new bool[4]; - public bool[] vRelearn = new bool[4]; public LegalityAnalysis(PK6 pk) { pk6 = pk; @@ -17,106 +14,19 @@ public LegalityAnalysis(PK6 pk) } 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() { - vMoves = getMoveValidity(pk6.Moves, pk6.RelearnMoves); - } - - 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 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; + try { vMoves = LegalityCheck.verifyMoves(pk6); } + catch { for (int i = 0; i < 4; i++) vMoves[i] = new LegalityCheck(Severity.Invalid, "Internal error."); } } 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(); private string getLegalityReport() { @@ -135,11 +45,11 @@ private string getLegalityReport() string r = ""; for (int i = 0; i < 4; i++) - if (!vMoves[i]) - r += $"Invalid: Move {i + 1}{Environment.NewLine}"; + if (!vMoves[i].Valid) + r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine; for (int i = 0; i < 4; i++) - if (!vRelearn[i]) - r += $"Invalid: Relearn Move {i + 1}{Environment.NewLine}"; + if (!vRelearn[i].Valid) + r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine; if (r.Length == 0 && chks.All(chk => chk.Valid)) return "Legal!"; diff --git a/Legality/Checks.cs b/Legality/Checks.cs index a7315400e..0cdb0fb65 100644 --- a/Legality/Checks.cs +++ b/Legality/Checks.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; namespace PKHeX { @@ -97,7 +99,6 @@ public static LegalityCheck verifyID(PK6 pk) return new LegalityCheck(Severity.Fishy, "SID is zero."); return new LegalityCheck(); } - public static LegalityCheck verifyEncounter(PK6 pk) { if (!pk.Gen6) @@ -122,15 +123,28 @@ public static LegalityCheck verifyEncounter(PK6 pk) if (pk.WasEgg) { // 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 (Legal.ValidMet_XY.Contains(pk.Met_Location)) - return new LegalityCheck(Severity.Valid, "Valid XY hatched egg."); + var lc = Legal.ValidMet_XY.Contains(pk.Met_Location) + ? 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)) - return new LegalityCheck(Severity.Valid, "Valid ORAS hatched egg."); + var lc = Legal.ValidMet_AO.Contains(pk.Met_Location) + ? 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."); } @@ -142,5 +156,151 @@ public static LegalityCheck verifyEncounter(PK6 pk) 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 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 eggMoves = new List(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; + } } } diff --git a/Legality/Core.cs b/Legality/Core.cs index a052945fb..de0a8a788 100644 --- a/Legality/Core.cs +++ b/Legality/Core.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace PKHeX @@ -40,7 +41,7 @@ internal static bool getDexNavValid(PK6 pk6) IEnumerable locs = (alpha ? DexNavA : DexNavO).Where(l => l.Location == pk6.Met_Location); return locs.Select(loc => getValidEncounterSlots(pk6, loc, DexNav: true)).Any(slots => slots.Any()); } - internal static int[] getValidMoves(PK6 pk6) + internal static IEnumerable getValidMoves(PK6 pk6) { List r = new List {0}; @@ -56,14 +57,14 @@ internal static int[] getValidMoves(PK6 pk6) } return r.Distinct().ToArray(); } - internal static int[] getValidRelearn(PK6 pk6, int skipOption) + internal static IEnumerable getValidRelearn(PK6 pk6, int skipOption) { List r = new List { 0 }; int species = getBaseSpecies(pk6, skipOption); r.AddRange(getLVLMoves(species, 1)); r.AddRange(getEggMoves(species)); r.AddRange(getLVLMoves(species, 100)); - return r.Distinct().ToArray(); + return r.Distinct(); } internal static int[] getLinkMoves(PK6 pk6) { @@ -96,7 +97,42 @@ internal static IEnumerable getValidWC6s(PK6 pk6) vs.Any(dl => dl.Species == wc6.Species) && wc6.OT == pk6.OT_Name); } - internal static IEnumerable 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 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 getLVLMoves(int species, int lvl) + { + return LevelUpXY[species].getMoves(lvl).Concat(LevelUpAO[species].getMoves(lvl)); + } + private static IEnumerable getEncounterSlots(PK6 pk6) { switch (pk6.Version) { @@ -111,29 +147,11 @@ internal static IEnumerable getEncounterSlots(PK6 pk6) default: return new List(); } } - - 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 getEncounterAreas(PK6 pk6) + private static IEnumerable getEncounterAreas(PK6 pk6) { return getEncounterSlots(pk6).Where(l => l.Location == pk6.Met_Location); } - internal static IEnumerable getValidEncounterSlots(PK6 pk6, EncounterArea loc, bool DexNav) + private static IEnumerable getValidEncounterSlots(PK6 pk6, EncounterArea loc, bool DexNav) { // Get Valid levels IEnumerable vs = getValidPreEvolutions(pk6); @@ -186,10 +204,6 @@ private static IEnumerable getEggMoves(int species) { return EggMoveAO[species].Moves.Concat(EggMoveXY[species].Moves); } - private static IEnumerable getLVLMoves(int species, int lvl) - { - return LevelUpXY[species].getMoves(lvl).Concat(LevelUpAO[species].getMoves(lvl)); - } private static IEnumerable getTutorMoves(int species) { PersonalInfo pkAO = PersonalAO[species]; diff --git a/PKX/f1-Main.cs b/PKX/f1-Main.cs index 2abfc75e0..09395bab6 100644 --- a/PKX/f1-Main.cs +++ b/PKX/f1-Main.cs @@ -1030,7 +1030,7 @@ private void InitializeLanguage() public void populateFields(PK6 pk, bool focus = true) { pk6 = pk ?? new PK6(); - if (fieldsInitialized & !PKX.verifychk(pk6.Data)) + if (fieldsInitialized & !pk6.ChecksumValid) Util.Alert("PKX File has an invalid checksum."); // Reset a little. @@ -2115,7 +2115,7 @@ private void validateMove(object sender, EventArgs e) Legality.updateRelearnLegality(); PictureBox[] movePB = { PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4 }; for (int i = 0; i < 4; i++) - movePB[i].Visible = !Legality.vRelearn[i]; + movePB[i].Visible = !Legality.vRelearn[i].Valid; } // else, Refresh Moves { @@ -2123,7 +2123,7 @@ private void validateMove(object sender, EventArgs e) Legality.updateMoveLegality(); PictureBox[] movePB = { PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4 }; 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) @@ -2155,10 +2155,10 @@ private void updateLegality() // Refresh Move Legality PictureBox[] movePB = {PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4}; 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}; for (int i = 0; i < 4; i++) - relPB[i].Visible = !Legality.vRelearn[i]; + relPB[i].Visible = !Legality.vRelearn[i].Valid; } private void updateStats() {