SharedRandomExtensions.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using Content.Shared.Dataset;
  4. using Content.Shared.FixedPoint;
  5. using Robust.Shared.Random;
  6. namespace Content.Shared.Random.Helpers
  7. {
  8. public static class SharedRandomExtensions
  9. {
  10. public static string Pick(this IRobustRandom random, DatasetPrototype prototype)
  11. {
  12. return random.Pick(prototype.Values);
  13. }
  14. /// <summary>
  15. /// Randomly selects an entry from <paramref name="prototype"/>, attempts to localize it, and returns the result.
  16. /// </summary>
  17. public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype)
  18. {
  19. var index = random.Next(prototype.Values.Count);
  20. return Loc.GetString(prototype.Values[index]);
  21. }
  22. public static string Pick(this IWeightedRandomPrototype prototype, System.Random random)
  23. {
  24. var picks = prototype.Weights;
  25. var sum = picks.Values.Sum();
  26. var accumulated = 0f;
  27. var rand = random.NextFloat() * sum;
  28. foreach (var (key, weight) in picks)
  29. {
  30. accumulated += weight;
  31. if (accumulated >= rand)
  32. {
  33. return key;
  34. }
  35. }
  36. // Shouldn't happen
  37. throw new InvalidOperationException($"Invalid weighted pick for {prototype.ID}!");
  38. }
  39. public static string Pick(this IWeightedRandomPrototype prototype, IRobustRandom? random = null)
  40. {
  41. IoCManager.Resolve(ref random);
  42. var picks = prototype.Weights;
  43. var sum = picks.Values.Sum();
  44. var accumulated = 0f;
  45. var rand = random.NextFloat() * sum;
  46. foreach (var (key, weight) in picks)
  47. {
  48. accumulated += weight;
  49. if (accumulated >= rand)
  50. {
  51. return key;
  52. }
  53. }
  54. // Shouldn't happen
  55. throw new InvalidOperationException($"Invalid weighted pick for {prototype.ID}!");
  56. }
  57. public static T Pick<T>(this IRobustRandom random, Dictionary<T, float> weights)
  58. where T: notnull
  59. {
  60. var sum = weights.Values.Sum();
  61. var accumulated = 0f;
  62. var rand = random.NextFloat() * sum;
  63. foreach (var (key, weight) in weights)
  64. {
  65. accumulated += weight;
  66. if (accumulated >= rand)
  67. {
  68. return key;
  69. }
  70. }
  71. throw new InvalidOperationException("Invalid weighted pick");
  72. }
  73. public static T PickAndTake<T>(this IRobustRandom random, Dictionary<T, float> weights)
  74. where T : notnull
  75. {
  76. var pick = Pick(random, weights);
  77. weights.Remove(pick);
  78. return pick;
  79. }
  80. public static bool TryPickAndTake<T>(this IRobustRandom random, Dictionary<T, float> weights, [NotNullWhen(true)] out T? pick)
  81. where T : notnull
  82. {
  83. if (weights.Count == 0)
  84. {
  85. pick = default;
  86. return false;
  87. }
  88. pick = PickAndTake(random, weights);
  89. return true;
  90. }
  91. public static T Pick<T>(Dictionary<T, float> weights, System.Random random)
  92. where T : notnull
  93. {
  94. var sum = weights.Values.Sum();
  95. var accumulated = 0f;
  96. var rand = random.NextFloat() * sum;
  97. foreach (var (key, weight) in weights)
  98. {
  99. accumulated += weight;
  100. if (accumulated >= rand)
  101. {
  102. return key;
  103. }
  104. }
  105. throw new InvalidOperationException("Invalid weighted pick");
  106. }
  107. public static (string reagent, FixedPoint2 quantity) Pick(this WeightedRandomFillSolutionPrototype prototype, IRobustRandom? random = null)
  108. {
  109. var randomFill = prototype.PickRandomFill(random);
  110. IoCManager.Resolve(ref random);
  111. var sum = randomFill.Reagents.Count;
  112. var accumulated = 0f;
  113. var rand = random.NextFloat() * sum;
  114. foreach (var reagent in randomFill.Reagents)
  115. {
  116. accumulated += 1f;
  117. if (accumulated >= rand)
  118. {
  119. return (reagent, randomFill.Quantity);
  120. }
  121. }
  122. // Shouldn't happen
  123. throw new InvalidOperationException($"Invalid weighted pick for {prototype.ID}!");
  124. }
  125. public static RandomFillSolution PickRandomFill(this WeightedRandomFillSolutionPrototype prototype, IRobustRandom? random = null)
  126. {
  127. IoCManager.Resolve(ref random);
  128. var fills = prototype.Fills;
  129. Dictionary<RandomFillSolution, float> picks = new();
  130. foreach (var fill in fills)
  131. {
  132. picks[fill] = fill.Weight;
  133. }
  134. var sum = picks.Values.Sum();
  135. var accumulated = 0f;
  136. var rand = random.NextFloat() * sum;
  137. foreach (var (randSolution, weight) in picks)
  138. {
  139. accumulated += weight;
  140. if (accumulated >= rand)
  141. {
  142. return randSolution;
  143. }
  144. }
  145. // Shouldn't happen
  146. throw new InvalidOperationException($"Invalid weighted pick for {prototype.ID}!");
  147. }
  148. }
  149. }