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