using PkmnLib.Dynamic.Models; using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.AI.Explicit; /// /// An option where a move is used against a target /// public record struct MoveOption(AIMoveState Move, IBattle Battle, IPokemon? Target, uint EstimatedDamage = 0); /// /// A function that takes an explicit AI and a move option and returns a boolean value. /// public delegate bool AIBoolHandler(IExplicitAI ai, MoveOption option); /// /// A function for returning whether a Pokemon should switch. /// public delegate bool AISwitchBoolHandler(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList reserves); /// /// A function for returning the base power of a move. /// public delegate void AIMoveBasePowerHandler(IExplicitAI ai, MoveOption option, ref int score); /// /// A function for returning a score /// public delegate void AIScoreMoveHandler(IExplicitAI ai, MoveOption option, ref int score); /// /// Explicit AI Handlers are a library that allow plugins to explicitly indicate AI preferences. /// public interface IReadOnlyExplicitAIHandlers { /// /// A list of checks to determine if a move will fail. /// IReadOnlyDictionary MoveFailureCheck { get; } /// /// Checks if a move will fail based on the provided function code and options. /// bool MoveWillFail(IExplicitAI ai, StringKey functionCode, MoveOption option); /// /// A list of checks to determine if a move will fail against a target. /// IReadOnlyDictionary MoveFailureAgainstTargetCheck { get; } /// /// Checks if a move will fail against a target based on the provided function code and options. /// bool MoveWillFailAgainstTarget(IExplicitAI ai, StringKey functionCode, MoveOption option); /// /// A list of handlers to apply scores for move effects. /// IReadOnlyDictionary MoveEffectScore { get; } /// /// Applies the score for a move effect based on the provided name and options. /// void ApplyMoveEffectScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score); /// /// A list of handlers to apply scores for move effects against a target. /// IReadOnlyDictionary MoveEffectAgainstTargetScore { get; } /// /// Applies the score for a move effect against a target based on the provided name and options. /// void ApplyMoveEffectAgainstTargetScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score); /// /// A list of handlers to determine the base power of a move. /// IReadOnlyDictionary MoveBasePower { get; } /// /// Applies the base power for a move based on the provided name and options. /// void GetBasePower(IExplicitAI ai, StringKey name, MoveOption option, ref int power); /// /// A list of handlers to apply scores for general move effectiveness. /// IReadOnlyDictionary GeneralMoveScore { get; } /// /// Applies the score for a general move based on the provided option. /// void ApplyGenerateMoveScoreModifiers(IExplicitAI ai, MoveOption option, ref int score); /// /// A list of handlers to apply scores for general move effectiveness against a target. /// IReadOnlyDictionary GeneralMoveAgainstTargetScore { get; } /// /// Applies the score for a general move against a target based on the provided option. /// void ApplyGenerateMoveAgainstTargetScoreModifiers(IExplicitAI ai, MoveOption option, ref int score); /// /// Functions that indicate whether a Pokemon should switch /// IReadOnlyDictionary ShouldSwitchFunctions { get; } /// /// Indicates whether a Pokemon should switch into another Pokemon /// bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList reserves); /// /// Functions that indicate whether a Pokemon should NOT switch /// IReadOnlyDictionary ShouldNotSwitchFunctions { get; } /// /// Indicates whether a Pokemon should NOT switch into another Pokemon /// bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList reserves); /// /// Scores abilities /// IReadOnlyDictionary AbilityRanking { get; } } /// public class ExplicitAIHandlers : IReadOnlyExplicitAIHandlers { /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.MoveFailureCheck => MoveFailureCheck; /// /// Checks whether a move will fail /// public FunctionHandlerDictionary MoveFailureCheck { get; } = new(); /// public bool MoveWillFail(IExplicitAI ai, StringKey functionCode, MoveOption option) => MoveFailureCheck.TryGetValue(functionCode, out var handler) && handler(ai, option); /// /// Checks whether a move will fail against a target /// public FunctionHandlerDictionary MoveFailureAgainstTargetCheck { get; } = new(); /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.MoveFailureAgainstTargetCheck => MoveFailureAgainstTargetCheck; /// public bool MoveWillFailAgainstTarget(IExplicitAI ai, StringKey functionCode, MoveOption option) => MoveFailureAgainstTargetCheck.TryGetValue(functionCode, out var handler) && handler(ai, option); /// /// Functions to score a move /// public FunctionHandlerDictionary MoveEffectScore { get; } = []; /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.MoveEffectScore => MoveEffectScore; /// /// Functions to score a move against a target /// public FunctionHandlerDictionary MoveEffectAgainstTargetScore = []; /// public void ApplyMoveEffectScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score) { if (MoveEffectScore.TryGetValue(name, out var handler)) handler(ai, option, ref score); } /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.MoveEffectAgainstTargetScore => MoveEffectAgainstTargetScore; /// /// Functions that indicate a moves base power /// public FunctionHandlerDictionary MoveBasePower = []; /// public void ApplyMoveEffectAgainstTargetScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score) { if (MoveEffectAgainstTargetScore.TryGetValue(name, out var handler)) handler(ai, option, ref score); } /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.MoveBasePower => MoveBasePower; /// /// Functions that indicate a moves score /// public FunctionHandlerDictionary GeneralMoveScore = []; /// public void GetBasePower(IExplicitAI ai, StringKey name, MoveOption option, ref int power) { if (MoveBasePower.TryGetValue(name, out var handler)) handler(ai, option, ref power); } /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.GeneralMoveScore => GeneralMoveScore; public FunctionHandlerDictionary GeneralMoveAgainstTargetScore = []; /// public void ApplyGenerateMoveScoreModifiers(IExplicitAI ai, MoveOption option, ref int score) { foreach (var (_, handler) in GeneralMoveScore) { handler(ai, option, ref score); } } /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.GeneralMoveAgainstTargetScore => GeneralMoveAgainstTargetScore; /// public void ApplyGenerateMoveAgainstTargetScoreModifiers(IExplicitAI ai, MoveOption option, ref int score) { foreach (var (_, handler) in GeneralMoveAgainstTargetScore) { handler(ai, option, ref score); } } public FunctionHandlerDictionary ShouldSwitchFunctions = []; /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.ShouldSwitchFunctions => ShouldSwitchFunctions; /// public bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList reserves) { var shouldSwitch = false; foreach (var (_, handler) in ShouldSwitchFunctions) { shouldSwitch |= handler(ai, pokemon, battle, reserves); } return shouldSwitch; } /// /// Functions that indicate whether we should not switch /// public FunctionHandlerDictionary ShouldNotSwitchFunctions = []; /// /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.ShouldNotSwitchFunctions => ShouldNotSwitchFunctions; /// /// Functions to rank an ability /// public FunctionHandlerDictionary AbilityRanking = []; /// public bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList reserves) { var shouldNotSwitch = false; foreach (var (_, handler) in ShouldNotSwitchFunctions) { shouldNotSwitch |= handler(ai, pokemon, battle, reserves); } return shouldNotSwitch; } /// IReadOnlyDictionary IReadOnlyExplicitAIHandlers.AbilityRanking => AbilityRanking; }