Implement highest damage AI, further work on AI runner, random fixes
All checks were successful
Build / Build (push) Successful in 51s
All checks were successful
Build / Build (push) Successful in 51s
This commit is contained in:
92
PkmnLib.Dynamic/AI/HighestDamageAI.cs
Normal file
92
PkmnLib.Dynamic/AI/HighestDamageAI.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.Models.Choices;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.AI;
|
||||
|
||||
public class HighestDamageAI : PokemonAI
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public HighestDamageAI() : base("highest_damage")
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
||||
{
|
||||
var opponentSide = pokemon.BattleData!.SideIndex == 0 ? (byte)1 : (byte)0;
|
||||
var opponent = battle.Sides[opponentSide].Pokemon.WhereNotNull().FirstOrDefault(x => x.IsUsable);
|
||||
var moves = pokemon.Moves.WhereNotNull().Where(x => battle.CanUse(new MoveChoice(pokemon, x, opponentSide, 0)))
|
||||
.ToList();
|
||||
if (opponent == null)
|
||||
{
|
||||
var move = moves.FirstOrDefault();
|
||||
if (move != null)
|
||||
{
|
||||
return new MoveChoice(pokemon, move, opponentSide, 0);
|
||||
}
|
||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, 0);
|
||||
}
|
||||
|
||||
var movesWithDamage = moves.Select(move =>
|
||||
{
|
||||
var hitData = new CustomHitData
|
||||
{
|
||||
BasePower = move.MoveData.BasePower,
|
||||
Effectiveness =
|
||||
battle.Library.StaticLibrary.Types.GetEffectiveness(move.MoveData.MoveType, opponent.Types),
|
||||
Type = move.MoveData.MoveType,
|
||||
};
|
||||
return new
|
||||
{
|
||||
Move = move,
|
||||
Damage = battle.Library.DamageCalculator.GetDamage(null, move.MoveData.Category, pokemon, opponent, 1,
|
||||
0, hitData),
|
||||
};
|
||||
}).OrderByDescending(x => x.Damage).ToList();
|
||||
if (movesWithDamage.Count == 0)
|
||||
{
|
||||
return battle.Library.MiscLibrary.ReplacementChoice(pokemon, opponentSide, 0);
|
||||
}
|
||||
var bestMove = movesWithDamage.First().Move;
|
||||
return new MoveChoice(pokemon, bestMove, opponentSide, 0);
|
||||
}
|
||||
|
||||
private class CustomHitData : IHitData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsCritical => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort BasePower { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public float Effectiveness { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Damage => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeIdentifier? Type { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsContact => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFailed => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Fail()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetFlag(StringKey flag)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFlag(StringKey flag) => false;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace PkmnLib.Dynamic.AI;
|
||||
/// <summary>
|
||||
/// The base class for implementing an AI for Pokémon.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[PublicAPI, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public abstract class PokemonAI
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -38,6 +38,8 @@ public interface IMoveChoice : ITurnChoice
|
||||
/// </summary>
|
||||
Dictionary<StringKey, object?>? AdditionalData { get; }
|
||||
|
||||
void SetAdditionalData(StringKey key, object? value);
|
||||
|
||||
/// <summary>
|
||||
/// Volatile effects that are applied to the move choice.
|
||||
/// </summary>
|
||||
@@ -84,7 +86,14 @@ public class MoveChoice : TurnChoice, IMoveChoice
|
||||
public ScriptContainer Script { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<StringKey, object?>? AdditionalData { get; }
|
||||
public Dictionary<StringKey, object?>? AdditionalData { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetAdditionalData(StringKey key, object? value)
|
||||
{
|
||||
AdditionalData ??= new Dictionary<StringKey, object?>();
|
||||
AdditionalData[key] = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScriptSet Volatile { get; }
|
||||
|
||||
@@ -936,6 +936,8 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||
AbilityScript.Clear();
|
||||
}
|
||||
|
||||
private (IAbility, AbilityIndex)? _abilityCache;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAbility? ActiveAbility
|
||||
{
|
||||
@@ -945,9 +947,12 @@ public class PokemonImpl : ScriptSource, IPokemon
|
||||
return null;
|
||||
if (OverrideAbility != null)
|
||||
return OverrideAbility;
|
||||
if (_abilityCache is not null && _abilityCache.Value.Item2 == AbilityIndex)
|
||||
return _abilityCache.Value.Item1;
|
||||
var ability = Form.GetAbility(AbilityIndex);
|
||||
if (!Library.StaticLibrary.Abilities.TryGet(ability, out var abilityObj))
|
||||
throw new KeyNotFoundException($"Ability {ability} not found.");
|
||||
_abilityCache = (abilityObj, AbilityIndex);
|
||||
return abilityObj;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user