Class ProbabilityList<T>
A generic, serializable container for weighted random selection with first-class support for probability influence, repeat prevention, pick history, and item depletion.
Inherited Members
Namespace: Scylla.Core.Util.Random
Assembly: ScyllaCore.dll
Syntax
[Serializable]
public class ProbabilityList<T>
Type Parameters
| Name | Description |
|---|---|
| T | The type of items stored in the list. |
Remarks
Selection strategies - Three algorithms are available via Strategy:
- AliasTable (default): O(n) build, O(1) sample - best for large lists with infrequent modification.
- BinarySearchCDF: O(n) build, O(log n) sample - suitable for medium-sized lists.
- LinearScan: O(1) build, O(n) sample - best for small lists (fewer than ~8 entries) or frequently modified lists.
The internal selection structure is rebuilt lazily the next time Pick(IRandomSource) or PickIndex(IRandomSource) is called after any state change.
Repeat prevention - The RepeatPrevention property selects one of the RepeatPreventionMode strategies. The Shuffle mode additionally requires no history tracking because it deals from a pre-shuffled deck.
Influence - Any entry can have an IProbabilityInfluence provider attached via SetInfluenceProvider(int, IProbabilityInfluence). When at least one influenced entry exists, the standard selection structures are bypassed in favour of a runtime linear scan over dynamically computed probabilities.
Depletion - When IsDepletable is true, individual
entries flagged with IsDepletable consume one
unit per pick and become unavailable at zero. Use RefillAll() or
RefillEntry(int) to restore them.
Unity cannot serialize open generic types directly. For Unity Inspector support,
create a concrete closed subclass, for example:
public class StringProbabilityList : ProbabilityList<string> { }
Properties
AllDepleted
Returns true when IsDepletable is true and every
enabled entry that has IsDepletable set has
zero remaining units. Non-depletable entries are not considered.
Returns false when the depletion system is disabled.
Declaration
public bool AllDepleted { get; }
Property Value
| Type | Description |
|---|---|
| bool |
Count
The total number of entries in this list, including disabled and depleted ones.
Declaration
public int Count { get; }
Property Value
| Type | Description |
|---|---|
| int |
HasInfluencedEntries
Returns true when at least one entry has an IProbabilityInfluence
provider registered via SetInfluenceProvider(int, IProbabilityInfluence). When true, selection
bypasses the precomputed alias table or CDF and uses a runtime linear scan over
dynamically computed effective probabilities instead.
Declaration
public bool HasInfluencedEntries { get; }
Property Value
| Type | Description |
|---|---|
| bool |
History
Provides read-only access to the pick history ring buffer. The history is created lazily on first access with a capacity of HistoryCapacity events. Use ClearHistory() to reset it without deallocating.
Declaration
public PickHistory History { get; }
Property Value
| Type | Description |
|---|---|
| PickHistory |
See Also
HistoryCapacity
The maximum number of pick events retained by the History ring buffer.
Defaults to 32. Values below 1 are clamped to 1.
When changed after the history has been created, existing records are transferred
into a new ring buffer of the requested size; older records beyond the new capacity
are discarded.
Declaration
public int HistoryCapacity { get; set; }
Property Value
| Type | Description |
|---|---|
| int |
IsDepletable
Master switch for the depletion system.
Declaration
public bool IsDepletable { get; set; }
Property Value
| Type | Description |
|---|---|
| bool |
Remarks
When false (the default), per-entry IsDepletable
flags and unit counts are completely ignored - all entries participate in selection
regardless of remaining units. Set to true to enable the depletion system
for entries that have been configured via SetDepletable(int, int).
Changing this property marks the list as dirty.
IsDirty
Returns true when the internal selection structure (alias table, CDF, or
shuffle deck) is stale and will be rebuilt before the next Pick(IRandomSource) call.
Becomes true whenever an entry is added, removed, enabled, disabled,
or has its probability changed.
Declaration
public bool IsDirty { get; }
Property Value
| Type | Description |
|---|---|
| bool |
this[int]
Returns the ProbabilityEntry<T> at the specified zero-based index, providing direct access to the item, probability, flags, and unit count.
Declaration
public ProbabilityEntry<T> this[int index] { get; }
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | Zero-based index in the range |
Property Value
| Type | Description |
|---|---|
| ProbabilityEntry<T> | The ProbabilityEntry<T> at the given position. |
RepeatPrevention
The strategy used to reduce or eliminate consecutive identical picks. Changing this value resets all shuffle state.
Declaration
public RepeatPreventionMode RepeatPrevention { get; set; }
Property Value
| Type | Description |
|---|---|
| RepeatPreventionMode |
See Also
ShuffleIterations
The number of complete passes through the shuffle deck that must be dealt before
the deck is reshuffled. Valid range is [1, 5]; values outside this range
are clamped. Higher values allow longer runs of the same ordering before variety
is reintroduced. Only used with Shuffle.
Declaration
public int ShuffleIterations { get; set; }
Property Value
| Type | Description |
|---|---|
| int |
Strategy
The algorithm used to sample entries during Pick(IRandomSource) and PickIndex(IRandomSource). Changing this property marks the list as dirty so the appropriate data structure is rebuilt on the next pick.
Declaration
public SelectionStrategy Strategy { get; set; }
Property Value
| Type | Description |
|---|---|
| SelectionStrategy |
See Also
TotalProbability
The sum of Probability for all currently enabled entries. Does not account for influence modifiers or depletion state.
Declaration
public float TotalProbability { get; }
Property Value
| Type | Description |
|---|---|
| float |
Methods
Add(T, float)
Adds a new entry with the given item and probability weight, then marks the list as dirty so the selection structure is rebuilt on the next pick.
Declaration
public void Add(T item, float probability)
Parameters
| Type | Name | Description |
|---|---|---|
| T | item | The item to add. |
| float | probability | The probability weight for this entry. Must be non-negative. The value is not automatically normalized against other entries. |
Remarks
The entry is created with Enabled set to
true and Locked set to false.
Call Normalize() afterwards if you want all probabilities to sum
to exactly 1.0.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
Clear()
Removes all entries from the list and resets all internal state, including influence providers, cached selection structures, shuffle state, and pick history.
Declaration
public void Clear()
Remarks
After this call Count is zero, IsDirty is true,
and History is cleared (but not deallocated). Configuration properties
such as Strategy, RepeatPrevention,
IsDepletable, and HistoryCapacity are not modified.
ClearHistory()
Clears all entries in the pick history ring buffer without deallocating it or changing the configured HistoryCapacity.
Declaration
public void ClearHistory()
Remarks
If the history has not been created yet (no picks have occurred), this method is a no-op.
FromProbabilities(T[], float[])
Creates a ProbabilityList<T> from a parallel array of raw float probabilities, normalizing them so that all enabled entries sum to 1.0.
Declaration
public static ProbabilityList<T> FromProbabilities(T[] items, float[] probabilities)
Parameters
| Type | Name | Description |
|---|---|---|
| T[] | items | The items to add to the list. Must not be |
| float[] | probabilities | Non-negative float probability weights for each item at the same index.
Must not be |
Returns
| Type | Description |
|---|---|
| ProbabilityList<T> | A new ProbabilityList<T> with normalized probabilities. |
Remarks
The provided probabilities do not need to sum to 1.0 beforehand; normalization is applied automatically via Normalize<T>(List<ProbabilityEntry<T>>). All entries are initially enabled and unlocked. Probabilities must be non-negative.
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | Thrown when |
| ArgumentException | Thrown when |
| ArgumentOutOfRangeException | Thrown when any element of |
FromWeights(T[], int[])
Creates a ProbabilityList<T> from a parallel array of integer weights, converting them to normalized float probabilities that sum to 1.0.
Declaration
public static ProbabilityList<T> FromWeights(T[] items, int[] weights)
Parameters
| Type | Name | Description |
|---|---|---|
| T[] | items | The items to add to the list. Must not be |
| int[] | weights | Non-negative integer weight for each item at the same index. The total weight must
be greater than zero. Must not be |
Returns
| Type | Description |
|---|---|
| ProbabilityList<T> | A new ProbabilityList<T> with normalized float probabilities. |
Remarks
Each item's probability is computed as weight[i] / totalWeight. The conversion
is performed once at construction; subsequent picks use the normalized float probabilities
directly. All entries are initially enabled and unlocked.
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | Thrown when |
| ArgumentException | Thrown when |
| ArgumentOutOfRangeException | Thrown when any element of |
GetEffectiveProbability(int)
Returns the effective selection probability for the entry at the specified index, taking into account the entry's enabled state, depletion, and any attached IProbabilityInfluence provider.
Declaration
public float GetEffectiveProbability(int index)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to query. |
Returns
| Type | Description |
|---|---|
| float | The effective probability for the entry, in the range |
Remarks
- Returns
0when the entry is disabled. - Returns
0when the entry is depletable, depletion is enabled, and Units is zero. - Returns the raw Probability when no influence provider is attached.
- Otherwise, computes the influenced probability using the provider's current Influence value and the entry's InfluenceMin / InfluenceMax bounds.
The result represents the un-normalized weight as it would be used in an on-demand linear scan; it is not normalized against other entries' effective probabilities.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
GetUnits(int)
Returns the number of remaining uses for the entry at the specified index.
Declaration
public int GetUnits(int index)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to query. |
Returns
| Type | Description |
|---|---|
| int | The current Units value if the entry is
depletable, or |
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
IsDepleted(int)
Returns true when the entry at the specified index is depletable and has
consumed all of its available units.
Declaration
public bool IsDepleted(int index)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to query. |
Returns
| Type | Description |
|---|---|
| bool |
|
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
Normalize()
Normalizes all enabled entry probabilities so they collectively sum to 1.0, respecting locked entries, then marks the list as dirty for rebuild.
Declaration
public void Normalize()
Remarks
Delegates to Normalize<T>(List<ProbabilityEntry<T>>). Locked entries keep their exact probability values; only unlocked entries are scaled to fill the remaining probability mass. Disabled entries are ignored entirely.
Pick(IRandomSource)
Picks and returns a single item according to the configured selection strategy, repeat prevention mode, and depletion rules.
Declaration
public T Pick(IRandomSource rng)
Parameters
| Type | Name | Description |
|---|---|---|
| IRandomSource | rng | The random source used for sampling. Must not be |
Returns
| Type | Description |
|---|---|
| T | The item held by the selected entry, or |
Remarks
This is a convenience wrapper around PickIndex(IRandomSource) that returns the item
rather than the index. The pick is recorded in the History ring buffer
and, when IsDepletable is true, decrements the unit count of
depletable entries.
PickIndex(IRandomSource)
Selects a single entry according to the configured selection strategy, repeat prevention mode, influence providers, and depletion state, then records the pick in the History ring buffer.
Declaration
public int PickIndex(IRandomSource rng)
Parameters
| Type | Name | Description |
|---|---|---|
| IRandomSource | rng | The random source used for sampling. Must not be |
Returns
| Type | Description |
|---|---|
| int | The zero-based index of the selected entry in the range |
Remarks
Selection flow:
- Rebuild internal structures if the list is dirty.
- Dispatch to the appropriate strategy: shuffle deck, alias table, CDF, or linear scan.
- Apply repeat prevention if configured (Spread or Repick).
- Decrement unit count for depletable entries when applicable.
- Record the pick in History.
ProbabilityEntry<T> MUST be a class (reference type) for the depletion decrement to take effect through the local entry variable.
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | Thrown when |
PickMultiple(IRandomSource, int, List<T>)
Picks multiple items and appends them to the provided list. Each pick is performed independently, respecting the configured repeat prevention mode, influence providers, and depletion state.
Declaration
public void PickMultiple(IRandomSource rng, int count, List<T> results)
Parameters
| Type | Name | Description |
|---|---|---|
| IRandomSource | rng | The random source used for each individual pick. Must not be |
| int | count | The maximum number of items to pick and append. |
| List<T> | results | The list to append selected items to. Must not be |
Remarks
The method stops early if PickIndex(IRandomSource) returns -1 (i.e., no
more eligible entries exist). The caller's list is never cleared before appending.
Exceptions
| Type | Condition |
|---|---|
| ArgumentNullException | Thrown when |
RefillAll()
Restores every depletable entry to its maximum unit count (MaxUnits) and marks the list as dirty.
Declaration
public void RefillAll()
Remarks
Non-depletable entries (where IsDepletable is
false) are not affected. Previously exhausted entries become selectable again
after the next rebuild triggered by the next pick.
RefillEntry(int)
Restores the depletable entry at the specified index to its maximum unit count (MaxUnits) and marks the list as dirty.
Declaration
public void RefillEntry(int index)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to refill. |
Remarks
If the entry is not depletable (IsDepletable is
false), this method is a no-op and the list is not marked dirty.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
RemoveAt(int)
Removes the entry at the specified index from the list, shifts all subsequent entries down by one, and invalidates the shuffle sequence and selection structure.
Declaration
public void RemoveAt(int index)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to remove. |
Remarks
Any influence provider previously registered for this index via SetInfluenceProvider(int, IProbabilityInfluence) is also removed. Influence providers for entries above the removed index are shifted down to preserve their association.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
ResetProbabilities()
Restores the Probability of every entry to the value captured in Scylla.Core.Util.Random.ProbabilityEntry<T>.OriginalProbability at construction time, then marks the list as dirty.
Declaration
public void ResetProbabilities()
Remarks
This method does not re-normalize probabilities; if the original values did not sum to 1.0, they will still not sum to 1.0 after the reset. Call Normalize() explicitly if normalization is required.
SetDepletable(int, int)
Marks the entry at the specified index as depletable with the given maximum unit count and immediately sets its current units to that maximum, as if fully refilled.
Declaration
public void SetDepletable(int index, int maxUnits)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to configure. |
| int | maxUnits | The maximum number of times this entry can be selected before it is depleted.
Must be non-negative. Pass |
Remarks
This method only takes effect when IsDepletable is also true
on the owning list. After calling this, the entry will be removed from selection
automatically once its unit count reaches zero.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetEnabled(int, bool)
Sets whether the entry at the specified index participates in selection. Disabled entries are excluded from the alias table, CDF, and shuffle deck.
Declaration
public void SetEnabled(int index, bool enabled)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to enable or disable. |
| bool | enabled |
|
Remarks
Changing the enabled state marks the list as dirty and invalidates the shuffle sequence, so the deck will be rebuilt from the new active set on the next pick.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetInfluenceInverted(int, bool)
Controls whether positive influence decreases the effective probability instead of increasing it for the entry at the specified index.
Declaration
public void SetInfluenceInverted(int index, bool inverted)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to configure. |
| bool | inverted |
|
Remarks
When true, the sign of the Influence
value is negated before it is applied, so a boost from an external system instead
suppresses this entry. This is useful for rare or penalising outcomes that should
become less likely when a positive buff is in effect.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetInfluenceProvider(int, IProbabilityInfluence)
Attaches or removes the IProbabilityInfluence provider for the entry at the specified index.
Declaration
public void SetInfluenceProvider(int index, IProbabilityInfluence provider)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to configure. |
| IProbabilityInfluence | provider | The IProbabilityInfluence provider to attach, or |
Remarks
When at least one provider is attached, HasInfluencedEntries becomes
true and the selection path switches to a runtime linear scan over dynamically
computed effective probabilities. The precomputed alias table and CDF are not used
in this mode because effective probabilities can change every frame.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetInfluenceSpread(int, float, float)
Sets the probability bounds used when an IProbabilityInfluence provider is active for the entry at the specified index.
Declaration
public void SetInfluenceSpread(int index, float min, float max)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to configure. |
| float | min | The probability floor applied at full negative influence ( |
| float | max | The probability ceiling applied at full positive influence ( |
Remarks
At influence -1.0 the effective probability approaches min;
at +1.0 it approaches max. Values are linearly interpolated
between the base probability and the respective bound.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetLocked(int, bool)
Sets whether the entry at the specified index is protected from probability normalization performed by Normalize() or Normalize<T>(List<ProbabilityEntry<T>>).
Declaration
public void SetLocked(int index, bool locked)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to lock or unlock. |
| bool | locked |
|
Remarks
Locked entries retain their exact Probability value during normalization. Their sum is subtracted from the available remainder (1.0) before unlocked entries are scaled proportionally. If locked entries collectively exceed 1.0, unlocked entries receive zero probability and a warning is logged.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |
SetProbability(int, float)
Updates the probability weight for the entry at the specified index and marks the list as dirty so the selection structure is rebuilt on the next pick.
Declaration
public void SetProbability(int index, float probability)
Parameters
| Type | Name | Description |
|---|---|---|
| int | index | The zero-based index of the entry to update. |
| float | probability | The new probability weight. Must be non-negative. |
Remarks
This method does not automatically renormalize other entries. Call Normalize() afterwards if you want all enabled probabilities to sum to 1.0.
Exceptions
| Type | Condition |
|---|---|
| ArgumentOutOfRangeException | Thrown when |