Make ExplicitAI configurable
All checks were successful
Build / Build (push) Successful in 1m2s

This commit is contained in:
2025-10-21 11:27:04 +02:00
parent 8fd7df5d80
commit 5831df701b
2 changed files with 82 additions and 21 deletions

View File

@@ -60,7 +60,7 @@ public partial class ExplicitAI
private IPokemon? ChooseBestReplacementPokemon(byte position, bool terribleMoves, private IPokemon? ChooseBestReplacementPokemon(byte position, bool terribleMoves,
IReadOnlyList<IPokemon> usablePokemon, IBattleSide battleSide) IReadOnlyList<IPokemon> usablePokemon, IBattleSide battleSide)
{ {
var options = usablePokemon.Where((pokemon, index) => var options = usablePokemon.Where((_, index) =>
{ {
if (_skillFlags.ReserveLastPokemon && index == usablePokemon.Count - 1 && usablePokemon.Count > 1) if (_skillFlags.ReserveLastPokemon && index == usablePokemon.Count - 1 && usablePokemon.Count > 1)
return false; // Don't switch to the last Pokemon if there are others available. return false; // Don't switch to the last Pokemon if there are others available.
@@ -132,6 +132,9 @@ public partial class ExplicitAI
return score; return score;
} }
/// <summary>
/// Calculates the expected entry hazard damage for a given Pokémon on a given battle side.
/// </summary>
public static uint CalculateEntryHazardDamage(IPokemon pokemon, IBattleSide side) public static uint CalculateEntryHazardDamage(IPokemon pokemon, IBattleSide side)
{ {
var damage = 0u; var damage = 0u;

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Dynamic.BattleFlow; using PkmnLib.Dynamic.BattleFlow;
using PkmnLib.Dynamic.Libraries; using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.Models;
@@ -9,52 +8,111 @@ using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.AI.Explicit; namespace PkmnLib.Dynamic.AI.Explicit;
public interface IExplicitAI
{
public bool TrainerHighSkill { get; }
public bool TrainerMediumSkill { get; }
public IRandom Random { get; }
}
/// <summary> /// <summary>
/// An explicit AI that has explicitly written logic for each Pokémon and move. /// An explicit AI that has explicitly written logic for each Pokémon and move.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is heavily based on the AI used in <a href="https://github.com/Maruno17/pokemon-essentials">Pokémon Essentials</a> /// This is heavily based on the AI used in <a href="https://github.com/Maruno17/pokemon-essentials">Pokémon Essentials</a>
/// </remarks> /// </remarks>
public interface IExplicitAI
{
/// <summary>
/// Whether the trainer is of high skill level.
/// </summary>
bool TrainerHighSkill { get; }
/// <summary>
/// Whether the trainer is of medium skill level.
/// </summary>
bool TrainerMediumSkill { get; }
/// <summary>
/// The random number generator used by the AI.
/// </summary>
IRandom Random { get; }
}
/// <inheritdoc cref="IExplicitAI"/>
public partial class ExplicitAI : PokemonAI, IExplicitAI public partial class ExplicitAI : PokemonAI, IExplicitAI
{ {
/// <summary>
/// The score assigned to a move that is predicted to fail.
/// </summary>
public const int MoveFailScore = 20; public const int MoveFailScore = 20;
/// <summary>
/// The score assigned to a move that is considered useless.
/// </summary>
public const int MoveUselessScore = 60; public const int MoveUselessScore = 60;
/// <summary>
/// The base score assigned to a move before modifiers.
/// </summary>
public const int MoveBaseScore = 100; public const int MoveBaseScore = 100;
private const float TrainerSkill = 100; // TODO: This should be configurable private readonly float _trainerSkill = 100;
private SkillFlags _skillFlags = new(); 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 IReadOnlyExplicitAIHandlers _handlers;
private readonly IRandom _random = new RandomImpl(); private readonly IRandom _random = new RandomImpl();
/// <summary>
/// Flags that control the skill of the AI.
/// </summary>
public class SkillFlags public class SkillFlags
{ {
// TODO: Make these configurable /// <summary>
public bool CanPredictMoveFailure { get; set; } = true; /// Whether the AI can predict if a move will fail.
public bool ScoreMoves { get; set; } = true; /// </summary>
public bool ConsiderSwitching { get; set; } = true; public bool CanPredictMoveFailure { get; init; } = true;
public bool ReserveLastPokemon { get; set; } = true;
public bool UsePokemonInOrder { get; set; } = true; /// <summary>
/// Whether the AI will score moves to determine their usefulness.
/// </summary>
public bool ScoreMoves { get; init; } = true;
/// <summary>
/// Whether the AI will consider switching out Pokémon.
/// </summary>
public bool ConsiderSwitching { get; init; } = true;
/// <summary>
/// Whether the AI will reserve the last Pokémon in its party (ace).
/// </summary>
public bool ReserveLastPokemon { get; init; } = false;
/// <summary>
/// Whether the AI will use Pokémon in the order they are in the party, or choose freely.
/// </summary>
public bool UsePokemonInOrder { get; init; } = false;
} }
/// <inheritdoc /> /// <inheritdoc cref="ExplicitAI"/>
/// <param name="library">The library for Pokémon data.</param>
public ExplicitAI(IDynamicLibrary library) : base("explicit") public ExplicitAI(IDynamicLibrary library) : base("explicit")
{ {
_handlers = library.ExplicitAIHandlers; _handlers = library.ExplicitAIHandlers;
} }
public bool TrainerHighSkill => TrainerSkill >= 45; /// <inheritdoc cref="ExplicitAI(IDynamicLibrary)"/>
public bool TrainerMediumSkill => TrainerSkill >= 32; /// <param name="library">The library for Pokémon data.</param>
/// <param name="trainerSkill">The skill level of the trainer (0-100).</param>
/// <param name="skillFlags">The skill flags that control the AI's behavior.</param>
public ExplicitAI(IDynamicLibrary library, float trainerSkill, SkillFlags skillFlags) : base("explicit")
{
_handlers = library.ExplicitAIHandlers;
_trainerSkill = trainerSkill;
_skillFlags = skillFlags;
}
/// <inheritdoc />
public bool TrainerHighSkill => _trainerSkill >= 45;
/// <inheritdoc />
public bool TrainerMediumSkill => _trainerSkill >= 32;
/// <inheritdoc /> /// <inheritdoc />
public IRandom Random => _random; public IRandom Random => _random;