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>
|
/// <summary>
|
||||||
/// Calculate the damage for a given hit on a Pokemon.
|
/// Calculate the damage for a given hit on a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData);
|
uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate the base power for a given hit on a Pokemon.
|
/// Calculate the base power for a given hit on a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, HitData hitData);
|
byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a specified hit should be critical or not.
|
/// Returns whether a specified hit should be critical or not.
|
||||||
|
@ -4,44 +4,71 @@ using PkmnLib.Static.Moves;
|
|||||||
|
|
||||||
namespace PkmnLib.Dynamic.Models;
|
namespace PkmnLib.Dynamic.Models;
|
||||||
|
|
||||||
/// <summary>
|
public interface IHitData
|
||||||
/// A hit data is the data for a single hit, on a single target.
|
|
||||||
/// </summary>
|
|
||||||
public record HitData
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the hit is critical.
|
/// Whether the hit is critical.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCritical { get; internal set; }
|
bool IsCritical { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base power of the hit.
|
/// The base power of the hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte BasePower { get; internal set; }
|
byte BasePower { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The effectiveness of the hit.
|
/// The effectiveness of the hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Effectiveness { get; internal set; }
|
float Effectiveness { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The damage done by the hit.
|
/// The damage done by the hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Damage { get; internal set; }
|
uint Damage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of the hit.
|
/// The type of the hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TypeIdentifier Type { get; internal set; }
|
TypeIdentifier Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the hit has failed.
|
/// Whether the hit has failed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasFailed { get; private set; }
|
bool HasFailed { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fails the hit.
|
/// Fails the hit.
|
||||||
/// </summary>
|
/// </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;
|
public void Fail() => HasFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PkmnLib.Plugin.Gen7", "Pkmn
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{63C1B450-DC26-444A-AEBD-15979F3EEE53}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{63C1B450-DC26-444A-AEBD-15979F3EEE53}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{FA5380F0-28CC-4AEC-8963-814B347A89BA}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{FA5380F0-28CC-4AEC-8963-814B347A89BA} = {63C1B450-DC26-444A-AEBD-15979F3EEE53}
|
{FA5380F0-28CC-4AEC-8963-814B347A89BA} = {63C1B450-DC26-444A-AEBD-15979F3EEE53}
|
||||||
|
{FBB53861-081F-4DAC-B006-79EE238D0DFC} = {63C1B450-DC26-444A-AEBD-15979F3EEE53}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
10
PkmnLib.Plugin.Gen7.Tests/BattleStatCalculatorTests.cs
Normal file
10
PkmnLib.Plugin.Gen7.Tests/BattleStatCalculatorTests.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace PkmnLib.Plugin.Gen7.Tests;
|
||||||
|
|
||||||
|
public class BattleStatCalculatorTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Test1()
|
||||||
|
{
|
||||||
|
Assert.Pass();
|
||||||
|
}
|
||||||
|
}
|
62
PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs
Normal file
62
PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
2
PkmnLib.Plugin.Gen7.Tests/GlobalUsings.cs
Normal file
2
PkmnLib.Plugin.Gen7.Tests/GlobalUsings.cs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
global using NUnit.Framework;
|
||||||
|
global using Moq;
|
26
PkmnLib.Plugin.Gen7.Tests/PkmnLib.Plugin.Gen7.Tests.csproj
Normal file
26
PkmnLib.Plugin.Gen7.Tests/PkmnLib.Plugin.Gen7.Tests.csproj
Normal file
@ -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;
|
return (uint)boostedStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint CalculateHealthStat(IPokemon pokemon)
|
private static uint CalculateHealthStat(IPokemon pokemon)
|
||||||
{
|
{
|
||||||
var baseValue = (ulong)pokemon.Form.BaseStats.Hp;
|
var baseValue = (ulong)pokemon.Form.BaseStats.Hp;
|
||||||
var iv = (ulong)pokemon.IndividualValues.Hp;
|
var iv = (ulong)pokemon.IndividualValues.Hp;
|
||||||
@ -59,7 +59,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||||||
return (uint)health;
|
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 baseValue = (ulong)pokemon.Form.BaseStats.GetStatistic(statistic);
|
||||||
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
||||||
@ -72,7 +72,7 @@ public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|||||||
return (uint)modified;
|
return (uint)modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
private static float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
||||||
{
|
{
|
||||||
var boost = pokemon.StatBoost.GetStatistic(statistic);
|
var boost = pokemon.StatBoost.GetStatistic(statistic);
|
||||||
return boost switch
|
return boost switch
|
||||||
|
@ -12,7 +12,7 @@ namespace PkmnLib.Plugin.Gen7.Libraries;
|
|||||||
public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <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;
|
var category = executingMove.UseMove.Category;
|
||||||
if (category == MoveCategory.Status)
|
if (category == MoveCategory.Status)
|
||||||
@ -71,7 +71,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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)
|
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||||
return 0;
|
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;
|
var category = executingMove.UseMove.Category;
|
||||||
if (category == MoveCategory.Status)
|
if (category == MoveCategory.Status)
|
||||||
@ -146,7 +146,7 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
|||||||
/// to apply a raw modifier to the damage.
|
/// to apply a raw modifier to the damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static float GetDamageModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
private static float GetDamageModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
||||||
HitData hitData)
|
IHitData hitData)
|
||||||
{
|
{
|
||||||
var modifier = 1.0f;
|
var modifier = 1.0f;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user