using System; using UnityEngine; using UnityEngine.TestTools; using Random = System.Random; using Vector2 = System.Numerics.Vector2; using Vector3 = System.Numerics.Vector3; namespace BracerLib.Utility { [ExcludeFromCoverage] public static class RandomUtility { public static readonly string CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; [ThreadStatic] private static Random unrepeatedRandomThreadInstance; private static Random unrepeatedRandom => unrepeatedRandomThreadInstance ??= new Random(DateTime.Now.Millisecond); private static int prevSeed = -1; public static float NextFloat(this Random prng) => (float)prng.NextDouble(); public static float Range(this Random prng, float minInclusive, float maxExclusive) => minInclusive + (maxExclusive - minInclusive) * prng.NextFloat(); public static void RandomizeUnityRandom() { var seed = (int)DateTime.Now.Ticks; if (seed != prevSeed) return; prevSeed = seed; UnityEngine.Random.InitState(seed); } /// Random between 0.0 inclusive and 1.0 exclusive. public static double NextDouble() => unrepeatedRandom.NextDouble(); /// Random between 0f inclusive and 1f exclusive. public static float NextFloat() => unrepeatedRandom.NextFloat(); /// /// Given a value, return a random number between the negative and positive of the input value. /// /// Non-negative value. public static float Range(float bookendRangeValue) => unrepeatedRandom.Range(-bookendRangeValue, bookendRangeValue); public static float Range(float minInclusive, float maxExclusive) => unrepeatedRandom.Range(minInclusive, maxExclusive); public static int Range(int minInclusive, int maxExclusive) => unrepeatedRandom.Next(minInclusive, maxExclusive); /// Random point uniformly distributed on the surface of a sphere of radius 1. public static Vector3 RandomOnUnitSphere() { var theta = MathUtility.TWO_PI * NextDouble(); var phi = Math.Acos(2.0 * NextDouble() - 1.0); var sinPhi = Math.Sin(phi); return new Vector3( (float)(sinPhi * Math.Cos(theta)), (float)(sinPhi * Math.Sin(theta)), (float)Math.Cos(phi) ); } /// Random point uniformly distributed inside a sphere of radius 1. public static Vector3 RandomInUnitSphere() => RandomOnUnitSphere() * (float)Math.Pow(NextDouble(), 1.0 / 3.0); /// Random point uniformly distributed on the perimeter of a circle of radius 1. public static Vector2 RandomOnUnitCircle() { var angle = MathUtility.TWO_PI * NextDouble(); return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); } /// Random point uniformly distributed in a circle of radius 1. public static Vector2 RandomInUnitCircle() => RandomOnUnitCircle() * (float)Math.Sqrt(NextDouble()); /// Random point uniformly distributed in a ring starting at radius1 and ending at radius2. public static Vector2 RandomInRing(float r1, float r2) { var r2sq = r2 * r2; return RandomOnUnitCircle() * (float)Math.Sqrt(NextDouble() * (r1 * r1 - r2sq) + r2sq); } public static Color RandomColorRGB() { return new Color( unrepeatedRandom.NextFloat(), unrepeatedRandom.NextFloat(), unrepeatedRandom.NextFloat(), 1f ); } public static Color RandomColorRGBA() { return new Color( unrepeatedRandom.NextFloat(), unrepeatedRandom.NextFloat(), unrepeatedRandom.NextFloat(), unrepeatedRandom.NextFloat() ); } public static string GetRandomString(int length) { var stringChars = new char[length]; var random = new Random(); for (var i = 0; i < stringChars.Length; i++) stringChars[i] = CHARS[random.Next(CHARS.Length)]; return new string(stringChars); } } }