Reduce linq usage, minor perf

This commit is contained in:
Kurt 2022-08-17 23:48:37 -07:00
parent 5140c47e84
commit 3dde8a7cfa
13 changed files with 215 additions and 80 deletions

View file

@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static PKHeX.Core.EncountersWC3;
namespace PKHeX.Core;
@ -74,7 +73,8 @@ public static class EncounterEvent
ICollection<WB8> b8 = GetWB8DB(Util.GetBinaryResource("wb8.pkl"));
ICollection<WA8> a8 = GetWA8DB(Util.GetBinaryResource("wa8.pkl"));
foreach (var gift in paths.Where(Directory.Exists).SelectMany(MysteryUtil.GetGiftsFromFolder))
var gifts = GetGifts(paths);
foreach (var gift in gifts)
{
static void AddOrExpand<T>(ref ICollection<T> arr, T obj)
{
@ -118,6 +118,18 @@ public static class EncounterEvent
MGDB_G8B = SetArray(b8);
}
private static IEnumerable<MysteryGift> GetGifts(IEnumerable<string> paths)
{
foreach (var path in paths)
{
if (!Directory.Exists(path))
continue;
var gifts = MysteryUtil.GetGiftsFromFolder(path);
foreach (var gift in gifts)
yield return gift;
}
}
public static IEnumerable<MysteryGift> GetAllEvents(bool sorted = true)
{
var regular = new IReadOnlyList<MysteryGift>[]

View file

@ -26,7 +26,11 @@ public static class EncounterSlotGenerator
public static IEnumerable<EncounterSlot> GetPossible(PKM pk, EvoCriteria[] chain, GameVersion gameSource)
{
var possibleAreas = GetAreasByGame(pk, gameSource);
return possibleAreas.SelectMany(z => z.GetSpecies(chain));
foreach (var area in possibleAreas)
{
foreach (var result in area.GetSpecies(chain))
yield return result;
}
}
private static IEnumerable<EncounterArea> GetAreasByGame(PKM pk, GameVersion gameSource) => gameSource switch
@ -81,16 +85,23 @@ public static class EncounterSlotGenerator
bool noMet = !pk.HasOriginalMetLocation || (pk.Format == 2 && gameSource != C);
if (noMet)
return slots;
return GetIsMatchLocation(pk, slots);
}
private static IEnumerable<EncounterArea> GetIsMatchLocation(PKM pk, IEnumerable<EncounterArea> areas)
{
var metLocation = pk.Met_Location;
return slots.Where(z => z.IsMatchLocation(metLocation));
foreach (var area in areas)
{
if (area.IsMatchLocation(metLocation))
yield return area;
}
}
internal static EncounterSlot? GetCaptureLocation(PKM pk, EvoCriteria[] chain)
{
return GetPossible(pk, chain, (GameVersion)pk.Version)
.OrderBy(z => !chain.Any(s => s.Species == z.Species && s.Form == z.Form))
.ThenBy(z => z.LevelMin)
.FirstOrDefault();
var possible = GetPossible(pk, chain, (GameVersion)pk.Version);
return EncounterUtil.GetMinByLevel(chain, possible);
}
private static IEnumerable<EncounterArea> GetEncounterTable(PKM pk, GameVersion game) => game switch

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.GameVersion;
@ -17,13 +17,33 @@ public static class EncounterTradeGenerator
private static IEnumerable<EncounterTradeGB> GetPossibleVC(EvoCriteria[] chain, GameVersion game)
{
var table = GetTableVC(game);
return table.Where(e => chain.Any(c => c.Species == e.Species && c.Form == 0));
foreach (var enc in table)
{
foreach (var evo in chain)
{
if (evo.Species != enc.Species)
continue;
if (evo.Form != 0)
break;
yield return enc;
break;
}
}
}
private static IEnumerable<EncounterTrade> GetPossible(EvoCriteria[] chain, GameVersion game)
{
var table = GetTable(game);
return table.Where(e => chain.Any(c => c.Species == e.Species));
foreach (var enc in table)
{
foreach (var evo in chain)
{
if (evo.Species != enc.Species)
continue;
yield return enc;
break;
}
}
}
public static IEnumerable<EncounterTradeGB> GetValidEncounterTradesVC(PKM pk, EvoCriteria[] chain, GameVersion game)

View file

@ -1,6 +1,5 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.EncounterEvent;
namespace PKHeX.Core;
@ -15,9 +14,16 @@ public static class MysteryGiftGenerator
yield return RangerManaphy;
var table = GetTable(gen, game);
var possible = table.Where(wc => chain.Any(evo => evo.Species == wc.Species));
foreach (var enc in possible)
yield return enc;
foreach (var enc in table)
{
foreach (var evo in chain)
{
if (evo.Species != enc.Species)
continue;
yield return enc;
break;
}
}
}
public static IEnumerable<MysteryGift> GetValidGifts(PKM pk, EvoCriteria[] chain, GameVersion game)

View file

@ -1,5 +1,4 @@
using System;
using System.Linq;
using static PKHeX.Core.GameVersion;
@ -28,8 +27,8 @@ public static class EncounterSuggestion
if (s is null)
return GetSuggestedEncounter(pk, w, loc);
bool isDefinitelySlot = chain.Any(z => z.Species == w.Species && z.Form == w.Form);
bool isDefinitelyStatic = chain.Any(z => z.Species == s.Species && z.Form == s.Form);
bool isDefinitelySlot = Array.Exists(chain, z => z.Species == w.Species && z.Form == w.Form);
bool isDefinitelyStatic = Array.Exists(chain, z => z.Species == s.Species && z.Form == s.Form);
IEncounterable obj = (isDefinitelySlot || !isDefinitelyStatic) ? w : s;
return GetSuggestedEncounter(pk, obj, loc);
}
@ -108,12 +107,20 @@ public static class EncounterSuggestion
{
var evos = table.GetValidPreEvolutions(pk, levelMax: i, skipChecks: true, levelMin: startLevel);
if (evos.Length < count) // lost an evolution, prior level was minimum current level
return evos.Max(evo => evo.LevelMax) + 1;
return GetMaxLevelMax(evos) + 1;
count = evos.Length;
}
return startLevel;
}
private static int GetMaxLevelMax(EvoCriteria[] evos)
{
int max = 0;
foreach (var evo in evos)
max = Math.Max(evo.LevelMax, max);
return max;
}
public static bool IterateMinimumCurrentLevel(PKM pk, bool isLegal, int max = 100)
{
var original = pk.CurrentLevel;

View file

@ -1,7 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
@ -13,7 +12,7 @@ public static class MoveListSuggest
return MoveList.GetBaseEggMoves(pk, pk.Species, 0, (GameVersion)pk.Version, pk.CurrentLevel);
if (types != MoveSourceType.None)
return GetValidMoves(pk, enc, evoChains, types).ToArray();
return GetValidMoves(pk, enc, evoChains, types);
// try to give current moves
if (enc.Generation <= 2)
@ -28,22 +27,28 @@ public static class MoveListSuggest
return MoveLevelUp.GetEncounterMoves(pk.Species, pk.Form, pk.CurrentLevel, (GameVersion)pk.Version);
}
return GetValidMoves(pk, enc, evoChains, types).ToArray();
return GetValidMoves(pk, enc, evoChains, types);
}
private static IEnumerable<int> GetValidMoves(PKM pk, IEncounterTemplate enc, EvolutionHistory evoChains, MoveSourceType types = MoveSourceType.ExternalSources)
private static int[] GetValidMoves(PKM pk, IEncounterTemplate enc, EvolutionHistory evoChains, MoveSourceType types = MoveSourceType.ExternalSources)
{
var length = pk.MaxMoveID + 1;
bool[] rent = ArrayPool<bool>.Shared.Rent(length);
LearnPossible.Get(pk, enc, evoChains, rent, types);
var span = rent.AsSpan(0, length);
LearnPossible.Get(pk, enc, evoChains, span, types);
for (int i = 1; i < length; i++)
var count = span[1..].Count(true);
var result = new int[count];
int ctr = 0;
for (int i = 1; i < span.Length; i++)
{
if (rent[i])
yield return i;
result[ctr++] = i;
}
ArrayPool<bool>.Shared.Return(rent, true);
span.Clear();
ArrayPool<bool>.Shared.Return(rent);
return result;
}
/// <summary>
@ -135,11 +140,13 @@ public static class MoveListSuggest
return result;
// Try again with the other split-breed species if possible.
var incense = EncounterEggGenerator.GenerateEggs(tmp, generation).FirstOrDefault();
if (incense is null || incense.Species == enc.Species)
return result;
return incense.GetEggRelearnMoves(parse, pk);
var other = EncounterEggGenerator.GenerateEggs(tmp, generation);
foreach (var incense in other)
{
if (incense.Species != enc.Species)
return incense.GetEggRelearnMoves(parse, pk);
}
return result;
}
private static int[] GetEggRelearnMoves(this IEncounterTemplate enc, ReadOnlySpan<MoveResult> parse, PKM pk)

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using static PKHeX.Core.PIDType;
@ -364,7 +363,7 @@ public static class MethodFinder
(int species, int genderValue) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
if ((uint)species > Legal.MaxSpeciesID_4)
return GetNonMatch(out pidiv);
static int getRatio(int species) => PersonalTable.HGSS[species].Gender;
switch (genderValue)
{
@ -669,9 +668,15 @@ public static class MethodFinder
Debug.Assert(b >> 15 == 0);
uint second = a << 16;
uint first = b << 16;
var pairs = method.RecoverLower16Bits(first, second)
.Concat(method.RecoverLower16Bits(first, second ^ 0x80000000));
foreach (var z in pairs)
var attempt1 = method.RecoverLower16Bits(first, second);
foreach (var z in attempt1)
{
yield return z;
yield return z ^ 0x80000000; // sister bitflip
}
var attempt2 = method.RecoverLower16Bits(first, second ^ 0x80000000);
foreach (var z in attempt2)
{
yield return z;
yield return z ^ 0x80000000; // sister bitflip
@ -684,9 +689,14 @@ public static class MethodFinder
Debug.Assert(rand3 >> 15 == 0);
rand1 <<= 16;
rand3 <<= 16;
var seeds = method.RecoverLower16BitsGap(rand1, rand3)
.Concat(method.RecoverLower16BitsGap(rand1, rand3 ^ 0x80000000));
foreach (var z in seeds)
var attempt1 = method.RecoverLower16Bits(rand1, rand3);
foreach (var z in attempt1)
{
yield return z;
yield return z ^ 0x80000000; // sister bitflip
}
var attempt2 = method.RecoverLower16Bits(rand1, rand3 ^ 0x80000000);
foreach (var z in attempt2)
{
yield return z;
yield return z ^ 0x80000000; // sister bitflip
@ -824,25 +834,27 @@ public static class MethodFinder
{
WC3 g => IsCompatible3Mystery(val, pk, g),
EncounterStatic3 s => IsCompatible3Static(val, pk, s),
EncounterSlot3 w => (w.Species == (int)Species.Unown ? MethodH_Unown : MethodH).Contains(val),
EncounterStaticShadow => val is CXD or CXDAnti,
EncounterSlot3PokeSpot => val == PokeSpot,
_ => val == None,
EncounterStaticShadow => val is (CXD or CXDAnti),
EncounterSlot3PokeSpot => val is PokeSpot,
EncounterSlot3 w => w.Species != (int)Species.Unown
? val is (Method_1 or Method_2 or Method_3 or Method_4)
: val is (Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown),
_ => val is None,
};
private static bool IsCompatible3Static(PIDType val, PKM pk, EncounterStatic3 s) => pk.Version switch
{
(int)GameVersion.CXD => val is CXD or CXD_ColoStarter or CXDAnti,
(int)GameVersion.E => val == Method_1, // no roamer glitch
(int)GameVersion.FR or (int) GameVersion.LG => s.Roaming ? val.IsRoamerPIDIV(pk) : val == Method_1, // roamer glitch
_ => s.Roaming ? val.IsRoamerPIDIV(pk) : MethodH14.Contains(val), // RS, roamer glitch && RSBox s/w emulation => method 4 available
(int)GameVersion.CXD => val is (CXD or CXD_ColoStarter or CXDAnti),
(int)GameVersion.E => val is Method_1, // no roamer glitch
(int)GameVersion.FR or (int) GameVersion.LG => s.Roaming ? val.IsRoamerPIDIV(pk) : val is Method_1, // roamer glitch
_ => s.Roaming ? val.IsRoamerPIDIV(pk) : val is (Method_1 or Method_4), // RS, roamer glitch && RSBox s/w emulation => method 4 available
};
private static bool IsCompatible3Mystery(PIDType val, PKM pk, WC3 g) => val == g.Method || val switch
{
// forced shiny eggs, when hatched, can lose their detectable correlation.
None => (g.Method is BACD_R_S or BACD_U_S) && g.IsEgg && !pk.IsEgg,
CXDAnti => g.Method == CXD && g.Shiny == Shiny.Never,
None => (g.Method is (BACD_R_S or BACD_U_S)) && g.IsEgg && !pk.IsEgg,
CXDAnti => g.Method is CXD && g.Shiny == Shiny.Never,
_ => false,
};
@ -864,24 +876,24 @@ public static class MethodFinder
{
// Pokewalker can sometimes be confused with CuteCharm due to the PID creation routine. Double check if it is okay.
EncounterStatic4Pokewalker when val is CuteCharm => GetCuteCharmMatch(pk, pk.EncryptionConstant, out _) && IsCuteCharm4Valid(encounter, pk),
EncounterStatic4Pokewalker => val == Pokewalker,
EncounterStatic4Pokewalker => val is Pokewalker,
EncounterStatic4 {Species: (int)Species.Pichu} => val == Pokewalker,
EncounterStatic4 {Shiny: Shiny.Always} => val == ChainShiny,
EncounterStatic4 {Species: (int)Species.Pichu} => val is Pokewalker,
EncounterStatic4 {Shiny: Shiny.Always} => val is ChainShiny,
EncounterStatic4 when val is CuteCharm => IsCuteCharm4Valid(encounter, pk),
EncounterStatic4 => val == Method_1,
EncounterStatic4 => val is Method_1,
EncounterSlot4 w => val switch
{
// Chain shiny with Poké Radar is only possible in DPPt, in grass. Safari Zone does not allow using the Poké Radar
ChainShiny => pk.IsShiny && !pk.HGSS && (w.GroundTile & GroundTileAllowed.Grass) != 0 && !Locations.IsSafariZoneLocation4(w.Location),
CuteCharm => IsCuteCharm4Valid(encounter, pk),
_ => val == Method_1,
_ => val is Method_1,
},
PGT => IsG4ManaphyPIDValid(val, pk), // Manaphy is the only PGT in the database
PCD d when d.Gift.PK.PID != 1 => true, // Already matches PCD's fixed PID requirement
_ => val == None,
_ => val is None,
};
private static bool IsG4ManaphyPIDValid(PIDType val, PKM pk)
@ -945,8 +957,4 @@ public static class MethodFinder
_ => (currentSpecies, pk.Gender),
};
private static readonly PIDType[] MethodH = { Method_1, Method_2, Method_3, Method_4 };
private static readonly PIDType[] MethodH14 = { Method_1, Method_4 };
private static readonly PIDType[] MethodH_Unown = { Method_1_Unown, Method_2_Unown, Method_3_Unown, Method_4_Unown };
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.Legal;
using static PKHeX.Core.GameVersion;
@ -162,7 +161,7 @@ internal static class GBRestrictions
if (!matchAny)
return PotentialGBOrigin.Either;
if (HeldItems_GSC.Contains(catch_rate))
if (IsTradebackCatchRate(catch_rate))
return PotentialGBOrigin.Either;
return PotentialGBOrigin.Gen1Only;
@ -221,7 +220,7 @@ internal static class GBRestrictions
_ => RateMatchesEncounter(enc.Species, enc.Version, pk1.Catch_Rate),
};
public static bool IsTradebackCatchRate(byte rate) => HeldItems_GSC.Contains(rate);
public static bool IsTradebackCatchRate(byte rate) => Array.IndexOf(HeldItems_GSC, rate) != -1;
}
public enum PotentialGBOrigin

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.LegalityCheckStrings;
using static PKHeX.Core.LanguageID;
@ -92,9 +91,11 @@ public sealed class NicknameVerifier : Verifier
{
// Gen3 gifts transferred to Generation 4 from another language can set the nickname flag.
var evos = data.Info.EvoChainsAllGens.Gen3;
bool matchAny = evos.Any(evo => !SpeciesName.IsNicknamedAnyLanguage(evo.Species, nickname, 3));
if (matchAny)
return;
foreach (var evo in evos)
{
if (!SpeciesName.IsNicknamedAnyLanguage(evo.Species, nickname, 3))
return;
}
}
if (pk.IsNicknamed)

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using static PKHeX.Core.EReaderBerryMatch;
namespace PKHeX.Core;
@ -15,7 +16,7 @@ public static class EReaderBerrySettings
private static string Name { get; set; } = string.Empty;
/// <summary> e-Reader Berry Name formatted in Title Case </summary>
public static string DisplayName => string.Format(LegalityCheckStrings.L_XEnigmaBerry_0, Util.ToTitleCase(Name));
public static string DisplayName => string.Format(LegalityCheckStrings.L_XEnigmaBerry_0, Util.ToTitleCase(Name.AsSpan()));
private static int Language { get; set; }

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
@ -49,7 +49,7 @@ public sealed class Record3
for (int i = 0; i < result.Length; i++)
{
var replaced = names[i].Replace('_', ' ');
var titled = Util.ToTitleCase(replaced);
var titled = Util.ToTitleCase(replaced.AsSpan());
result[i] = new ComboItem(titled, values[i]);
}
return result;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
@ -162,19 +161,73 @@ public static partial class Util
private static bool IsHexUpper(char c) => (uint)(c - 'A') <= 5;
private static bool IsHexLower(char c) => (uint)(c - 'a') <= 5;
private static bool IsHex(char c) => IsNum(c) || IsHexUpper(c) || IsHexLower(c);
private static string TitleCase(string word) => char.ToUpper(word[0]) + word[1..].ToLower();
/// <summary>
/// Filters the string down to only valid hex characters, returning a new string.
/// </summary>
/// <param name="str">Input string to filter</param>
public static string GetOnlyHex(string str) => string.IsNullOrWhiteSpace(str) ? string.Empty : string.Concat(str.Where(IsHex));
public static string GetOnlyHex(string str)
{
if (string.IsNullOrWhiteSpace(str))
return string.Empty;
var sb = new StringBuilder(str.Length);
foreach (var c in str)
{
if (IsHex(c))
sb.Append(c);
}
return sb.ToString();
}
/// <summary>
/// Returns a new string with each word converted to its appropriate title case.
/// </summary>
/// <param name="str">Input string to modify</param>
public static string ToTitleCase(string str) => string.IsNullOrWhiteSpace(str) ? string.Empty : string.Join(" ", str.Split(' ').Select(TitleCase));
/// <param name="trim">Trim ends of whitespace</param>
public static string ToTitleCase(ReadOnlySpan<char> str, bool trim = false)
{
int start = 0;
if (trim)
{
// Get First index that isn't a space
while (start < str.Length && char.IsWhiteSpace(str[start]))
start++;
}
if (start == str.Length)
return string.Empty;
int end = str.Length - 1;
if (trim)
{
// Get Last index that isn't a space
while (end > start && char.IsWhiteSpace(str[end]))
end--;
}
var span = str.Slice(start, end - start + 1);
var sb = new StringBuilder(span.Length);
// Add each word to the string builder. Continue from the first index that isn't a space.
// Add the first character as uppercase, then add each successive character as lowercase.
bool first = true;
foreach (char c in span)
{
if (char.IsWhiteSpace(c))
{
first = true;
sb.Append(c);
}
else if (first)
{
sb.Append(char.ToUpper(c));
first = false;
}
else
{
sb.Append(char.ToLower(c));
}
}
return sb.ToString();
}
/// <summary>
/// Trims a string at the first instance of a 0x0000 terminator.

View file

@ -1,6 +1,5 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using PKHeX.Drawing;
using PKHeX.WinForms.Properties;
@ -17,7 +16,8 @@ public partial class ShinyLeaf : UserControl
private readonly CheckBox[] Flags;
public void CheckAll(bool all = true) => SetValue(all ? 0b00111111 : 0);
private const byte CrownAndFiveLeafs = 0b00_1_11111;
public void CheckAll(bool all = true) => SetValue(all ? CrownAndFiveLeafs : 0);
public int GetValue()
{
@ -51,11 +51,21 @@ public partial class ShinyLeaf : UserControl
resource = Resources.leaf;
if (!c.Checked)
CHK_C.Checked = CHK_C.Enabled = false;
else if (Flags.Take(5).All(z => z.Checked))
else if (HasAllFiveLeafs())
CHK_C.Enabled = true;
}
if (!c.Checked)
resource = ImageUtil.ChangeOpacity(resource, 0.4);
c.Image = resource;
}
private bool HasAllFiveLeafs()
{
for (int i = 0; i < 5; i++)
{
if (!Flags[i].Checked)
return false;
}
return true;
}
}