Skip to content

Commit

Permalink
Merge pull request #2018 from planetarium/improve/has-execute
Browse files Browse the repository at this point in the history
Optimize HackAndSlash.Execute #1
  • Loading branch information
U-lis authored Jul 19, 2023
2 parents ddb70d8 + 8f4aef5 commit f76932c
Show file tree
Hide file tree
Showing 40 changed files with 407 additions and 400 deletions.
34 changes: 34 additions & 0 deletions .Lib9c.Tests/Action/HitHelperTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Lib9c.Tests.Action
{
using System;
using Nekoyume.Battle;
using Xunit;

public class HitHelperTest
{
[Fact]
public void GetHitStep2()
{
// copy from previous logic
int GetHitStep2Legacy(int attackerHit, int defenderHit)
{
attackerHit = Math.Max(1, attackerHit);
defenderHit = Math.Max(1, defenderHit);
var additionalCorrection = (int)((attackerHit - defenderHit / 3m) / defenderHit * 100);
return Math.Min(
Math.Max(additionalCorrection, HitHelper.GetHitStep2AdditionalCorrectionMin),
HitHelper.GetHitStep2AdditionalCorrectionMax);
}

for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
var legacy = GetHitStep2Legacy(i, j);
var current = HitHelper.GetHitStep2(i, j);
Assert.True(legacy == current, $"{i}, {j}, {legacy}, {current}");
}
}
}
}
}
34 changes: 32 additions & 2 deletions .Lib9c.Tests/Model/Skill/CombatTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void CalculateDEFAndDamageReduction(int def, int drv, int drr, int enemyA
var normalAttack = new NormalAttack(skillRow, 0, 100, default, StatType.NONE);

var prevHP = _player.CurrentHP;
normalAttack.Use(_enemy, 1, new List<StatBuff>());
normalAttack.Use(_enemy, 1, new List<StatBuff>(), false);
var currentHP = _player.CurrentHP;
var damage = prevHP - currentHP;

Expand All @@ -100,11 +100,41 @@ public void CalculateCritDamage(int cdmg, int atk, int expectedDamage)
var normalAttack = new NormalAttack(skillRow, 0, 100, default, StatType.NONE);

var prevHP = _player.CurrentHP;
normalAttack.Use(_enemy, 1, new List<StatBuff>());
normalAttack.Use(_enemy, 1, new List<StatBuff>(), false);
var currentHP = _player.CurrentHP;
var damage = prevHP - currentHP;

Assert.Equal(expectedDamage, damage);
}

[Fact]
public void Thorn()
{
var prevHP = _enemy.CurrentHP;
var skill = _enemy.GiveThornDamage(1);
var currentHP = _enemy.CurrentHP;
// get 1dmg from thorn
Assert.Equal(prevHP - 1, currentHP);
Assert.Equal(prevHP, skill.Character.CurrentHP);
var skillInfo = Assert.Single(skill.SkillInfos);
Assert.Equal(currentHP, skillInfo.Target!.CurrentHP);
}

[Fact]
public void Bleed()
{
var actionBuffSheet = _tableSheets.ActionBuffSheet;
var row = actionBuffSheet.Values.First();
var bleed = Assert.IsType<Bleed>(BuffFactory.GetActionBuff(_enemy.Stats, row));
var dmg = bleed.Power;
var prevHP = _player.CurrentHP;
var skill = bleed.GiveEffect(_player, 1);
var currentHP = _player.CurrentHP;
// get dmg from bleed
Assert.Equal(prevHP - dmg, currentHP);
Assert.Equal(prevHP, skill.Character.CurrentHP);
var skillInfo = Assert.Single(skill.SkillInfos);
Assert.Equal(currentHP, skillInfo.Target!.CurrentHP);
}
}
}
21 changes: 12 additions & 9 deletions .Lib9c.Tests/Model/Skill/NormalAttackTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ public NormalAttackTest(ITestOutputHelper outputHelper)
.CreateLogger();
}

[Fact]
public void Use()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Use(bool copyCharacter)
{
var sheets = TableSheetsImporter.ImportSheets();
var tableSheets = new TableSheets(sheets);
Expand Down Expand Up @@ -65,7 +67,8 @@ public void Use()
StageSimulator.GetWaveRewards(
random,
tableSheets.StageSheet[1],
tableSheets.MaterialItemSheet)
tableSheets.MaterialItemSheet),
copyCharacter
);
var player = new Player(avatarState, simulator);

Expand All @@ -79,13 +82,13 @@ public void Use()
var battleStatusSkill = normalAttack.Use(
player,
0,
new List<StatBuff>());
new List<StatBuff>(),
copyCharacter);
Assert.NotNull(battleStatusSkill);
Assert.Single(battleStatusSkill.SkillInfos);

var skillInfo = battleStatusSkill.SkillInfos.FirstOrDefault();
Assert.NotNull(skillInfo);
Assert.Equal(enemy.Id, skillInfo.Target.Id);
Assert.Equal(!copyCharacter, battleStatusSkill.Character is null);
var skillInfo = Assert.Single(battleStatusSkill.SkillInfos);
Assert.Equal(enemy.Id, skillInfo.CharacterId);
Assert.Equal(!copyCharacter, skillInfo.Target is null);
}
}
}
40 changes: 26 additions & 14 deletions Lib9c/Action/HackAndSlash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,14 @@ public IAccountStateDelta Execute(
}
}

var stageWaveRow = sheets.GetSheet<StageWaveSheet>()[StageId];
var enemySkillSheet = sheets.GetSheet<EnemySkillSheet>();
var costumeStatSheet = sheets.GetSheet<CostumeStatSheet>();
var stageCleared = !isNotClearedStage;
var starCount = 0;
for (var i = 0; i < TotalPlayCount; i++)
{
var rewards = StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet);
sw.Restart();
// First simulating will use Foods and Random Skills.
// Remainder simulating will not use Foods.
Expand All @@ -453,13 +459,14 @@ public IAccountStateDelta Execute(
WorldId,
StageId,
stageRow,
sheets.GetSheet<StageWaveSheet>()[StageId],
avatarState.worldInformation.IsStageCleared(StageId),
stageWaveRow,
stageCleared,
StageRewardExpHelper.GetExp(avatarState.level, StageId),
simulatorSheets,
sheets.GetSheet<EnemySkillSheet>(),
sheets.GetSheet<CostumeStatSheet>(),
StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet));
enemySkillSheet,
costumeStatSheet,
rewards,
false);
sw.Stop();
Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed);

Expand All @@ -471,13 +478,17 @@ public IAccountStateDelta Execute(
sw.Restart();
if (simulator.Log.IsClear)
{
simulator.Player.worldInformation.ClearStage(
WorldId,
StageId,
blockIndex,
worldSheet,
worldUnlockSheet
);
if (!stageCleared)
{
avatarState.worldInformation.ClearStage(
WorldId,
StageId,
blockIndex,
worldSheet,
worldUnlockSheet
);
stageCleared = true;
}
sw.Stop();
Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed);
}
Expand All @@ -504,9 +515,8 @@ public IAccountStateDelta Execute(
player.eventMapForBeforeV100310.Clear();
}

starCount += simulator.Log.clearedWaveNumber;
avatarState.Update(simulator);
// Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add)
skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet);

sw.Stop();
Log.Verbose(
Expand All @@ -526,6 +536,8 @@ public IAccountStateDelta Execute(
Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}",
addressesHex, sw.Elapsed, TotalPlayCount);

// Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add)
skillState?.Update(starCount, crystalStageBuffSheet);
sw.Restart();
avatarState.UpdateQuestRewards(materialItemSheet);
avatarState.updatedAt = blockIndex;
Expand Down
127 changes: 51 additions & 76 deletions Lib9c/Battle/AttackCountHelper.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Nekoyume.Battle
{
public static class AttackCountHelper
{
public struct Info
{
public decimal DamageMultiplier;
public decimal AdditionalCriticalChance;
}

public const int CountMaxLowerLimit = 2;
public const int CountMaxUpperLimit = 5;

/// <summary>
/// key: attack count max
/// value: attack count, info
/// </summary>
public static readonly IReadOnlyDictionary<int, IReadOnlyDictionary<int, Info>> CachedInfo =
new Dictionary<int, IReadOnlyDictionary<int, Info>>
{
{
1, new Dictionary<int, Info>
{
{1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}}
}
},
{
2, new Dictionary<int, Info>
{
{1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}},
{2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 25m}}
}
},
{
3, new Dictionary<int, Info>
{
{1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}},
{2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}},
{3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 35m}}
}
},
{
4, new Dictionary<int, Info>
{
{1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}},
{2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}},
{3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 20m}},
{4, new Info {DamageMultiplier = 4m, AdditionalCriticalChance = 45m}}
}
},
{
5, new Dictionary<int, Info>
{
{1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}},
{2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}},
{3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 20m}},
{4, new Info {DamageMultiplier = 4m, AdditionalCriticalChance = 30m}},
{5, new Info {DamageMultiplier = 5m, AdditionalCriticalChance = 55m}}
}
}
};

public static int GetCountMax(int level)
{
if (level < 11)
Expand All @@ -76,40 +22,69 @@ public static int GetCountMax(int level)
: CountMaxUpperLimit;
}

public static decimal GetDamageMultiplier(int attackCount, int attackCountMax)
public static int GetDamageMultiplier(int attackCount, int attackCountMax)
{
if (attackCount > attackCountMax)
throw new ArgumentOutOfRangeException(
$"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}");

var info = GetInfo(attackCount, attackCountMax);
return info.DamageMultiplier;
}

public static decimal GetAdditionalCriticalChance(int attackCount, int attackCountMax)
{
if (attackCount > attackCountMax)
throw new ArgumentOutOfRangeException(
$"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}");
if (attackCountMax <= 5)
{
return Math.Max(1, attackCount);
}

var info = GetInfo(attackCount, attackCountMax);
return info.AdditionalCriticalChance;
throw new ArgumentOutOfRangeException();
}

private static Info GetInfo(int attackCount, int attackCountMax)
public static int GetAdditionalCriticalChance(int attackCount, int attackCountMax)
{
if (attackCount > attackCountMax)
throw new ArgumentOutOfRangeException(
$"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}");
switch (attackCount)
{
case 1:
return 0;
case 2:
switch (attackCountMax)
{
case 2:
return 25;
case 3:
case 4:
case 5:
return 10;
}
break;
case 3:
switch (attackCountMax)
{
case 3:
return 35;
case 4:
case 5:
return 20;
}
break;
case 4:
switch (attackCountMax)
{
case 4:
return 45;
case 5:
return 30;
}
break;
case 5:
switch (attackCountMax)
{
case 5:
return 55;
}
break;
}

if (!CachedInfo.ContainsKey(attackCountMax))
throw new ArgumentOutOfRangeException($"{nameof(attackCountMax)}: {attackCountMax}");

if (!CachedInfo[attackCountMax].ContainsKey(attackCount))
throw new ArgumentOutOfRangeException(
$"{nameof(attackCountMax)}: {attackCountMax} / {nameof(attackCount)}: {attackCount}");

return CachedInfo[attackCountMax][attackCount];
throw new ArgumentOutOfRangeException();
}
}
}
Loading

0 comments on commit f76932c

Please sign in to comment.