Support for basic AIs
This commit is contained in:
parent
dbd07564c4
commit
b43e1dc15e
22
PkmnLib.Dynamic/AI/PassTurnAI.cs
Normal file
22
PkmnLib.Dynamic/AI/PassTurnAI.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Dynamic.Models.Choices;
|
||||||
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An extremely simple AI that always passes its turn.
|
||||||
|
/// </summary>
|
||||||
|
public class PassTurnAI : PokemonAI
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public PassTurnAI() : base("pass_turn")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
||||||
|
{
|
||||||
|
return new PassChoice(pokemon);
|
||||||
|
}
|
||||||
|
}
|
94
PkmnLib.Dynamic/AI/PokemonAI.cs
Normal file
94
PkmnLib.Dynamic/AI/PokemonAI.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Dynamic.Models.Choices;
|
||||||
|
using PkmnLib.Static.Moves;
|
||||||
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base class for implementing an AI for Pokémon.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PokemonAI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the AI.
|
||||||
|
/// </summary>
|
||||||
|
public StringKey Name { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="PokemonAI" />
|
||||||
|
protected PokemonAI(StringKey name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the choice for the Pokémon.
|
||||||
|
/// </summary>
|
||||||
|
public abstract ITurnChoice GetChoice(IBattle battle, IPokemon pokemon);
|
||||||
|
|
||||||
|
public IEnumerable<(byte side, byte position)> GetValidTargetsForMove(IPokemon user, ILearnedMove move)
|
||||||
|
{
|
||||||
|
byte GetOppositeSide(byte side) => side == 0 ? (byte)1 : (byte)0;
|
||||||
|
var userBattleData = user.BattleData!;
|
||||||
|
switch (move.MoveData.Target)
|
||||||
|
{
|
||||||
|
case MoveTarget.Adjacent:
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AdjacentAlly:
|
||||||
|
if (userBattleData.Position > 0)
|
||||||
|
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
|
||||||
|
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
||||||
|
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
|
||||||
|
break;
|
||||||
|
case MoveTarget.AdjacentAllySelf:
|
||||||
|
if (userBattleData.Position > 0)
|
||||||
|
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
|
||||||
|
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
||||||
|
yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
|
||||||
|
yield return (userBattleData.SideIndex, userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AdjacentOpponent:
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
||||||
|
if (userBattleData.Position > 0)
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position - 1));
|
||||||
|
if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position + 1));
|
||||||
|
break;
|
||||||
|
case MoveTarget.All:
|
||||||
|
yield return (userBattleData.SideIndex, userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AllAdjacent:
|
||||||
|
yield return (userBattleData.SideIndex, userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AllAdjacentOpponent:
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AllAlly:
|
||||||
|
yield return (userBattleData.SideIndex, userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.AllOpponent:
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.Any:
|
||||||
|
foreach (var side in userBattleData.Battle.Sides)
|
||||||
|
{
|
||||||
|
foreach (var pokemon in side.Pokemon)
|
||||||
|
{
|
||||||
|
if (pokemon?.BattleData == null)
|
||||||
|
continue;
|
||||||
|
yield return (side.Index, pokemon.BattleData!.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MoveTarget.RandomOpponent:
|
||||||
|
yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
|
||||||
|
break;
|
||||||
|
case MoveTarget.SelfUse:
|
||||||
|
yield return (userBattleData.SideIndex, userBattleData.Position);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
PkmnLib.Dynamic/AI/RandomAI.cs
Normal file
45
PkmnLib.Dynamic/AI/RandomAI.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Dynamic.Models.Choices;
|
||||||
|
using PkmnLib.Static.Utils;
|
||||||
|
|
||||||
|
namespace PkmnLib.Dynamic.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An AI that makes random choices.
|
||||||
|
/// </summary>
|
||||||
|
public class RandomAI : PokemonAI
|
||||||
|
{
|
||||||
|
private IRandom _random;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public RandomAI() : base("Random")
|
||||||
|
{
|
||||||
|
_random = new RandomImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override ITurnChoice GetChoice(IBattle battle, IPokemon pokemon)
|
||||||
|
{
|
||||||
|
var moves = pokemon.Moves.Where(x => x?.CurrentPp > 0).ToList();
|
||||||
|
while (moves.Count > 0)
|
||||||
|
{
|
||||||
|
var move = moves[_random.GetInt(moves.Count)];
|
||||||
|
var targets = GetValidTargetsForMove(pokemon, move!).ToArray();
|
||||||
|
if (targets.Length == 0)
|
||||||
|
{
|
||||||
|
moves.Remove(move);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var target = targets[_random.GetInt(targets.Length)];
|
||||||
|
var moveTurnChoice = new MoveChoice(pokemon, move!, target.side, target.position);
|
||||||
|
if (battle.CanUse(moveTurnChoice))
|
||||||
|
{
|
||||||
|
return moveTurnChoice;
|
||||||
|
}
|
||||||
|
moves.Remove(move);
|
||||||
|
}
|
||||||
|
return new PassChoice(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user