init
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
namespace GamblingCoin;
|
||||
|
||||
public class WeightedRandomExecutor<TEvent>
|
||||
{
|
||||
private class WeightedAction
|
||||
{
|
||||
public Action<TEvent> Action { get; }
|
||||
public double Weight { get; }
|
||||
|
||||
public WeightedAction(Action<TEvent> action, double weight)
|
||||
{
|
||||
if (weight <= 0)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(weight),
|
||||
"Weight must be positive."
|
||||
);
|
||||
|
||||
Action = action ?? throw new ArgumentNullException(nameof(action));
|
||||
Weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<WeightedAction> _actions = new();
|
||||
private readonly Random _random = new();
|
||||
private double _totalWeight;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a function to be potentially executed, along with its weight.
|
||||
/// </summary>
|
||||
/// <param name="action">The function to add. It must accept an argument of type TEvent.</param>
|
||||
/// <param name="weight">The positive weight for this function. Higher weights mean higher probability.</param>
|
||||
public WeightedRandomExecutor<TEvent> AddAction(Action<TEvent> action, double weight)
|
||||
{
|
||||
var weightedAction = new WeightedAction(action, weight);
|
||||
_actions.Add(weightedAction);
|
||||
_totalWeight += weight;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes one of the added functions randomly based on their weights.
|
||||
/// The chosen function will be called with the provided 'ev' argument.
|
||||
/// </summary>
|
||||
/// <param name="ev">The event argument to pass to the chosen function.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if no actions have been added.</exception>
|
||||
public void Execute(TEvent ev)
|
||||
{
|
||||
if (_actions.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"No actions have been added to execute."
|
||||
);
|
||||
}
|
||||
|
||||
if (_totalWeight <= 0) // Should not happen if AddAction validates weight > 0
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Total weight is zero or negative, cannot execute."
|
||||
);
|
||||
}
|
||||
|
||||
var randomNumber = _random.NextDouble() * _totalWeight;
|
||||
double cumulativeWeight = 0;
|
||||
|
||||
foreach (var weightedAction in _actions)
|
||||
{
|
||||
cumulativeWeight += weightedAction.Weight;
|
||||
if (!(randomNumber < cumulativeWeight)) continue;
|
||||
weightedAction.Action(ev);
|
||||
return; // Exit after executing one action
|
||||
}
|
||||
|
||||
// Fallback in case of floating point inaccuracies,
|
||||
// or if somehow randomNumber was exactly _totalWeight (NextDouble is < 1.0)
|
||||
// This should ideally pick the last item if all weights were summed up.
|
||||
if (_actions.Any())
|
||||
{
|
||||
_actions.Last().Action(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user