81 lines
2.4 KiB
C#
81 lines
2.4 KiB
C#
namespace ALttPRandomizer.Model {
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
|
|
public interface IRandomizableWeights {
|
|
public bool IsEmpty { get; }
|
|
|
|
public object? Roll();
|
|
}
|
|
|
|
public class RandomizableWeights<T> : IRandomizableWeights where T : struct, Enum {
|
|
private static readonly Random random = new();
|
|
|
|
public ImmutableList<(T Item, int Weight)> Options { get; }
|
|
public int TotalWeights { get; }
|
|
|
|
public RandomizableWeights(IList<(T Item, int Weight)>? items = null) {
|
|
if (items == null) {
|
|
this.TotalWeights = 0;
|
|
this.Options = ImmutableList<(T, int)>.Empty;
|
|
return;
|
|
}
|
|
|
|
int totalWeight = 0;
|
|
var builder = new List<(T, int)>();
|
|
foreach (var pair in items) {
|
|
if (pair.Weight < 0) {
|
|
throw new ArgumentException("Weights must be non-negative", $"items[{pair.Item}]");
|
|
}
|
|
|
|
if (pair.Weight > 0) {
|
|
builder.Add(pair);
|
|
totalWeight += pair.Weight;
|
|
}
|
|
}
|
|
|
|
this.TotalWeights = totalWeight;
|
|
this.Options = builder.ToImmutableList();
|
|
}
|
|
|
|
public static RandomizableWeights<T> EmptyWeights => new();
|
|
|
|
public static RandomizableWeights<T> EqualAll {
|
|
get => RandomizableWeights<T>.FromDictionary(Enum.GetValues<T>().ToDictionary(v => v, v => 1));
|
|
}
|
|
|
|
public static RandomizableWeights<T> ConstantWeights(T value) {
|
|
return new(new List<(T, int)> { (value, 100) });
|
|
}
|
|
|
|
public static RandomizableWeights<T> FromDictionary(IDictionary<T, int> dict) {
|
|
return new(dict.Select(kvp => (kvp.Key, kvp.Value)).ToList());
|
|
}
|
|
|
|
public bool IsEmpty { get => this.Options.Count == 0; }
|
|
|
|
public T? Roll() {
|
|
if (this.TotalWeights <= 0) {
|
|
return default;
|
|
}
|
|
|
|
int value = random.Next(this.TotalWeights);
|
|
|
|
foreach (var (item, weight) in this.Options) {
|
|
value -= weight;
|
|
|
|
if (value < 0) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
// should never reach
|
|
return default;
|
|
}
|
|
|
|
object? IRandomizableWeights.Roll() => Roll();
|
|
}
|
|
}
|