Adds unit test for gen 7 damage library
This commit is contained in:
parent
9186d0efcc
commit
864dda6644
|
@ -10,12 +10,12 @@ public interface IDamageCalculator
|
|||
/// <summary>
|
||||
/// Calculate the damage for a given hit on a Pokemon.
|
||||
/// </summary>
|
||||
uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData);
|
||||
uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the base power for a given hit on a Pokemon.
|
||||
/// </summary>
|
||||
byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData);
|
||||
byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a specified hit should be critical or not.
|
||||
|
|
|
@ -4,44 +4,71 @@ using PkmnLib.Static.Moves;
|
|||
|
||||
namespace PkmnLib.Dynamic.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A hit data is the data for a single hit, on a single target.
|
||||
/// </summary>
|
||||
public record HitData
|
||||
public interface IHitData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the hit is critical.
|
||||
/// </summary>
|
||||
public bool IsCritical { get; internal set; }
|
||||
bool IsCritical { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The base power of the hit.
|
||||
/// </summary>
|
||||
public byte BasePower { get; internal set; }
|
||||
byte BasePower { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The effectiveness of the hit.
|
||||
/// </summary>
|
||||
public float Effectiveness { get; internal set; }
|
||||
float Effectiveness { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The damage done by the hit.
|
||||
/// </summary>
|
||||
public uint Damage { get; internal set; }
|
||||
uint Damage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the hit.
|
||||
/// </summary>
|
||||
public TypeIdentifier Type { get; internal set; }
|
||||
TypeIdentifier Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the hit has failed.
|
||||
/// </summary>
|
||||
public bool HasFailed { get; private set; }
|
||||
bool HasFailed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Fails the hit.
|
||||
/// </summary>
|
||||
void Fail();
|
||||
|
||||
bool Equals(HitData? other);
|
||||
bool Equals(object? other);
|
||||
int GetHashCode();
|
||||
string ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public record HitData : IHitData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsCritical { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte BasePower { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public float Effectiveness { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Damage { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TypeIdentifier Type { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasFailed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Fail() => HasFailed = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PkmnLib.Plugin.Gen7", "Pkmn
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{63C1B450-DC26-444A-AEBD-15979F3EEE53}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PkmnLib.Plugin.Gen7.Tests", "PkmnLib.Plugin.Gen7.Tests\PkmnLib.Plugin.Gen7.Tests.csproj", "{FBB53861-081F-4DAC-B006-79EE238D0DFC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -32,8 +34,13 @@ Global
|
|||
{FA5380F0-28CC-4AEC-8963-814B347A89BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA5380F0-28CC-4AEC-8963-814B347A89BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA5380F0-28CC-4AEC-8963-814B347A89BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FBB53861-081F-4DAC-B006-79EE238D0DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FBB53861-081F-4DAC-B006-79EE238D0DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FBB53861-081F-4DAC-B006-79EE238D0DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FBB53861-081F-4DAC-B006-79EE238D0DFC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{FA5380F0-28CC-4AEC-8963-814B347A89BA} = {63C1B450-DC26-444A-AEBD-15979F3EEE53}
|
||||
{FBB53861-081F-4DAC-B006-79EE238D0DFC} = {63C1B450-DC26-444A-AEBD-15979F3EEE53}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
namespace PkmnLib.Plugin.Gen7.Tests;
|
||||
|
||||
public class BattleStatCalculatorTests
|
||||
{
|
||||
[Test]
|
||||
public void Test1()
|
||||
{
|
||||
Assert.Pass();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Plugin.Gen7.Libraries;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests;
|
||||
|
||||
public class DamageCalculatorTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the following example from Bulbapedia:
|
||||
/// </summary>
|
||||
/// <para>
|
||||
/// Imagine a level 75 Glaceon that does not suffer a burn and holds no item with an effective Attack stat of 123
|
||||
/// uses Ice Fang (an Ice-type physical move with a power of 65) against a Garchomp with an effective Defense stat
|
||||
/// of 163 in Generation VI, and does not land a critical hit. Then, the move will receive STAB, because Glaceon's
|
||||
/// Ice type matches the move's: STAB = 1.5. Additionally, Garchomp is Dragon/Ground, and therefore has a double
|
||||
/// weakness to the move's Ice type: Type = 4. All other (non-random) modifiers will be 1.
|
||||
///
|
||||
/// That means Ice Fang will do between 168 and 196 HP damage, depending on luck.
|
||||
/// </para>
|
||||
[Test]
|
||||
public void BulbapediaExampleDamageTest()
|
||||
{
|
||||
var attacker = new Mock<IPokemon>();
|
||||
// Imagine a level 75 Glaceon
|
||||
attacker.Setup(x => x.Level).Returns(75);
|
||||
// with an effective Attack stat of 123
|
||||
attacker.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
||||
1, 123, 1, 1, 1, 1));
|
||||
// We use 10 as the Ice type
|
||||
attacker.Setup(x => x.Types).Returns([new TypeIdentifier(10)]);
|
||||
|
||||
var defender = new Mock<IPokemon>();
|
||||
// a Garchomp with an effective Defense stat of 163
|
||||
defender.Setup(x => x.BoostedStats).Returns(new StatisticSet<uint>(
|
||||
1, 1, 163, 1, 1, 1));
|
||||
|
||||
var useMove = new Mock<IMoveData>();
|
||||
// Ice Fang (an Ice-type physical move with a power of 65)
|
||||
useMove.Setup(x => x.Category).Returns(MoveCategory.Physical);
|
||||
|
||||
var damageCalculator = new Gen7DamageCalculator(false);
|
||||
var executingMove = new Mock<IExecutingMove>();
|
||||
executingMove.Setup(x => x.UseMove).Returns(useMove.Object);
|
||||
executingMove.Setup(x => x.User).Returns(attacker.Object);
|
||||
executingMove.Setup(x => x.GetScripts()).Returns(new ScriptIterator([]));
|
||||
|
||||
var hit = new Mock<IHitData>();
|
||||
// Ice Fang (an Ice-type physical move with a power of 65)
|
||||
hit.Setup(x => x.BasePower).Returns(65);
|
||||
hit.Setup(x => x.Type).Returns(new TypeIdentifier(10));
|
||||
// has a double weakness to the move's Ice type
|
||||
hit.Setup(x => x.Effectiveness).Returns(4.0f);
|
||||
|
||||
var damage = damageCalculator.GetDamage(executingMove.Object, defender.Object, 0, hit.Object);
|
||||
// That means Ice Fang will do between 168 and 196 HP damage, depending on luck.
|
||||
// Note that we are testing deterministic damage, so we expect the maximum damage.
|
||||
Assert.That(damage, Is.EqualTo(196));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
global using NUnit.Framework;
|
||||
global using Moq;
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.6.1"/>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PkmnLib.Dynamic\PkmnLib.Dynamic.csproj" />
|
||||
<ProjectReference Include="..\PkmnLib.Plugin.Gen7\PkmnLib.Plugin.Gen7.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -48,7 +48,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
return (uint)boostedStat;
|
||||
}
|
||||
|
||||
private uint CalculateHealthStat(IPokemon pokemon)
|
||||
private static uint CalculateHealthStat(IPokemon pokemon)
|
||||
{
|
||||
var baseValue = (ulong)pokemon.Form.BaseStats.Hp;
|
||||
var iv = (ulong)pokemon.IndividualValues.Hp;
|
||||
|
@ -59,7 +59,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
return (uint)health;
|
||||
}
|
||||
|
||||
private uint CalculateNormalStat(IPokemon pokemon, Statistic statistic)
|
||||
private static uint CalculateNormalStat(IPokemon pokemon, Statistic statistic)
|
||||
{
|
||||
var baseValue = (ulong)pokemon.Form.BaseStats.GetStatistic(statistic);
|
||||
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
||||
|
@ -72,7 +72,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||
return (uint)modified;
|
||||
}
|
||||
|
||||
private float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
||||
private static float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
||||
{
|
||||
var boost = pokemon.StatBoost.GetStatistic(statistic);
|
||||
return boost switch
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace PkmnLib.Plugin.Gen7.Libraries;
|
|||
public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData)
|
||||
public uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
var category = executingMove.UseMove.Category;
|
||||
if (category == MoveCategory.Status)
|
||||
|
@ -71,7 +71,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData)
|
||||
public byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||
return 0;
|
||||
|
@ -99,7 +99,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
};
|
||||
}
|
||||
|
||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData)
|
||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
var category = executingMove.UseMove.Category;
|
||||
if (category == MoveCategory.Status)
|
||||
|
@ -146,7 +146,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||
/// to apply a raw modifier to the damage.
|
||||
/// </summary>
|
||||
private static float GetDamageModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
||||
HitData hitData)
|
||||
IHitData hitData)
|
||||
{
|
||||
var modifier = 1.0f;
|
||||
|
||||
|
|
Loading…
Reference in New Issue