Move MathUtil methods to extensions (#432)
* Begin moving MathUtil methods to Extensions * Add missing extension
This commit is contained in:
@@ -494,10 +494,7 @@ public class InsuranceController(
|
||||
);
|
||||
|
||||
// Create prob array and add all attachments with rouble price as the weight
|
||||
var attachmentsProbabilityArray = new ProbabilityObjectArray<string, double?>(
|
||||
_mathUtil,
|
||||
_cloner
|
||||
);
|
||||
var attachmentsProbabilityArray = new ProbabilityObjectArray<string, double?>(_cloner);
|
||||
foreach (var (itemTpl, price) in weightedAttachmentByPrice)
|
||||
{
|
||||
attachmentsProbabilityArray.Add(
|
||||
@@ -507,7 +504,7 @@ public class InsuranceController(
|
||||
|
||||
// Draw x attachments from weighted array to remove from parent, remove from pool after being picked
|
||||
var attachmentIdsToRemove = attachmentsProbabilityArray.Draw(
|
||||
(int)countOfAttachmentsToRemove,
|
||||
(int) countOfAttachmentsToRemove,
|
||||
false
|
||||
);
|
||||
foreach (var attachmentId in attachmentIdsToRemove)
|
||||
|
||||
@@ -881,7 +881,6 @@ public class RepeatableQuestController(
|
||||
repeatableConfig
|
||||
);
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace SPTarkov.Server.Core.Extensions
|
||||
{
|
||||
public static class MathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper to create the cumulative sum of all enumerable elements
|
||||
/// [1, 2, 3, 4].CumulativeSum() = [1, 3, 6, 10]
|
||||
/// </summary>
|
||||
/// <param name="values">The enumerable with numbers of which to calculate the cumulative sum</param>
|
||||
/// <returns>cumulative sum of values</returns>
|
||||
public static IEnumerable<double> CumulativeSum(this IEnumerable<double> values)
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var value in values)
|
||||
{
|
||||
sum += value;
|
||||
yield return sum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to create the cumulative sum of all enumerable elements
|
||||
/// [1, 2, 3, 4].CumulativeSum() = [1, 3, 6, 10]
|
||||
/// </summary>
|
||||
/// <param name="values">The enumerable with numbers of which to calculate the cumulative sum</param>
|
||||
/// <returns>cumulative sum of values</returns>
|
||||
public static IEnumerable<float> CumulativeSum(this IEnumerable<float> values)
|
||||
{
|
||||
float sum = 0;
|
||||
foreach (var value in values)
|
||||
{
|
||||
sum += value;
|
||||
yield return sum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to create the product of each element times factor
|
||||
/// </summary>
|
||||
/// <param name="values">The enumerable of numbers which shall be multiplied by the factor</param>
|
||||
/// <param name="factor">Number to multiply each element by</param>
|
||||
/// <returns>An enumerable of elements all multiplied by the factor</returns>
|
||||
public static IEnumerable<double> Product(this IEnumerable<double> values, double factor)
|
||||
{
|
||||
return values.Select(v => v * factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to create the product of each element times factor
|
||||
/// </summary>
|
||||
/// <param name="values">The enumerable of numbers which shall be multiplied by the factor</param>
|
||||
/// <param name="factor">Number to multiply each element by</param>
|
||||
/// <returns>An enumerable of elements all multiplied by the factor</returns>
|
||||
public static IEnumerable<float> Product(this IEnumerable<float> values, float factor)
|
||||
{
|
||||
return values.Select(v => v * factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,14 +401,14 @@ public class LocationLootGenerator(
|
||||
}
|
||||
|
||||
// Create probability array with all possible container ids in this group and their relative probability of spawning
|
||||
var containerDistribution = new ProbabilityObjectArray<string, double>(_mathUtil, _cloner);
|
||||
var containerDistribution = new ProbabilityObjectArray<string, double>(_cloner);
|
||||
foreach (var x in containerIds)
|
||||
{
|
||||
var value = containerData.ContainerIdsWithProbability[x];
|
||||
containerDistribution.Add(new ProbabilityObject<string, double>(x, value, value));
|
||||
}
|
||||
|
||||
chosenContainerIds.AddRange(containerDistribution.Draw((int)containerData.ChosenCount));
|
||||
chosenContainerIds.AddRange(containerDistribution.Draw((int) containerData.ChosenCount));
|
||||
|
||||
return chosenContainerIds;
|
||||
}
|
||||
@@ -653,7 +653,7 @@ public class LocationLootGenerator(
|
||||
)
|
||||
{
|
||||
// Create probability array to calculate the total count of lootable items inside container
|
||||
var itemCountArray = new ProbabilityObjectArray<int, float?>(_mathUtil, _cloner);
|
||||
var itemCountArray = new ProbabilityObjectArray<int, float?>(_cloner);
|
||||
var countDistribution = staticLootDist[containerTypeId]?.ItemCountDistribution;
|
||||
if (countDistribution is null)
|
||||
{
|
||||
@@ -698,7 +698,7 @@ public class LocationLootGenerator(
|
||||
var seasonalEventActive = _seasonalEventService.SeasonalEventEnabled();
|
||||
var seasonalItemTplBlacklist = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
|
||||
var itemDistribution = new ProbabilityObjectArray<string, float?>(_mathUtil, _cloner);
|
||||
var itemDistribution = new ProbabilityObjectArray<string, float?>(_cloner);
|
||||
|
||||
var itemContainerDistribution = staticLootDist[containerTypeId]?.ItemDistribution;
|
||||
if (itemContainerDistribution is null)
|
||||
@@ -804,7 +804,7 @@ public class LocationLootGenerator(
|
||||
);
|
||||
|
||||
// Init empty array to hold spawn points, letting us pick them pseudo-randomly
|
||||
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(_mathUtil, _cloner);
|
||||
var spawnPointArray = new ProbabilityObjectArray<string, Spawnpoint>(_cloner);
|
||||
|
||||
// Positions not in forced but have 100% chance to spawn
|
||||
List<Spawnpoint> guaranteedLoosePoints = [];
|
||||
@@ -855,7 +855,7 @@ public class LocationLootGenerator(
|
||||
if (randomSpawnPointCount > 0 && spawnPointArray.Count > 0)
|
||||
// Add randomly chosen spawn points
|
||||
{
|
||||
foreach (var si in spawnPointArray.Draw((int)randomSpawnPointCount, false))
|
||||
foreach (var si in spawnPointArray.Draw((int) randomSpawnPointCount, false))
|
||||
{
|
||||
chosenSpawnPoints.Add(spawnPointArray.Data(si));
|
||||
}
|
||||
@@ -940,7 +940,7 @@ public class LocationLootGenerator(
|
||||
var validItemIds = spawnPoint.Template.Items.Select(item => item.Id).ToHashSet();
|
||||
|
||||
// Construct container to hold above filtered items, letting us pick an item for the spot
|
||||
var itemArray = new ProbabilityObjectArray<string, double?>(_mathUtil, _cloner);
|
||||
var itemArray = new ProbabilityObjectArray<string, double?>(_cloner);
|
||||
foreach (var itemDist in spawnPoint.ItemDistribution)
|
||||
{
|
||||
if (!validItemIds.Contains(itemDist.ComposedKey.Key))
|
||||
|
||||
+18
-22
@@ -333,22 +333,18 @@ public class EliminationQuestGenerator(
|
||||
var locationsConfig = repeatableConfig.Locations;
|
||||
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(
|
||||
mathUtil,
|
||||
cloner,
|
||||
eliminationConfig.Targets
|
||||
);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
mathUtil,
|
||||
cloner,
|
||||
eliminationConfig.BodyParts
|
||||
);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
mathUtil,
|
||||
cloner,
|
||||
eliminationConfig.WeaponCategoryRequirements
|
||||
);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<string>>(
|
||||
mathUtil,
|
||||
cloner,
|
||||
eliminationConfig.WeaponRequirements
|
||||
);
|
||||
@@ -609,9 +605,9 @@ public class EliminationQuestGenerator(
|
||||
+ generationData.EliminationConfig.MinDistance
|
||||
);
|
||||
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5d)) * 5;
|
||||
distance = (int) Math.Ceiling((decimal) (distance / 5d)) * 5;
|
||||
|
||||
var distanceDifficulty = (int)(
|
||||
var distanceDifficulty = (int) (
|
||||
MaxDistDifficulty * distance / generationData.EliminationConfig.MaxDistance
|
||||
);
|
||||
|
||||
@@ -633,26 +629,26 @@ public class EliminationQuestGenerator(
|
||||
{
|
||||
// Filter out close range weapons from far distance requirement
|
||||
case > 50:
|
||||
{
|
||||
List<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
|
||||
{
|
||||
List<string> weaponTypeBlacklist = ["Shotgun", "Pistol"];
|
||||
|
||||
// Filter out close range weapons from long distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Filter out close range weapons from long distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Filter out long range weapons from close distance requirement
|
||||
case < 20:
|
||||
{
|
||||
List<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
|
||||
{
|
||||
List<string> weaponTypeBlacklist = ["MarksmanRifle", "DMR"];
|
||||
|
||||
// Filter out far range weapons from close distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Filter out far range weapons from close distance requirement
|
||||
generationData.WeaponCategoryRequirementConfig.RemoveAll(category =>
|
||||
weaponTypeBlacklist.Contains(category.Key)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a weighted weapon category
|
||||
|
||||
@@ -433,7 +433,7 @@ public class ItemHelper(
|
||||
// Run getItemPrice for each tpl in tpls array, return sum
|
||||
return tpls.Aggregate(
|
||||
0,
|
||||
(total, tpl) => total + (int)GetItemPrice(tpl).GetValueOrDefault(0)
|
||||
(total, tpl) => total + (int) GetItemPrice(tpl).GetValueOrDefault(0)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -902,8 +902,8 @@ public class ItemHelper(
|
||||
// Find required items to take after buying (handles multiple items)
|
||||
var desiredBarterIds =
|
||||
desiredBarterItemIds.GetType() == typeof(string)
|
||||
? [(string)desiredBarterItemIds]
|
||||
: (List<string>)desiredBarterItemIds;
|
||||
? [(string) desiredBarterItemIds]
|
||||
: (List<string>) desiredBarterItemIds;
|
||||
|
||||
List<Item> matchingItems = [];
|
||||
foreach (var barterId in desiredBarterIds)
|
||||
@@ -1456,7 +1456,7 @@ public class ItemHelper(
|
||||
var cartridgeItemToAdd = CreateCartridges(
|
||||
ammoBox[0].Id,
|
||||
cartridgeTpl,
|
||||
(int)cartridgeCountToAdd,
|
||||
(int) cartridgeCountToAdd,
|
||||
location
|
||||
);
|
||||
|
||||
@@ -1485,7 +1485,7 @@ public class ItemHelper(
|
||||
0
|
||||
].Filter?.FirstOrDefault();
|
||||
ammoBox.Add(
|
||||
CreateCartridges(ammoBox[0].Id, cartridgeTpl, (int)ammoBoxMaxCartridgeCount, 0)
|
||||
CreateCartridges(ammoBox[0].Id, cartridgeTpl, (int) ammoBoxMaxCartridgeCount, 0)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1593,8 +1593,8 @@ public class ItemHelper(
|
||||
}
|
||||
|
||||
var desiredStackCount = _randomUtil.GetInt(
|
||||
(int)Math.Round(minSizeMultiplier * magazineCartridgeMaxCount ?? 0),
|
||||
(int)magazineCartridgeMaxCount
|
||||
(int) Math.Round(minSizeMultiplier * magazineCartridgeMaxCount ?? 0),
|
||||
(int) magazineCartridgeMaxCount
|
||||
);
|
||||
|
||||
if (magazineWithChildCartridges.Count > 1)
|
||||
@@ -1693,7 +1693,7 @@ public class ItemHelper(
|
||||
return null;
|
||||
}
|
||||
|
||||
var ammoArray = new ProbabilityObjectArray<string, float?>(_mathUtil, _cloner);
|
||||
var ammoArray = new ProbabilityObjectArray<string, float?>(_cloner);
|
||||
foreach (var icd in ammos)
|
||||
{
|
||||
// Whitelist exists and tpl not inside it, skip
|
||||
@@ -1706,7 +1706,7 @@ public class ItemHelper(
|
||||
ammoArray.Add(
|
||||
new ProbabilityObject<string, float?>(
|
||||
icd.Tpl,
|
||||
(double)icd.RelativeProbability,
|
||||
(double) icd.RelativeProbability,
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Utils.Cloners;
|
||||
|
||||
namespace SPTarkov.Server.Core.Utils.Collections;
|
||||
@@ -23,16 +24,13 @@ namespace SPTarkov.Server.Core.Utils.Collections;
|
||||
public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
{
|
||||
private readonly ICloner _cloner;
|
||||
private readonly MathUtil _mathUtil;
|
||||
|
||||
public ProbabilityObjectArray(
|
||||
MathUtil mathUtil,
|
||||
ICloner cloner,
|
||||
ICollection<ProbabilityObject<K, V>>? items = null
|
||||
)
|
||||
: base(items ?? [])
|
||||
{
|
||||
_mathUtil = mathUtil;
|
||||
_cloner = cloner;
|
||||
}
|
||||
|
||||
@@ -43,11 +41,11 @@ public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
/// <returns>Cumulative Sum normalized to 1</returns>
|
||||
public List<double> CumulativeProbability(List<double> probValues)
|
||||
{
|
||||
var sum = _mathUtil.ListSum(probValues);
|
||||
var probCumsum = _mathUtil.ListCumSum(probValues);
|
||||
probCumsum = _mathUtil.ListProduct(probCumsum, 1D / sum);
|
||||
var sum = probValues.Sum();
|
||||
var probCumsum = probValues.CumulativeSum();
|
||||
probCumsum = probCumsum.Product(1D / sum);
|
||||
|
||||
return probCumsum;
|
||||
return probCumsum.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,11 +55,7 @@ public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
/// <returns>Filtered results</returns>
|
||||
public ProbabilityObjectArray<K, V> Filter(Predicate<ProbabilityObject<K, V>> predicate)
|
||||
{
|
||||
var result = new ProbabilityObjectArray<K, V>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
new List<ProbabilityObject<K, V>>()
|
||||
);
|
||||
var result = new ProbabilityObjectArray<K, V>(_cloner, new List<ProbabilityObject<K, V>>());
|
||||
foreach (var probabilityObject in this)
|
||||
{
|
||||
if (predicate.Invoke(probabilityObject))
|
||||
@@ -81,7 +75,6 @@ public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
{
|
||||
var clone = _cloner.Clone(this);
|
||||
var probabilityObjects = new ProbabilityObjectArray<K, V>(
|
||||
_mathUtil,
|
||||
_cloner,
|
||||
new List<ProbabilityObject<K, V>>()
|
||||
);
|
||||
@@ -97,7 +90,7 @@ public class ProbabilityObjectArray<K, V> : List<ProbabilityObject<K, V>>
|
||||
/// <returns>ProbabilityObjectArray without the dropped element</returns>
|
||||
public ProbabilityObjectArray<K, V> Drop(K key)
|
||||
{
|
||||
return (ProbabilityObjectArray<K, V>)this.Where(r => !r.Key?.Equals(key) ?? false);
|
||||
return (ProbabilityObjectArray<K, V>) this.Where(r => !r.Key?.Equals(key) ?? false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,58 +6,6 @@ namespace SPTarkov.Server.Core.Utils;
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class MathUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper to create the sum of all list elements
|
||||
/// </summary>
|
||||
/// <param name="values">List of floats to sum</param>
|
||||
/// <returns>sum of all values</returns>
|
||||
public double ListSum(List<double> values)
|
||||
{
|
||||
// Sum the list starting with an initial value of 0
|
||||
return values.Sum();
|
||||
}
|
||||
|
||||
public float ListSum(List<float> values)
|
||||
{
|
||||
// Sum the list starting with an initial value of 0
|
||||
return values.Sum();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to create the cumulative sum of all list elements
|
||||
/// ListCumSum([1, 2, 3, 4]) = [1, 3, 6, 10]
|
||||
/// </summary>
|
||||
/// <param name="values">The list with numbers of which to calculate the cumulative sum</param>
|
||||
/// <returns>cumulative sum of values</returns>
|
||||
public List<double> ListCumSum(List<double> values)
|
||||
{
|
||||
if (values.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var cumSumArray = new double[values.Count];
|
||||
cumSumArray[0] = values[0];
|
||||
|
||||
for (var i = 1; i < values.Count; i++)
|
||||
{
|
||||
cumSumArray[i] = cumSumArray[i - 1] + values[i];
|
||||
}
|
||||
|
||||
return [.. cumSumArray];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to create the product of each element times factor
|
||||
/// </summary>
|
||||
/// <param name="values">The list of numbers which shall be multiplied by the factor</param>
|
||||
/// <param name="factor">Number to multiply each element by</param>
|
||||
/// <returns>A list of elements all multiplied by the factor</returns>
|
||||
public List<double> ListProduct(List<double> values, double factor)
|
||||
{
|
||||
return values.Select(v => v * factor).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to add a constant to all list elements
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
|
||||
namespace UnitTests.Tests.Utils;
|
||||
@@ -19,7 +20,7 @@ public class MathUtilTests
|
||||
var test = new List<float> { 1.1f, 2.1f, 3.3f };
|
||||
const double expected = 6.5f;
|
||||
|
||||
var actual = _mathUtil.ListSum(test);
|
||||
var actual = test.Sum();
|
||||
|
||||
Assert.AreEqual(expected, actual, $"ListSum() Expected: {expected}, Actual: {actual}");
|
||||
}
|
||||
@@ -30,7 +31,7 @@ public class MathUtilTests
|
||||
var test = new List<double> { 1f, 2f, 3f, 4f };
|
||||
var expected = new List<double> { 1f, 3f, 6f, 10f };
|
||||
|
||||
var actual = _mathUtil.ListCumSum(test);
|
||||
var actual = test.CumulativeSum().ToList();
|
||||
|
||||
for (var i = 0; i < actual.Count; i++)
|
||||
{
|
||||
@@ -49,7 +50,7 @@ public class MathUtilTests
|
||||
var test = new List<double> { 1f, 2f, 3f, 4f };
|
||||
var expected = new List<double> { 2f, 4f, 6f, 8f };
|
||||
|
||||
var actual = _mathUtil.ListProduct(test, 2);
|
||||
var actual = test.Product(2).ToList();
|
||||
|
||||
for (var i = 0; i < actual.Count; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user