using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Security.Cryptography; namespace DominionBase.Utilities { public static class Shuffler { private static DominionRandom provider; /// /// Performs an in-place shuffle of the list provided using strong crypto to (virtually) guarantee a randomized shuffling /// /// Type of object to be shuffled (unimportant for this method) /// List of items to be shuffled public static void Shuffle(this IList list) { Contract.Requires(list != null, "list cannot be null"); if (provider == null) provider = new DominionRandom(); var n = list.Count; while (n > 1) { n--; var k = provider.Next(n + 1); var value = list[k]; list[k] = list[n]; list[n] = value; } } /// /// Chooses a random item from the list provided, using strong crypto to (virtually) guarantee a natural distribution /// /// Type of object to be chosen at random /// List of items to choose from /// An item from the list, chosen at random public static T Choose(this IList list) { Contract.Requires(list != null, "list cannot be null"); if (provider == null) provider = new DominionRandom(); return list[provider.Next(list.Count)]; } } public class DominionRandom { private const int BufferSize = 1024; // must be a multiple of 4 private readonly byte[] RandomBuffer; private int BufferOffset; private readonly RNGCryptoServiceProvider rng; public DominionRandom() { RandomBuffer = new byte[BufferSize]; rng = new RNGCryptoServiceProvider(); BufferOffset = RandomBuffer.Length; } private void FillBuffer() { rng.GetBytes(RandomBuffer); BufferOffset = 0; } public int Next() { if (BufferOffset >= RandomBuffer.Length) { FillBuffer(); } var val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff; BufferOffset += sizeof(int); return val; } public int Next(int maxValue) { return Next() % maxValue; } public int Next(int minValue, int maxValue) { Contract.Requires(maxValue >= minValue, "maxValue >= minValue"); var range = maxValue - minValue; return minValue + Next(range); } public double NextDouble() { var val = Next(); return (double)val / int.MaxValue; } public void GetBytes(byte[] buff) { rng.GetBytes(buff); } } public static class Gaussian { private static bool _HaveNextNextGaussian; private static double _NextNextGaussian; private static Random _Random; private static readonly object _RNGLock = new object(); /// /// Returns a random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1 /// /// A random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1 public static double NextGaussian() { return NextGaussian(null); } /// /// Returns a random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1 /// /// The Random object to use instead of the internal one (e.g. if you need to control the seed) /// A random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1 public static double NextGaussian(Random rng) { if (_HaveNextNextGaussian) { _HaveNextNextGaussian = false; return _NextNextGaussian; } if (_Random == null && rng == null) _Random = new Random(); double v1, v2, s; do { if (rng == null) { v1 = 2.0 * _Random.NextDouble() - 1; // between -1.0 and 1.0 v2 = 2.0 * _Random.NextDouble() - 1; // between -1.0 and 1.0 } else { lock (_RNGLock) { v1 = 2.0 * rng.NextDouble() - 1; // between -1.0 and 1.0 v2 = 2.0 * rng.NextDouble() - 1; // between -1.0 and 1.0 } } s = v1 * v1 + v2 * v2; } while (s >= 1 || Math.Abs(s) < 0.001); var multiplier = Math.Sqrt(-2 * Math.Log(s) / s); _NextNextGaussian = v2 * multiplier; _HaveNextNextGaussian = true; return v1 * multiplier; } } }