using System; namespace MLEM.Misc { /// /// A seed source contains an value which can be used as a seed for a or . Seed sources feature a convenient way to add multiple seeds using , which will be sufficiently scrambled to be deterministically pseudorandom and combined into a single . /// This struct behaves similarly to System.HashCode in many ways, with an important distinction being that 's scrambling procedure is not considered an implementation detail, and will stay consistent between process executions. /// /// /// For example, a seed source can be used to create a new based on an object's x and y coordinates by combining them into a using . The values generated by the created using will then be determined by the specific pair of x and y values used. /// public readonly struct SeedSource { private readonly int? value; private SeedSource(int? value) { this.value = value; } /// /// Adds the given seed to this seed source's value and returns the result as a new seed source. /// The algorithm used for adding involves various scrambling operations that sufficiently pseudo-randomize the seed and final value. /// /// The seed to add. /// A new seed source with the seed added. public SeedSource Add(int seed) { return new SeedSource(SeedSource.Scramble(this.Get()) + SeedSource.Scramble(seed)); } /// /// Adds the given seed to this seed source's value and returns the result as a new seed source. /// Floating point values are scrambled by invoking using a typecast version, followed by invoking using the decimal value multiplied by . /// /// The seed to add. /// A new seed source with the seed added. public SeedSource Add(float seed) { return this.Add((int) seed).Add((seed - (int) seed) * int.MaxValue); } /// /// Adds the given seed to this seed source's value and returns the result as a new seed source. /// Strings are scrambled by invoking using every character contained in the string, in order. /// /// The seed to add. /// A new seed source with the seed added. public SeedSource Add(string seed) { var ret = this; foreach (var c in seed) ret = ret.Add(c); return ret; } /// /// Adds the given seed to this seed source's value and returns the result as a new seed source. /// Guids are scrambled by invoking using every byte in the 's byte array. /// /// The seed to add. /// A new seed source with the seed added. public SeedSource Add(Guid seed) { var ret = this; foreach (var b in seed.ToByteArray()) ret = ret.Add(b); return ret; } /// /// Adds the given seed to this seed source's value and returns the result as a new seed source. /// Any objects that don't have a specially defined overload get scrambled using . /// /// The seed to add. /// A new seed source with the seed added. public SeedSource Add(object seed) { return this.Add(seed?.GetHashCode() ?? 0); } /// /// Returns a new seed source whose value is this seed source's value, but scrambled further. /// In essence, this creates a new seed source whose value is determined by the current seed source. /// /// A new seed source with a rotated value. public SeedSource Rotate() { return new SeedSource(SeedSource.Scramble(this.Get())); } /// /// Returns this seed source's seed value, which can then be used in or elsewhere. /// /// This seed source's value. public int Get() { return this.value ?? 1623487; } /// /// Returns a new instance using this source seed's value, retrieved using . /// /// A new using this seed source's value. public Random Random() { return new Random(this.Get()); } private static int Scramble(int x) { x += 84317; x ^= x << 7; x *= 207398809; x ^= x << 17; x *= 928511849; return x; } } }