4 * Copyright (c) 1997 Ben Harrison, and others
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies. Other copyrights may also apply.
11 /* Purpose: a simple random number generator -BEN- */
17 #include "term/z-rand.h"
18 #include "util/rng-xoshiro.h"
19 #include "world/world.h"
26 * Angband 2.7.9 introduced a new (optimized) random number generator,
27 * based loosely on the old "random.c" from Berkeley but with some major
28 * optimizations and algorithm changes. See below for more details.
30 * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
32 * This code provides (1) a "decent" RNG, based on the "BSD-degree-63-RNG"
33 * used in Angband 2.7.8, but rather optimized, and (2) a "simple" RNG,
34 * based on the simple "LCRNG" currently used in Angband, but "corrected"
35 * to give slightly better values. Both of these are available in two
36 * flavors, first, the simple "mod" flavor, which is fast, but slightly
37 * biased at high values, and second, the simple "div" flavor, which is
38 * less fast (and potentially non-terminating) but which is not biased
39 * and is much less subject to low-bit-non-randomness problems.
41 * You can select your favorite flavor by proper definition of the
42 * "randint0()" macro in the "defines.h" file.
44 * Note that, in Angband 2.8.0, the "state" table will be saved in the
45 * savefile, so a special "initialization" phase will be necessary.
47 * Note the use of the "simple" RNG, first you activate it via
48 * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used
49 * automatically used instead of the "complex" RNG, and when you are
50 * done, you de-activate it via "Rand_quick = FALSE" or choose a new
51 * seed via "Rand_value = seed".
54 * RNG algorithm was fully rewritten. Upper comment is OLD.
57 void Rand_state_init(void)
61 Xoshiro128StarStar::state_type Rand_state{};
63 FILE *fp = fopen(RNG_DEVICE, "r");
67 n = fread(Rand_state.data(), sizeof(Rand_state[0]), 4, fp);
68 } while (n != 4 || (Rand_state[0] | Rand_state[1] | Rand_state[2] | Rand_state[3]) == 0);
72 w_ptr->rng.set_state(Rand_state);
74 #elif defined(WINDOWS)
76 Xoshiro128StarStar::state_type Rand_state{};
80 CryptAcquireContext(&hProvider, nullptr, nullptr, PROV_RSA_FULL, 0);
83 CryptGenRandom(hProvider, sizeof(Rand_state[0]) * 4, (BYTE *)Rand_state.data());
84 } while ((Rand_state[0] | Rand_state[1] | Rand_state[2] | Rand_state[3]) == 0);
86 CryptReleaseContext(hProvider, 0);
88 w_ptr->rng.set_state(Rand_state);
93 uint32_t seed = (time(nullptr));
95 /* Mutate the seed on Unix machines */
96 seed = ((seed >> 3) * (getpid() << 1));
99 w_ptr->rng.set_state(seed);
104 int rand_range(int a, int b)
109 std::uniform_int_distribution<> d(a, b);
110 return d(w_ptr->rng);
114 * Generate a random integer number of NORMAL distribution
116 int16_t randnor(int mean, int stand)
118 std::normal_distribution<> d(mean, stand);
119 auto result = std::round(d(w_ptr->rng));
120 return static_cast<int16_t>(result);
124 * Generates damage for "2d6" style dice rolls
126 int16_t damroll(DICE_NUMBER num, DICE_SID sides)
129 for (i = 0; i < num; i++)
130 sum += randint1(sides);
131 return (int16_t)(sum);
135 * Same as above, but always maximal
137 int16_t maxroll(DICE_NUMBER num, DICE_SID sides)
139 return (num * sides);
143 * Given a numerator and a denominator, supply a properly rounded result,
144 * using the RNG to smooth out remainders. -LM-
146 int32_t div_round(int32_t n, int32_t d)
150 /* Refuse to divide by zero */
158 if ((std::abs(n) % std::abs(d)) > randint0(std::abs(d))) {
159 /* Increase the absolute value */
171 * Extract a "random" number from 0 to m-1, using the RNG.
173 * This function should be used when generating random numbers in
174 * "external" program parts like the main-*.c files. It preserves
175 * the current RNG state to prevent influences on game-play.
177 * Could also use rand() from <stdlib.h> directly.
179 int32_t Rand_external(int32_t m)
185 static std::optional<Xoshiro128StarStar> urbg_external;
187 if (!urbg_external.has_value()) {
188 /* Initialize with new seed */
189 auto seed = static_cast<uint32_t>(time(nullptr));
190 urbg_external = Xoshiro128StarStar(seed);
193 std::uniform_int_distribution<> d(0, m - 1);
194 return d(urbg_external.value());