diff --git a/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs b/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs index dbafc1f5..32226d2d 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs @@ -494,10 +494,7 @@ public class InsuranceController( ); // Create prob array and add all attachments with rouble price as the weight - var attachmentsProbabilityArray = new ProbabilityObjectArray( - _mathUtil, - _cloner - ); + var attachmentsProbabilityArray = new ProbabilityObjectArray(_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) diff --git a/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs b/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs index 713f7e3a..ace292d2 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs @@ -881,7 +881,6 @@ public class RepeatableQuestController( repeatableConfig ); var targetsConfig = new ProbabilityObjectArray( - _mathUtil, _cloner, eliminationConfig.Targets ); diff --git a/Libraries/SPTarkov.Server.Core/Extensions/MathExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/MathExtensions.cs new file mode 100644 index 00000000..a1f489b8 --- /dev/null +++ b/Libraries/SPTarkov.Server.Core/Extensions/MathExtensions.cs @@ -0,0 +1,59 @@ +namespace SPTarkov.Server.Core.Extensions +{ + public static class MathExtensions + { + /// + /// Helper to create the cumulative sum of all enumerable elements + /// [1, 2, 3, 4].CumulativeSum() = [1, 3, 6, 10] + /// + /// The enumerable with numbers of which to calculate the cumulative sum + /// cumulative sum of values + public static IEnumerable CumulativeSum(this IEnumerable values) + { + double sum = 0; + foreach (var value in values) + { + sum += value; + yield return sum; + } + } + + /// + /// Helper to create the cumulative sum of all enumerable elements + /// [1, 2, 3, 4].CumulativeSum() = [1, 3, 6, 10] + /// + /// The enumerable with numbers of which to calculate the cumulative sum + /// cumulative sum of values + public static IEnumerable CumulativeSum(this IEnumerable values) + { + float sum = 0; + foreach (var value in values) + { + sum += value; + yield return sum; + } + } + + /// + /// Helper to create the product of each element times factor + /// + /// The enumerable of numbers which shall be multiplied by the factor + /// Number to multiply each element by + /// An enumerable of elements all multiplied by the factor + public static IEnumerable Product(this IEnumerable values, double factor) + { + return values.Select(v => v * factor); + } + + /// + /// Helper to create the product of each element times factor + /// + /// The enumerable of numbers which shall be multiplied by the factor + /// Number to multiply each element by + /// An enumerable of elements all multiplied by the factor + public static IEnumerable Product(this IEnumerable values, float factor) + { + return values.Select(v => v * factor); + } + } +} diff --git a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs index 1a358a6b..d6120170 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs @@ -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(_mathUtil, _cloner); + var containerDistribution = new ProbabilityObjectArray(_cloner); foreach (var x in containerIds) { var value = containerData.ContainerIdsWithProbability[x]; containerDistribution.Add(new ProbabilityObject(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(_mathUtil, _cloner); + var itemCountArray = new ProbabilityObjectArray(_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(_mathUtil, _cloner); + var itemDistribution = new ProbabilityObjectArray(_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(_mathUtil, _cloner); + var spawnPointArray = new ProbabilityObjectArray(_cloner); // Positions not in forced but have 100% chance to spawn List 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(_mathUtil, _cloner); + var itemArray = new ProbabilityObjectArray(_cloner); foreach (var itemDist in spawnPoint.ItemDistribution) { if (!validItemIds.Contains(itemDist.ComposedKey.Key)) diff --git a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/EliminationQuestGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/EliminationQuestGenerator.cs index ae9e184f..57dac1bc 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/EliminationQuestGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/EliminationQuestGenerator.cs @@ -333,22 +333,18 @@ public class EliminationQuestGenerator( var locationsConfig = repeatableConfig.Locations; var targetsConfig = new ProbabilityObjectArray( - mathUtil, cloner, eliminationConfig.Targets ); var bodyPartsConfig = new ProbabilityObjectArray>( - mathUtil, cloner, eliminationConfig.BodyParts ); var weaponCategoryRequirementConfig = new ProbabilityObjectArray>( - mathUtil, cloner, eliminationConfig.WeaponCategoryRequirements ); var weaponRequirementConfig = new ProbabilityObjectArray>( - 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 weaponTypeBlacklist = ["Shotgun", "Pistol"]; + { + List 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 weaponTypeBlacklist = ["MarksmanRifle", "DMR"]; + { + List 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 diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs index fb837020..6c94c73e 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs @@ -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)desiredBarterItemIds; + ? [(string) desiredBarterItemIds] + : (List) desiredBarterItemIds; List 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(_mathUtil, _cloner); + var ammoArray = new ProbabilityObjectArray(_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( icd.Tpl, - (double)icd.RelativeProbability, + (double) icd.RelativeProbability, null ) ); diff --git a/Libraries/SPTarkov.Server.Core/Utils/Collections/ProbabilityObjectArray.cs b/Libraries/SPTarkov.Server.Core/Utils/Collections/ProbabilityObjectArray.cs index d802bcca..f39a5711 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/Collections/ProbabilityObjectArray.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/Collections/ProbabilityObjectArray.cs @@ -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 : List> { private readonly ICloner _cloner; - private readonly MathUtil _mathUtil; public ProbabilityObjectArray( - MathUtil mathUtil, ICloner cloner, ICollection>? items = null ) : base(items ?? []) { - _mathUtil = mathUtil; _cloner = cloner; } @@ -43,11 +41,11 @@ public class ProbabilityObjectArray : List> /// Cumulative Sum normalized to 1 public List CumulativeProbability(List 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(); } /// @@ -57,11 +55,7 @@ public class ProbabilityObjectArray : List> /// Filtered results public ProbabilityObjectArray Filter(Predicate> predicate) { - var result = new ProbabilityObjectArray( - _mathUtil, - _cloner, - new List>() - ); + var result = new ProbabilityObjectArray(_cloner, new List>()); foreach (var probabilityObject in this) { if (predicate.Invoke(probabilityObject)) @@ -81,7 +75,6 @@ public class ProbabilityObjectArray : List> { var clone = _cloner.Clone(this); var probabilityObjects = new ProbabilityObjectArray( - _mathUtil, _cloner, new List>() ); @@ -97,7 +90,7 @@ public class ProbabilityObjectArray : List> /// ProbabilityObjectArray without the dropped element public ProbabilityObjectArray Drop(K key) { - return (ProbabilityObjectArray)this.Where(r => !r.Key?.Equals(key) ?? false); + return (ProbabilityObjectArray) this.Where(r => !r.Key?.Equals(key) ?? false); } /// diff --git a/Libraries/SPTarkov.Server.Core/Utils/MathUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/MathUtil.cs index c2b6d7c5..72bcc7ad 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/MathUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/MathUtil.cs @@ -6,58 +6,6 @@ namespace SPTarkov.Server.Core.Utils; [Injectable(InjectionType.Singleton)] public class MathUtil { - /// - /// Helper to create the sum of all list elements - /// - /// List of floats to sum - /// sum of all values - public double ListSum(List values) - { - // Sum the list starting with an initial value of 0 - return values.Sum(); - } - - public float ListSum(List values) - { - // Sum the list starting with an initial value of 0 - return values.Sum(); - } - - /// - /// Helper to create the cumulative sum of all list elements - /// ListCumSum([1, 2, 3, 4]) = [1, 3, 6, 10] - /// - /// The list with numbers of which to calculate the cumulative sum - /// cumulative sum of values - public List ListCumSum(List 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]; - } - - /// - /// Helper to create the product of each element times factor - /// - /// The list of numbers which shall be multiplied by the factor - /// Number to multiply each element by - /// A list of elements all multiplied by the factor - public List ListProduct(List values, double factor) - { - return values.Select(v => v * factor).ToList(); - } - /// /// Helper to add a constant to all list elements /// diff --git a/UnitTests/Tests/Utils/MathUtilTests.cs b/UnitTests/Tests/Utils/MathUtilTests.cs index 6709d1f3..8e94704b 100644 --- a/UnitTests/Tests/Utils/MathUtilTests.cs +++ b/UnitTests/Tests/Utils/MathUtilTests.cs @@ -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 { 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 { 1f, 2f, 3f, 4f }; var expected = new List { 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 { 1f, 2f, 3f, 4f }; var expected = new List { 2f, 4f, 6f, 8f }; - var actual = _mathUtil.ListProduct(test, 2); + var actual = test.Product(2).ToList(); for (var i = 0; i < actual.Count; i++) {