Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
random.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <concepts>
6#include <cstdint>
7#include <functional>
8#include <limits>
9#include <random>
10#include <type_traits>
11
12namespace helios::utils {
13
14/**
15 * @brief Concept for random number engines compatible with std distributions.
16 * @details Requires presence of `result_type`, `operator()`, and
17 * `min()`/`max()` members.
18 */
19template <typename T>
20concept RandomEngine = requires(T&& engine) {
21 typename std::remove_cvref_t<T>::result_type;
22 { engine() } -> std::convertible_to<typename T::result_type>;
23 {
24 std::remove_cvref_t<T>::min()
25 } -> std::convertible_to<typename std::remove_cvref_t<T>::result_type>;
26 {
27 std::remove_cvref_t<T>::max()
28 } -> std::convertible_to<typename std::remove_cvref_t<T>::result_type>;
29};
30
31/**
32 * @brief Concept for standard-like distributions.
33 * @details Requires presence of `result_type` and callable operator(engine).
34 */
35template <typename T, typename Engine>
36concept Distribution = requires(T&& dist, Engine engine) {
37 typename std::remove_cvref_t<T>::result_type;
38 {
39 dist(engine)
40 } -> std::convertible_to<typename std::remove_cvref_t<T>::result_type>;
41};
42
43/**
44 * @brief Default engine type used by random utilities.
45 * @details Uses a 64-bit Mersenne Twister for quality pseudorandom numbers.
46 */
47using DefaultRandomEngine = std::mt19937_64;
48
49/**
50 * @brief Fast but lower-quality engine type used by random utilities.
51 * @details Uses a 32-bit linear congruential engine suitable for
52 * non-cryptographic, performance-critical scenarios.
53 */
54using FastRandomEngine = std::minstd_rand;
55
56/**
57 * @brief Internal helper to obtain seed from `std::random_device`.
58 * @details This function is intentionally small and header-only to avoid static
59 * initialization of engines in user code.
60 * @return 64-bit seed value from `std::random_device`
61 */
62[[nodiscard]] inline uint64_t RandomDeviceSeed() {
63 std::random_device rd{};
64 using result_type = std::random_device::result_type;
65 constexpr auto result_bits = sizeof(result_type) * 8U;
66 constexpr auto target_bits = sizeof(uint64_t) * 8U;
67
68 if constexpr (result_bits >= target_bits) {
69 return static_cast<uint64_t>(rd());
70 } else {
71 uint64_t value = 0;
72 auto shift = 0U;
73 while (shift < target_bits) {
74 value |= static_cast<uint64_t>(rd()) << shift;
75 shift += result_bits;
76 }
77 return value;
78 }
79}
80
81/**
82 * @brief Creates a default-quality random engine seeded with
83 * `std::random_device`.
84 * @details Useful when the caller wants an engine instance but does not care
85 * a specific engine type beyond the default choice.
86 * @return `DefaultRandomEngine` instance seeded with `std::random_device`
87 */
91
92/**
93 * @brief Creates a fast linear random engine seeded with `std::random_device`.
94 * @details Intended for performance-critical code where statistical quality is
95 * less important. Not suitable for cryptographic purposes.
96 * @return `FastRandomEngine` instance seeded with `std::random_device`
97 */
98[[nodiscard]] inline FastRandomEngine MakeFastEngine() {
99 return FastRandomEngine{
100 static_cast<FastRandomEngine::result_type>(RandomDeviceSeed())};
101}
102
103/**
104 * @brief Thread-local default-quality engine.
105 * @details Uses Meyer's singleton pattern per thread to avoid global static
106 * initialization order issues while providing a convenient default.
107 * @return Reference to thread-local `DefaultRandomEngine` instance
108 */
109[[nodiscard]] inline DefaultRandomEngine& DefaultEngine() {
110 thread_local auto engine = MakeDefaultEngine();
111 return engine;
112}
113
114/**
115 * @brief Thread-local fast engine.
116 * @details Uses Meyer's singleton pattern per thread to avoid global static
117 * initialization order issues while providing a fast default.
118 * @return Reference to thread-local `FastRandomEngine` instance
119 */
120[[nodiscard]] inline FastRandomEngine& FastEngineInstance() {
121 thread_local auto engine = MakeFastEngine();
122 return engine;
123}
124
125/**
126 * @brief Random number utilities with a user-provided engine.
127 * @details This wrapper delegates all random generation to an underlying engine
128 * instance supplied by the user. It never owns the engine and does not perform
129 * any static initialization of engines itself.
130 * @tparam Engine RandomEngine type used for generation
131 */
132template <RandomEngine Engine>
134public:
135 /**
136 * @brief Constructs a `RandomGenerator` from an existing engine reference.
137 * @details The engine is not owned and must outlive this object.
138 * @param engine Reference to engine used for random generation
139 */
140 explicit RandomGenerator(Engine& engine) noexcept : engine_(engine) {}
141 RandomGenerator(const RandomGenerator&) noexcept = default;
142 RandomGenerator(RandomGenerator&&) noexcept = default;
143 ~RandomGenerator() noexcept = default;
144
145 RandomGenerator& operator=(const RandomGenerator&) noexcept = default;
146 RandomGenerator& operator=(RandomGenerator&&) noexcept = default;
147
148 /**
149 * @brief Generates a value using the provided distribution.
150 * @details This is a low-level interface that accepts an arbitrary
151 * distribution object. Intended for cases where caller needs full control
152 * over distribution parameters.
153 * @tparam Dist Distribution type compatible with `Engine`
154 * @param dist Distribution instance used for generation
155 * @return Generated random value of type `Dist::result_type`
156 */
157 template <typename Dist>
158 requires Distribution<Dist, Engine>
159 [[nodiscard]] auto Next(Dist& dist) noexcept(
160 std::is_nothrow_invocable_v<Dist, Engine&>) ->
161 typename Dist::result_type {
162 return dist(engine_.get());
163 }
164
165 /**
166 * @brief Generates a random arithmetic value using a reasonable default
167 * distribution.
168 * @details For integral types, uses `std::uniform_int_distribution` over the
169 * full representable range, except for `bool` which uses a uniform {false,
170 * true}. For floating point types, uses `std::uniform_real_distribution` in
171 * the [0, 1) range to avoid dependence on `std::numeric_limits<>::min()`.
172 * @tparam T Arithmetic type to generate
173 * @return Randomly generated value of type `T`
174 */
175 template <utils::ArithmeticTrait T>
176 [[nodiscard]] T Value();
177
178 /**
179 * @brief Generates a random arithmetic value within the specified range.
180 * @details For integral types, uses `std::uniform_int_distribution` with
181 * closed interval [min, max]. For floating point types, uses
182 * std::uniform_real_distribution with interval [min, max).
183 * @tparam T Arithmetic type
184 * @tparam U Arithmetic type
185 * @param min Lower bound of the range
186 * @param max Upper bound of the range
187 * @return Random value of `std::common_type_t<T, U>` within [min, max] or
188 * [min, max)
189 */
190 template <utils::ArithmeticTrait T, utils::ArithmeticTrait U>
191 [[nodiscard]] auto ValueFromRange(T min, U max) -> std::common_type_t<T, U>;
192
193 /**
194 * @brief Provides access to the underlying engine.
195 * @return Reference to the engine used by this generator
196 */
197 [[nodiscard]] Engine& EngineRef() const noexcept { return engine_.get(); }
198
199private:
200 std::reference_wrapper<Engine> engine_;
201};
202
203template <RandomEngine Engine>
204template <utils::ArithmeticTrait T>
206 Engine& engine = engine_.get();
207 if constexpr (std::integral<T>) {
208 if constexpr (std::same_as<T, bool>) {
209 std::uniform_int_distribution<int> dist(0, 1);
210 return dist(engine) == 1;
211 } else {
212 std::uniform_int_distribution<T> dist(std::numeric_limits<T>::min(),
213 std::numeric_limits<T>::max());
214 return dist(engine);
215 }
216 } else {
217 std::uniform_real_distribution<T> dist(static_cast<T>(0),
218 static_cast<T>(1));
219 return dist(engine);
220 }
221}
222
223template <RandomEngine Engine>
224template <utils::ArithmeticTrait T, utils::ArithmeticTrait U>
226 -> std::common_type_t<T, U> {
227 using Common = std::common_type_t<T, U>;
228 const auto cmin = static_cast<Common>(min);
229 const auto cmax = static_cast<Common>(max);
230
231 Engine& engine = engine_.get();
232 if constexpr (std::integral<Common>) {
233 using DistType =
234 std::conditional_t<std::signed_integral<Common>, int64_t, uint64_t>;
235 std::uniform_int_distribution<DistType> dist(static_cast<DistType>(cmin),
236 static_cast<DistType>(cmax));
237 return static_cast<Common>(dist(engine));
238 } else {
239 std::uniform_real_distribution<Common> dist(cmin, cmax);
240 return dist(engine);
241 }
242}
243
244/**
245 * @brief Convenience alias for a generator using the default-quality engine.
246 * @details Uses thread-local `DefaultEngine()` as the underlying engine.
247 */
249
250/**
251 * @brief Convenience alias for a generator using the fast engine.
252 * @details Uses thread-local `FastEngineInstance()` as the underlying engine.
253 */
255
256/**
257 * @brief Provides access to a thread-local default-quality random generator.
258 * @details Uses Meyer's singleton pattern per thread and avoids any static
259 * engine objects other than thread-local instances that are lazily initialized
260 * on first use.
261 * @return Reference to `DefaultRandomGenerator` bound to `DefaultEngine()`
262 */
263[[nodiscard]] inline DefaultRandomGenerator& RandomDefault() {
264 thread_local DefaultRandomGenerator generator{DefaultEngine()};
265 return generator;
266}
267
268/**
269 * @brief Provides access to a thread-local fast random generator.
270 * @details Uses Meyer's singleton pattern per thread and avoids any static
271 * engine objects other than thread-local instances that are lazily initialized
272 * on first use.
273 * @return Reference to `FastRandomGeneratorType` bound to
274 * `FastEngineInstance()`
275 */
276[[nodiscard]] inline FastRandomGeneratorType& RandomFast() {
277 thread_local FastRandomGeneratorType generator{FastEngineInstance()};
278 return generator;
279}
280
281/**
282 * @brief Convenience function to generate a default-distribution value using
283 * the default engine.
284 * @details Equivalent to `RandomDefault().Value<T>()`, but shorter to call.
285 * @tparam T Arithmetic type to generate
286 * @return Randomly generated value of type T
287 */
288template <utils::ArithmeticTrait T>
289[[nodiscard]] inline T RandomValue() {
290 return RandomDefault().Value<T>();
291}
292
293/**
294 * @brief Convenience function to generate a value in range using the default
295 * engine.
296 * @details Equivalent to `RandomDefault().ValueFromRange(min, max)`.
297 * @tparam T Arithmetic type
298 * @tparam U Arithmetic type
299 * @param min Lower bound of the range
300 * @param max Upper bound of the range
301 * @return Random value of `std::common_type_t<T, U>` in [min, max] or [min,
302 * max)
303 */
304template <utils::ArithmeticTrait T, utils::ArithmeticTrait U>
305[[nodiscard]] inline auto RandomValueFromRange(T min, U max)
306 -> std::common_type_t<T, U> {
307 return RandomDefault().ValueFromRange(min, max);
308}
309
310/**
311 * @brief Convenience function to generate a default-distribution value using
312 * the fast engine.
313 * @details Equivalent to `RandomFast().Value<T>()`, but shorter to call.
314 * @tparam T Arithmetic type to generate
315 * @return Randomly generated value of type `T` using the fast engine
316 */
317template <utils::ArithmeticTrait T>
318[[nodiscard]] inline T RandomFastValue() {
319 return RandomFast().Value<T>();
320}
321
322/**
323 * @brief Convenience function to generate a value in range using the fast
324 * engine.
325 * @details Equivalent to `RandomFast().ValueFromRange(min, max)`.
326 * @tparam T Arithmetic type
327 * @tparam U Arithmetic type
328 * @param min Lower bound of the range
329 * @param max Upper bound of the range
330 * @return Random value of `std::common_type_t<T, U>` in [min, max] or [min,
331 * max)
332 */
333template <utils::ArithmeticTrait T, utils::ArithmeticTrait U>
334[[nodiscard]] inline auto RandomFastValueFromRange(T min, U max)
335 -> std::common_type_t<T, U> {
336 return RandomFast().ValueFromRange(min, max);
337}
338
339} // namespace helios::utils
Random number utilities with a user-provided engine.
Definition random.hpp:133
auto ValueFromRange(T min, U max) -> std::common_type_t< T, U >
Generates a random arithmetic value within the specified range.
Definition random.hpp:225
RandomGenerator(const RandomGenerator &) noexcept=default
auto Next(Dist &dist) noexcept(std::is_nothrow_invocable_v< Dist, Engine & >) -> typename Dist::result_type
Definition random.hpp:159
Engine & EngineRef() const noexcept
Provides access to the underlying engine.
Definition random.hpp:197
RandomGenerator(RandomGenerator &&) noexcept=default
RandomGenerator(Engine &engine) noexcept
Constructs a RandomGenerator from an existing engine reference.
Definition random.hpp:140
T Value()
Generates a random arithmetic value using a reasonable default distribution.
Definition random.hpp:205
Concept for standard-like distributions.
Definition random.hpp:36
Concept for random number engines compatible with std distributions.
Definition random.hpp:20
auto RandomFastValueFromRange(T min, U max) -> std::common_type_t< T, U >
Convenience function to generate a value in range using the fast engine.
Definition random.hpp:334
RandomGenerator< DefaultRandomEngine > DefaultRandomGenerator
Convenience alias for a generator using the default-quality engine.
Definition random.hpp:248
auto RandomValueFromRange(T min, U max) -> std::common_type_t< T, U >
Convenience function to generate a value in range using the default engine.
Definition random.hpp:305
DefaultRandomGenerator & RandomDefault()
Provides access to a thread-local default-quality random generator.
Definition random.hpp:263
DefaultRandomEngine MakeDefaultEngine()
Creates a default-quality random engine seeded with std::random_device.
Definition random.hpp:88
FastRandomGeneratorType & RandomFast()
Provides access to a thread-local fast random generator.
Definition random.hpp:276
T RandomFastValue()
Convenience function to generate a default-distribution value using the fast engine.
Definition random.hpp:318
std::minstd_rand FastRandomEngine
Fast but lower-quality engine type used by random utilities.
Definition random.hpp:54
RandomGenerator< FastRandomEngine > FastRandomGeneratorType
Convenience alias for a generator using the fast engine.
Definition random.hpp:254
uint64_t RandomDeviceSeed()
Internal helper to obtain seed from std::random_device.
Definition random.hpp:62
FastRandomEngine & FastEngineInstance()
Thread-local fast engine.
Definition random.hpp:120
std::mt19937_64 DefaultRandomEngine
Default engine type used by random utilities.
Definition random.hpp:47
FastRandomEngine MakeFastEngine()
Creates a fast linear random engine seeded with std::random_device.
Definition random.hpp:98
DefaultRandomEngine & DefaultEngine()
Thread-local default-quality engine.
Definition random.hpp:109
T RandomValue()
Convenience function to generate a default-distribution value using the default engine.
Definition random.hpp:289
STL namespace.