OSDN Git Service

[Refactor] #1434 ABS() のマクロ関数をstd::abs() に差し替えた / Replaced the macro function ABS...
[hengbandforosx/hengbandosx.git] / src / term / z-rand.cpp
1 /* File: z-rand.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
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.
9  */
10
11 /* Purpose: a simple random number generator -BEN- */
12
13 #if defined(WINDOWS)
14 #include <Windows.h>
15 #endif
16
17 #include "term/z-rand.h"
18 #include "util/rng-xoshiro.h"
19 #include "world/world.h"
20
21 #include <cmath>
22 #include <optional>
23 #include <random>
24
25 /*
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.
29  *
30  * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
31  *
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.
40  *
41  * You can select your favorite flavor by proper definition of the
42  * "randint0()" macro in the "defines.h" file.
43  *
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.
46  *
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".
52  *
53  *
54  * RNG algorithm was fully rewritten. Upper comment is OLD.
55  */
56
57 void Rand_state_init(void)
58 {
59 #ifdef RNG_DEVICE
60
61     Xoshiro128StarStar::state_type Rand_state{};
62
63     FILE *fp = fopen(RNG_DEVICE, "r");
64     int n;
65
66     do {
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);
69
70     fclose(fp);
71
72     w_ptr->rng.set_state(Rand_state);
73
74 #elif defined(WINDOWS)
75
76     Xoshiro128StarStar::state_type Rand_state{};
77
78     HCRYPTPROV hProvider;
79
80     CryptAcquireContext(&hProvider, nullptr, nullptr, PROV_RSA_FULL, 0);
81
82     do {
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);
85
86     CryptReleaseContext(hProvider, 0);
87
88     w_ptr->rng.set_state(Rand_state);
89
90 #else
91
92     /* Basic seed */
93     uint32_t seed = (time(nullptr));
94 #ifdef SET_UID
95     /* Mutate the seed on Unix machines */
96     seed = ((seed >> 3) * (getpid() << 1));
97 #endif
98     /* Seed the RNG */
99     w_ptr->rng.set_state(seed);
100
101 #endif
102 }
103
104 int rand_range(int a, int b)
105 {
106     if (a > b) {
107         return a;
108     }
109     std::uniform_int_distribution<> d(a, b);
110     return d(w_ptr->rng);
111 }
112
113 /*
114  * Generate a random integer number of NORMAL distribution
115  */
116 int16_t randnor(int mean, int stand)
117 {
118     std::normal_distribution<> d(mean, stand);
119     auto result = std::round(d(w_ptr->rng));
120     return static_cast<int16_t>(result);
121 }
122
123 /*
124  * Generates damage for "2d6" style dice rolls
125  */
126 int16_t damroll(DICE_NUMBER num, DICE_SID sides)
127 {
128     int i, sum = 0;
129     for (i = 0; i < num; i++)
130         sum += randint1(sides);
131     return (int16_t)(sum);
132 }
133
134 /*
135  * Same as above, but always maximal
136  */
137 int16_t maxroll(DICE_NUMBER num, DICE_SID sides)
138 {
139     return (num * sides);
140 }
141
142 /*
143  * Given a numerator and a denominator, supply a properly rounded result,
144  * using the RNG to smooth out remainders.  -LM-
145  */
146 int32_t div_round(int32_t n, int32_t d)
147 {
148     int32_t tmp;
149
150     /* Refuse to divide by zero */
151     if (!d)
152         return (n);
153
154     /* Division */
155     tmp = n / d;
156
157     /* Rounding */
158     if ((std::abs(n) % std::abs(d)) > randint0(std::abs(d))) {
159         /* Increase the absolute value */
160         if (n * d > 0L)
161             tmp += 1L;
162         else
163             tmp -= 1L;
164     }
165
166     /* Return */
167     return (tmp);
168 }
169
170 /*
171  * Extract a "random" number from 0 to m-1, using the RNG.
172  *
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.
176  *
177  * Could also use rand() from <stdlib.h> directly.
178  */
179 int32_t Rand_external(int32_t m)
180 {
181     if (m <= 0) {
182         return 0;
183     }
184
185     static std::optional<Xoshiro128StarStar> urbg_external;
186
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);
191     }
192
193     std::uniform_int_distribution<> d(0, m - 1);
194     return d(urbg_external.value());
195 }