From 5831df701bac124c78b08323559891ab6bec6cb7 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Tue, 21 Oct 2025 11:27:04 +0200 Subject: [PATCH] Make ExplicitAI configurable --- .../AI/Explicit/ExplicitAI.Switch.cs | 5 +- PkmnLib.Dynamic/AI/Explicit/ExplicitAI.cs | 98 +++++++++++++++---- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.Switch.cs b/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.Switch.cs index 2cad868..bea4ee8 100644 --- a/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.Switch.cs +++ b/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.Switch.cs @@ -60,7 +60,7 @@ public partial class ExplicitAI private IPokemon? ChooseBestReplacementPokemon(byte position, bool terribleMoves, IReadOnlyList usablePokemon, IBattleSide battleSide) { - var options = usablePokemon.Where((pokemon, index) => + var options = usablePokemon.Where((_, index) => { if (_skillFlags.ReserveLastPokemon && index == usablePokemon.Count - 1 && usablePokemon.Count > 1) return false; // Don't switch to the last Pokemon if there are others available. @@ -132,6 +132,9 @@ public partial class ExplicitAI return score; } + /// + /// Calculates the expected entry hazard damage for a given Pokémon on a given battle side. + /// public static uint CalculateEntryHazardDamage(IPokemon pokemon, IBattleSide side) { var damage = 0u; diff --git a/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.cs b/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.cs index 8667cef..8378117 100644 --- a/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.cs +++ b/PkmnLib.Dynamic/AI/Explicit/ExplicitAI.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using PkmnLib.Dynamic.BattleFlow; using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Models; @@ -9,52 +8,111 @@ using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.AI.Explicit; -public interface IExplicitAI -{ - public bool TrainerHighSkill { get; } - public bool TrainerMediumSkill { get; } - public IRandom Random { get; } -} - /// /// An explicit AI that has explicitly written logic for each Pokémon and move. /// /// /// This is heavily based on the AI used in Pokémon Essentials /// +public interface IExplicitAI +{ + /// + /// Whether the trainer is of high skill level. + /// + bool TrainerHighSkill { get; } + + /// + /// Whether the trainer is of medium skill level. + /// + bool TrainerMediumSkill { get; } + + /// + /// The random number generator used by the AI. + /// + IRandom Random { get; } +} + +/// public partial class ExplicitAI : PokemonAI, IExplicitAI { + /// + /// The score assigned to a move that is predicted to fail. + /// public const int MoveFailScore = 20; + + /// + /// The score assigned to a move that is considered useless. + /// public const int MoveUselessScore = 60; + + /// + /// The base score assigned to a move before modifiers. + /// public const int MoveBaseScore = 100; - private const float TrainerSkill = 100; // TODO: This should be configurable - private SkillFlags _skillFlags = new(); + private readonly float _trainerSkill = 100; + private readonly SkillFlags _skillFlags = new(); - private float MoveScoreThreshold => (float)(0.6f + 0.35f * Math.Sqrt(Math.Min(TrainerSkill, 100) / 100f)); + private float MoveScoreThreshold => (float)(0.6f + 0.35f * Math.Sqrt(Math.Min(_trainerSkill, 100) / 100f)); private readonly IReadOnlyExplicitAIHandlers _handlers; private readonly IRandom _random = new RandomImpl(); + /// + /// Flags that control the skill of the AI. + /// public class SkillFlags { - // TODO: Make these configurable - public bool CanPredictMoveFailure { get; set; } = true; - public bool ScoreMoves { get; set; } = true; - public bool ConsiderSwitching { get; set; } = true; - public bool ReserveLastPokemon { get; set; } = true; - public bool UsePokemonInOrder { get; set; } = true; + /// + /// Whether the AI can predict if a move will fail. + /// + public bool CanPredictMoveFailure { get; init; } = true; + + /// + /// Whether the AI will score moves to determine their usefulness. + /// + public bool ScoreMoves { get; init; } = true; + + /// + /// Whether the AI will consider switching out Pokémon. + /// + public bool ConsiderSwitching { get; init; } = true; + + /// + /// Whether the AI will reserve the last Pokémon in its party (ace). + /// + public bool ReserveLastPokemon { get; init; } = false; + + /// + /// Whether the AI will use Pokémon in the order they are in the party, or choose freely. + /// + public bool UsePokemonInOrder { get; init; } = false; } - /// + /// + /// The library for Pokémon data. public ExplicitAI(IDynamicLibrary library) : base("explicit") { _handlers = library.ExplicitAIHandlers; } - public bool TrainerHighSkill => TrainerSkill >= 45; - public bool TrainerMediumSkill => TrainerSkill >= 32; + /// + /// The library for Pokémon data. + /// The skill level of the trainer (0-100). + /// The skill flags that control the AI's behavior. + public ExplicitAI(IDynamicLibrary library, float trainerSkill, SkillFlags skillFlags) : base("explicit") + { + _handlers = library.ExplicitAIHandlers; + _trainerSkill = trainerSkill; + _skillFlags = skillFlags; + } + + /// + public bool TrainerHighSkill => _trainerSkill >= 45; + + /// + public bool TrainerMediumSkill => _trainerSkill >= 32; /// public IRandom Random => _random;