From 830c5ac08f88d27d5ed97ac9f1d3c5c142565a6a Mon Sep 17 00:00:00 2001 From: Eppin <96589679+Eppin@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:28:19 +0100 Subject: [PATCH] Add StopCondition for specific amount of flawless IVs --- .../Helpers/TargetFlawlessIVsConverter.cs | 36 +++++++++ .../Settings/StopConditionSettings.cs | 76 ++++++++++++++++-- SysBot.Tests/GenerateTests.cs | 47 +---------- .../Resources/0133 - Eevee - D7F3BFC57EC0.pk9 | Bin 0 -> 344 bytes .../0813 - Scorbunny - 4F320450C78B.pk9 | Bin 0 -> 344 bytes SysBot.Tests/StopConditionsTests.cs | 67 +++++++++++++++ SysBot.Tests/SysBot.Tests.csproj | 10 ++- 7 files changed, 185 insertions(+), 51 deletions(-) create mode 100644 SysBot.Pokemon/Helpers/TargetFlawlessIVsConverter.cs create mode 100644 SysBot.Tests/Resources/0133 - Eevee - D7F3BFC57EC0.pk9 create mode 100644 SysBot.Tests/Resources/0813 - Scorbunny - 4F320450C78B.pk9 create mode 100644 SysBot.Tests/StopConditionsTests.cs diff --git a/SysBot.Pokemon/Helpers/TargetFlawlessIVsConverter.cs b/SysBot.Pokemon/Helpers/TargetFlawlessIVsConverter.cs new file mode 100644 index 00000000..865be125 --- /dev/null +++ b/SysBot.Pokemon/Helpers/TargetFlawlessIVsConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace SysBot.Pokemon; + +public class TargetFlawlessIVsConverter(Type type) : EnumConverter(type) +{ + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (value == null) return base.ConvertTo(context, culture, value, destinationType); + + var name = Enum.GetName(type, value); + if (string.IsNullOrWhiteSpace(name)) + return value.ToString(); + + var fieldInfo = type.GetField(name); + if (fieldInfo == null) + return value.ToString(); + + return Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)) is DescriptionAttribute dna + ? dna.Description + : value.ToString(); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + foreach (var fieldInfo in type.GetFields()) + { + if (Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)) is DescriptionAttribute dna && (string)value == dna.Description) + return Enum.Parse(type, fieldInfo.Name); + } + + return Enum.Parse(type, (string)value); + } +} diff --git a/SysBot.Pokemon/Settings/StopConditionSettings.cs b/SysBot.Pokemon/Settings/StopConditionSettings.cs index 723d9420..06699c8f 100644 --- a/SysBot.Pokemon/Settings/StopConditionSettings.cs +++ b/SysBot.Pokemon/Settings/StopConditionSettings.cs @@ -44,7 +44,16 @@ public class StopConditionSettings [Category(StopConditions)] public class SearchCondition { - public override string ToString() => $"{(!IsEnabled ? $"{Nature}, condition is disabled" : $"{Nature}, {StopOnSpecies}, {TargetMinIVs} - {TargetMaxIVs}")}"; + public override string ToString() + { + if (!IsEnabled) return $"{Nature}, condition is disabled"; + + var ivsStr = FlawlessIVs == TargetFlawlessIVsType.Disabled + ? $"{TargetMinIVs} - {TargetMaxIVs}" + : $"Flawless IVs: {Convert(FlawlessIVs)}"; + + return $"{Nature}, {StopOnSpecies}, {ivsStr}"; + } [Category(StopConditions), DisplayName("1. Enabled")] public bool IsEnabled { get; set; } = true; @@ -58,10 +67,14 @@ public class SearchCondition [Category(StopConditions), DisplayName("4. Gender")] public TargetGenderType GenderTarget { get; set; } = TargetGenderType.Any; - [Category(StopConditions), DisplayName("5. Minimum accepted IVs")] + [Category(StopConditions), DisplayName("5. Minimum flawless IVs")] + [TypeConverter(typeof(TargetFlawlessIVsConverter))] + public TargetFlawlessIVsType FlawlessIVs { get; set; } = TargetFlawlessIVsType.Disabled; + + [Category(StopConditions), DisplayName("6. Minimum accepted IVs")] public string TargetMinIVs { get; set; } = ""; - [Category(StopConditions), DisplayName("6. Maximum accepted IVs")] + [Category(StopConditions), DisplayName("7. Maximum accepted IVs")] public string TargetMaxIVs { get; set; } = ""; } @@ -112,13 +125,13 @@ public static bool EncounterFound(T pk, StopConditionSettings settings, IRead return true; return settings.SearchConditions.Any(s => - MatchIVs(pkIVsArr, s.TargetMinIVs, s.TargetMaxIVs) && + (MatchIVs(pkIVsArr, s.TargetMinIVs, s.TargetMaxIVs, s.FlawlessIVs) || MatchFlawlessIVs(pkIVsArr, s.FlawlessIVs)) && (s.Nature == pk.Nature || s.Nature == Nature.Random) && (s.StopOnSpecies == (Species)pk.Species || s.StopOnSpecies == Species.None) && MatchGender(s.GenderTarget, (Gender)pk.Gender) && s.IsEnabled); } - + private static bool MatchGender(TargetGenderType target, Gender result) { return target switch @@ -131,8 +144,28 @@ private static bool MatchGender(TargetGenderType target, Gender result) }; } - private static bool MatchIVs(IReadOnlyList pkIVs, string targetMinIVsStr, string targetMaxIVsStr) + private static bool MatchFlawlessIVs(IReadOnlyList pkIVs, TargetFlawlessIVsType targetFlawlessIVs) + { + var count = pkIVs.Count(iv => iv == 31); + + return targetFlawlessIVs switch + { + TargetFlawlessIVsType.Disabled => false, + TargetFlawlessIVsType._0 => count >= 0, + TargetFlawlessIVsType._1 => count >= 1, + TargetFlawlessIVsType._2 => count >= 2, + TargetFlawlessIVsType._3 => count >= 3, + TargetFlawlessIVsType._4 => count >= 4, + TargetFlawlessIVsType._5 => count >= 5, + TargetFlawlessIVsType._6 => count == 6, + _ => throw new ArgumentOutOfRangeException(nameof(targetFlawlessIVs), targetFlawlessIVs, null) + }; + } + + private static bool MatchIVs(IReadOnlyList pkIVs, string targetMinIVsStr, string targetMaxIVsStr, TargetFlawlessIVsType targetFlawlessIVs) { + if (targetFlawlessIVs != TargetFlawlessIVsType.Disabled) return false; + var targetMinIVs = ReadTargetIVs(targetMinIVsStr, true); var targetMaxIVs = ReadTargetIVs(targetMaxIVsStr, false); @@ -215,6 +248,25 @@ public static string GetMarkName(IRibbonIndex pk) } return ""; } + + // Quite ugly solution to display DescriptionAttribute + private static string Convert(T value) where T : Enum + { + var k = typeof(T); + var g = k.Name; + + var name = Enum.GetName(typeof(T), value); + if (string.IsNullOrWhiteSpace(name)) + return value.ToString(); + + var fieldInfo = typeof(T).GetField(name); + if (fieldInfo == null) + return value.ToString(); + + return Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute)) is DescriptionAttribute dna + ? dna.Description + : value.ToString(); + } } public enum TargetShinyType @@ -233,3 +285,15 @@ public enum TargetGenderType Female, // Match female only Genderless, // Match genderless only } + +public enum TargetFlawlessIVsType +{ + Disabled, + [Description("0")] _0, + [Description("1")] _1, + [Description("2")] _2, + [Description("3")] _3, + [Description("4")] _4, + [Description("5")] _5, + [Description("6")] _6, +} diff --git a/SysBot.Tests/GenerateTests.cs b/SysBot.Tests/GenerateTests.cs index e3a75643..b062f17b 100644 --- a/SysBot.Tests/GenerateTests.cs +++ b/SysBot.Tests/GenerateTests.cs @@ -2,6 +2,7 @@ using PKHeX.Core; using SysBot.Pokemon; using Xunit; +using LegalitySettings = SysBot.Pokemon.LegalitySettings; namespace SysBot.Tests; @@ -63,48 +64,7 @@ public void TestAbilityTwitch(string set, int abilNumber) } } - [Theory] - [InlineData(InvalidSpec)] - public void ShouldNotGenerate(string set) - { - _ = AutoLegalityWrapper.GetTrainerInfo(); - var s = ShowdownUtil.ConvertToShowdown(set); - s.Should().BeNull(); - } - - [Theory] - [InlineData(Torkoal2, 2)] - [InlineData(Charizard4, 4)] - public void TestAbility(string set, int abilNumber) - { - var sav = AutoLegalityWrapper.GetTrainerInfo(); - for (int i = 0; i < 10; i++) - { - var s = new ShowdownSet(set); - var template = AutoLegalityWrapper.GetTemplate(s); - var pk = sav.GetLegal(template, out _); - pk.AbilityNumber.Should().Be(abilNumber); - } - } - - [Theory] - [InlineData(Torkoal2, 2)] - [InlineData(Charizard4, 4)] - public void TestAbilityTwitch(string set, int abilNumber) - { - var sav = AutoLegalityWrapper.GetTrainerInfo(); - for (int i = 0; i < 10; i++) - { - var twitch = set.Replace("\r\n", " ").Replace("\n", " "); - var s = ShowdownUtil.ConvertToShowdown(twitch); - var template = s == null ? null : AutoLegalityWrapper.GetTemplate(s); - var pk = template == null ? null : sav.GetLegal(template, out _); - pk.Should().NotBeNull(); - pk!.AbilityNumber.Should().Be(abilNumber); - } - } - - private const string Gengar = + private const string Gengar = @"Gengar-Gmax @ Life Orb Ability: Cursed Body Shiny: Yes @@ -159,7 +119,6 @@ Timid Nature - Solar Beam - Beat Up"; - private const string InvalidSpec = + private const string InvalidSpec = "(Pikachu)"; - } } diff --git a/SysBot.Tests/Resources/0133 - Eevee - D7F3BFC57EC0.pk9 b/SysBot.Tests/Resources/0133 - Eevee - D7F3BFC57EC0.pk9 new file mode 100644 index 0000000000000000000000000000000000000000..557b7a494e5f98c0e76d69c1b385aa6ff95bdb57 GIT binary patch literal 344 zcmX?5cXU4k!{_U*3=9lf3Qwbe6hkxv6NqAPQhNG=j}OcwNVvy%p{Q|XNM$Gk;#3q- z5J#7Rk1?EaFN21NBo{KDhvEN2ZX>XAVK7Ms0nz>+;(WaNy}|CwWXQu%RR9qYXJP^( bM)%Sn25v?SMHm8%3_J{MV93hA3X}x^@JJSO literal 0 HcmV?d00001 diff --git a/SysBot.Tests/Resources/0813 - Scorbunny - 4F320450C78B.pk9 b/SysBot.Tests/Resources/0813 - Scorbunny - 4F320450C78B.pk9 new file mode 100644 index 0000000000000000000000000000000000000000..26b5e02e8652310a1bf57594d3a2e24782aff1bc GIT binary patch literal 344 zcmeA^9>BuDVC1jM%)p?f@HC162%H!gK@>v?Q~g>ng9t)WZzYPVV1{Ide1;;1B!*Ik zJRq!u%PKPHGIRiW${Ji?F&LkR;oJZJ;vgYTVXy=lgb`TJe GetResource(string file) + { + var info = Assembly.GetExecutingAssembly().GetName(); + + await using var stream = Assembly + .GetExecutingAssembly() + .GetManifestResourceStream($"{info.Name}.Resources.{file}")!; + + using var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + return memoryStream.ToArray(); + } +} diff --git a/SysBot.Tests/SysBot.Tests.csproj b/SysBot.Tests/SysBot.Tests.csproj index c88570a5..c049e26d 100644 --- a/SysBot.Tests/SysBot.Tests.csproj +++ b/SysBot.Tests/SysBot.Tests.csproj @@ -1,9 +1,17 @@ - + false + + + + + + + +