SimulationCraft
SimulationCraft is a tool to explore combat mechanics in the popular MMO RPG World of Warcraft (tm).
rng.hpp
1 // ==========================================================================
2 // Dedmonwakeen's Raid DPS/TPS Simulator.
3 // Send questions to [email protected]
4 // ==========================================================================
5 
6 #pragma once
7 
11 #include "config.hpp"
12 
13 #include <array>
14 #include <cassert>
15 #include <cmath>
16 #include <cstdint>
17 
18 #include "util/timespan.hpp"
19 
23 namespace rng {
24 
30 template <typename Engine>
32 {
33 public:
34  explicit basic_rng_t() = default;
35 
36  basic_rng_t(const basic_rng_t&) = delete;
37  basic_rng_t& operator=(const basic_rng_t&) = delete;
38 
40  const char* name() const {
41  return engine.name();
42  }
43 
45  void seed( uint64_t s ) {
46  engine.seed( s );
47  }
48 
50  uint64_t reseed();
51 
53  void reset();
54 
56  double real();
57 
59  bool roll( double chance );
60 
62  double range( double min, double max );
63 
65  template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
66  T range( T min, T max ) {
67  return static_cast<T>(range(static_cast<double>(min), static_cast<double>(max)));
68  }
69 
71  template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
72  T range( T max ) {
73  return range<T>( T{}, max );
74  }
75 
77  double gauss( double mean, double stddev, bool truncate_low_end = false );
78 
80  double exponential( double nu );
81 
83  double exgauss( double gauss_mean, double gauss_stddev, double exp_nu );
84 
87 
89  timespan_t gauss( timespan_t mean, timespan_t stddev );
90 
93 
94 private:
95  Engine engine;
96  // Allow re-use of unused ( but necessary ) random number of a previous call to gauss()
97  double gauss_pair_value = 0.0;
98  bool gauss_pair_use = false;
99 };
100 
102 template <typename Engine>
104 {
105  const uint64_t s = engine.next();
106  seed( s );
107  reset();
108  return s;
109 }
110 
112 template <typename Engine>
114 {
115  gauss_pair_value = 0;
116  gauss_pair_use = false;
117 }
118 
119 // ==========================================================================
120 // Probability Distributions
121 // ==========================================================================
122 
124 template <typename Engine>
126 {
128  uint64_t ui64 = engine.next();
129  ui64 &= 0x000fffffffffffff;
130  ui64 |= 0x3ff0000000000000;
131  union { uint64_t ui64; double d; } u;
132  u.ui64 = ui64;
133  return u.d - 1.0;
134 }
135 
137 template <typename Engine>
138 bool basic_rng_t<Engine>::roll( double chance )
139 {
140  if ( chance <= 0 ) return false;
141  if ( chance >= 1 ) return true;
142  return real() < chance;
143 }
144 
146 template <typename Engine>
147 double basic_rng_t<Engine>::range( double min, double max )
148 {
149  assert( min <= max );
150  return min + real() * ( max - min );
151 }
152 
164 template <typename Engine>
165 double basic_rng_t<Engine>::gauss( double mean, double stddev, bool truncate_low_end )
166 {
167  assert(stddev >= 0 && "Calling gauss with negative stddev");
168 
169  double z;
170 
171  if ( stddev != 0 )
172  {
173  if ( gauss_pair_use )
174  {
175  z = gauss_pair_value;
176  gauss_pair_use = false;
177  }
178  else
179  {
180  double x1, x2, w, y1, y2;
181  do
182  {
183  x1 = 2.0 * real() - 1.0;
184  x2 = 2.0 * real() - 1.0;
185  w = x1 * x1 + x2 * x2;
186  }
187  while ( w >= 1.0 || w == 0.0 );
188 
189  w = std::sqrt( ( -2.0 * std::log( w ) ) / w );
190  y1 = x1 * w;
191  y2 = x2 * w;
192 
193  z = y1;
194  gauss_pair_value = y2;
195  gauss_pair_use = true;
196  }
197  }
198  else
199  z = 0.0;
200 
201  double result = mean + z * stddev;
202 
203  // True gaussian distribution can of course yield any number at some probability. So truncate on the low end.
204  if ( truncate_low_end && result < 0 )
205  result = 0;
206 
207  return result;
208 }
209 
211 template <typename Engine>
213 {
214  double x;
215  do { x = real(); } while ( x >= 1.0 ); // avoid ln(0)
216  return - std::log( 1 - x ) * nu;
217 }
218 
220 template <typename Engine>
221 double basic_rng_t<Engine>::exgauss( double gauss_mean, double gauss_stddev, double exp_nu )
222 {
223  double v = gauss( gauss_mean, gauss_stddev ) + exponential( exp_nu );
224  return v > 0.0 ? v : 0.0;
225 }
226 
228 template <typename Engine>
230 {
231  return timespan_t::from_native( range( timespan_t::to_native( min ), timespan_t::to_native( max ) ) );
232 }
233 
235 template <typename Engine>
237 {
238  return timespan_t::from_native( gauss( static_cast<double>( timespan_t::to_native( mean ) ),
239  static_cast<double>( timespan_t::to_native( stddev ) ) ) );
240 }
241 
243 template <typename Engine>
245 {
246  return timespan_t::from_native( exgauss( static_cast<double>( timespan_t::to_native( mean ) ),
247  static_cast<double>( timespan_t::to_native( stddev ) ),
248  static_cast<double>( timespan_t::to_native( nu ) ) ) );
249 }
250 
252 
260 {
261  uint64_t next() noexcept;
262  void seed( uint64_t start ) noexcept;
263  const char* name() const noexcept;
264 private:
265  std::array<uint64_t, 2> s;
266 };
267 
279 {
280  uint64_t next() noexcept;
281  void seed( uint64_t start ) noexcept;
282  const char* name() const noexcept;
283 private:
284  std::array<uint64_t, 4> s;
285 };
286 
294 {
295  uint64_t next() noexcept;
296  void seed( uint64_t start ) noexcept;
297  const char* name() const noexcept;
298 private:
299  std::array<uint64_t, 16> s;
300  int p;
301 };
302 
303 // "Default" rng
304 // Explicitly *NOT* a type alias to allow forward declaraions
305 struct rng_t : public basic_rng_t<xoshiro256plus_t> {};
306 
307 double stdnormal_cdf( double );
308 double stdnormal_inv( double );
309 
310 } // rng
double exponential(double nu)
Exponential Distribution.
Definition: rng.hpp:212
RNG Engines.
Definition: rng.hpp:259
void reset()
Reset any state.
Definition: rng.hpp:113
double gauss(double mean, double stddev, bool truncate_low_end=false)
Gaussian Distribution.
Definition: rng.hpp:165
xoshiro256+ Random Number Generator
Definition: rng.hpp:278
Definition: rng.hpp:305
const char * name() const
Return engine name.
Definition: rng.hpp:40
double range(double min, double max)
Uniform distribution in the range [min..max)
Definition: rng.hpp:147
Random number generator wrapper around an rng engine.
Definition: rng.hpp:31
void seed(uint64_t s)
Seed rng engine.
Definition: rng.hpp:45
Class for representing InGame time.
Definition: timespan.hpp:37
double exgauss(double gauss_mean, double gauss_stddev, double exp_nu)
Exponentially Modified Gaussian Distribution.
Definition: rng.hpp:221
bool roll(double chance)
Bernoulli Distribution.
Definition: rng.hpp:138
T range(T max)
Uniform distribution in the range [0..max)
Definition: rng.hpp:72
Random number generation.
Definition: action.hpp:37
uint64_t reseed()
Reseed using current state.
Definition: rng.hpp:103
XORSHIFT-1024 Random Number Generator.
Definition: rng.hpp:293
double stdnormal_cdf(double u)
The standard normal CDF, for one random variable.
Definition: rng.cpp:162
double real()
Uniform distribution in range [0..1)
Definition: rng.hpp:125
double stdnormal_inv(double p)
The inverse standard normal distribution.
Definition: rng.cpp:259
T range(T min, T max)
Uniform distribution in the range [min..max)
Definition: rng.hpp:66