| 1 | #pragma once |
| 2 | |
| 3 | #include "basis/seadRawPrint.h" |
| 4 | #include "basis/seadTypes.h" |
| 5 | #include "prim/seadBitUtil.h" |
| 6 | |
| 7 | namespace sead |
| 8 | { |
| 9 | /// A fast non-cryptographically secure pseudorandom number generator based on Xorshift128. |
| 10 | class Random |
| 11 | { |
| 12 | public: |
| 13 | Random() { init(); } |
| 14 | Random(u32 seed) { init(seed); } |
| 15 | /// @warning Parameters have to be chosen carefully to get a long period. Using this is not |
| 16 | /// recommended. |
| 17 | Random(u32 seed_x, u32 seed_y, u32 seed_z, u32 seed_w) { init(seed_x, seed_y, seed_z, seed_w); } |
| 18 | |
| 19 | /// Reset and seed the engine with the current system tick count. |
| 20 | void init(); |
| 21 | /// Reset and seed the engine with the specified value. |
| 22 | void init(u32 seed); |
| 23 | /// @warning Parameters have to be chosen carefully to get a long period. Using this is not |
| 24 | /// recommended. |
| 25 | void init(u32 seed_x, u32 seed_y, u32 seed_z, u32 seed_w); |
| 26 | |
| 27 | /// Generate a random u32. |
| 28 | u32 getU32(); |
| 29 | /// Generate a random u64. |
| 30 | u64 getU64(); |
| 31 | /// Generate a random u32 in [0 .. max). |
| 32 | u32 getU32(u32 max); |
| 33 | /// Generate a random s32 in [a .. b). |
| 34 | /// Note that this does not provide a uniform distribution. |
| 35 | s32 getS32Range(s32 a, s32 b); |
| 36 | /// Generate a random s64 in [a .. b). |
| 37 | /// Note that this does not provide a uniform distribution. |
| 38 | s64 getS64Range(s64 a, s64 b); |
| 39 | /// Generate a random f32 in [0, 1). |
| 40 | f32 getF32(); |
| 41 | /// Generate a random f32 in [a, b). |
| 42 | f32 getF32Range(f32 a, f32 b); |
| 43 | /// Generate a random f64 in [0, 1). |
| 44 | f64 getF64(); |
| 45 | /// Generate a random f64 in [a, b). |
| 46 | f64 getF64Range(f64 a, f64 b); |
| 47 | /// Generate a random boolean. |
| 48 | bool getBool(); |
| 49 | |
| 50 | void getContext(u32* x, u32* y, u32* z, u32* w) const; |
| 51 | |
| 52 | private: |
| 53 | u32 mX; |
| 54 | u32 mY; |
| 55 | u32 mZ; |
| 56 | u32 mW; |
| 57 | }; |
| 58 | |
| 59 | inline u32 Random::getU32(u32 max) |
| 60 | { |
| 61 | return getU32() * u64(max) >> 32u; |
| 62 | } |
| 63 | |
| 64 | inline s32 Random::getS32Range(s32 a, s32 b) |
| 65 | { |
| 66 | SEAD_ASSERT_MSG(a <= b, "b[%d] >= a[%d]" , a, b); |
| 67 | return getU32(max: b - a) + static_cast<u32>(a); |
| 68 | } |
| 69 | |
| 70 | // UNCHECKED |
| 71 | inline s64 Random::getS64Range(s64 a, s64 b) |
| 72 | { |
| 73 | SEAD_ASSERT_MSG(a <= b, "b[%ld] >= a[%ld]" , a, b); |
| 74 | return (getU32() * u64(b - a) >> 32u) + a; |
| 75 | } |
| 76 | |
| 77 | inline f32 Random::getF32() |
| 78 | { |
| 79 | return BitUtil::bitCast<f32>(value: (getU32() >> 9u) | 0x3F800000u) - 1.0f; |
| 80 | } |
| 81 | |
| 82 | inline f32 Random::getF32Range(f32 a, f32 b) |
| 83 | { |
| 84 | SEAD_ASSERT_MSG(a <= b, "b[%f] >= a[%f]" , a, b); |
| 85 | return a + (b - a) * getF32(); |
| 86 | } |
| 87 | |
| 88 | // UNCHECKED |
| 89 | inline f64 Random::getF64() |
| 90 | { |
| 91 | return BitUtil::bitCast<f64>(value: (getU64() >> 12u) | 0x3FF0'0000'0000'0000lu) - 1.0; |
| 92 | } |
| 93 | |
| 94 | // UNCHECKED |
| 95 | inline f64 Random::getF64Range(f64 a, f64 b) |
| 96 | { |
| 97 | SEAD_ASSERT_MSG(a <= b, "b[%f] >= a[%f]" , a, b); |
| 98 | return a + (b - a) * getF64(); |
| 99 | } |
| 100 | |
| 101 | // UNCHECKED |
| 102 | inline bool Random::getBool() |
| 103 | { |
| 104 | return getU32() & 0x80000000u; |
| 105 | } |
| 106 | |
| 107 | } // namespace sead |
| 108 | |