OSDN Git Service

[Chore] UTF-8エンコーディングのファイルからBOMを削除
[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 #include "term/z-rand.h"
14 #include "util/rng-xoshiro.h"
15 #include "world/world.h"
16
17 #include <algorithm>
18 #include <cmath>
19 #include <limits>
20 #include <optional>
21 #include <random>
22
23 /*
24  * Angband 2.7.9 introduced a new (optimized) random number generator,
25  * based loosely on the old "random.c" from Berkeley but with some major
26  * optimizations and algorithm changes.  See below for more details.
27  *
28  * Code by myself (benh@phial.com) and Randy (randy@stat.tamu.edu).
29  *
30  * This code provides (1) a "decent" RNG, based on the "BSD-degree-63-RNG"
31  * used in Angband 2.7.8, but rather optimized, and (2) a "simple" RNG,
32  * based on the simple "LCRNG" currently used in Angband, but "corrected"
33  * to give slightly better values.  Both of these are available in two
34  * flavors, first, the simple "mod" flavor, which is fast, but slightly
35  * biased at high values, and second, the simple "div" flavor, which is
36  * less fast (and potentially non-terminating) but which is not biased
37  * and is much less subject to low-bit-non-randomness problems.
38  *
39  * You can select your favorite flavor by proper definition of the
40  * "randint0()" macro in the "defines.h" file.
41  *
42  * Note that, in Angband 2.8.0, the "state" table will be saved in the
43  * savefile, so a special "initialization" phase will be necessary.
44  *
45  * Note the use of the "simple" RNG, first you activate it via
46  * "Rand_quick = TRUE" and "Rand_value = seed" and then it is used
47  * automatically used instead of the "complex" RNG, and when you are
48  * done, you de-activate it via "Rand_quick = FALSE" or choose a new
49  * seed via "Rand_value = seed".
50  *
51  *
52  * RNG algorithm was fully rewritten. Upper comment is OLD.
53  */
54
55 void Rand_state_init(void)
56 {
57     using element_type = Xoshiro128StarStar::state_type::value_type;
58     constexpr auto a = std::numeric_limits<element_type>::min();
59     constexpr auto b = std::numeric_limits<element_type>::max();
60
61     std::random_device rd;
62     std::uniform_int_distribution<element_type> dist(a, b);
63
64     Xoshiro128StarStar::state_type Rand_state{};
65     do {
66         std::generate(Rand_state.begin(), Rand_state.end(), [&dist, &rd] { return dist(rd); });
67     } while (std::all_of(Rand_state.begin(), Rand_state.end(), [](auto s) { return s == 0; }));
68
69     w_ptr->rng.set_state(Rand_state);
70 }
71
72 int rand_range(int a, int b)
73 {
74     if (a > b) {
75         return a;
76     }
77     std::uniform_int_distribution<> d(a, b);
78     return d(w_ptr->rng);
79 }
80
81 /*
82  * Generate a random integer number of NORMAL distribution
83  */
84 int16_t randnor(int mean, int stand)
85 {
86     if (stand <= 0) {
87         return static_cast<int16_t>(mean);
88     }
89     std::normal_distribution<> d(mean, stand);
90     auto result = std::round(d(w_ptr->rng));
91     return static_cast<int16_t>(result);
92 }
93
94 /*
95  * Generates damage for "2d6" style dice rolls
96  */
97 int16_t damroll(DICE_NUMBER num, DICE_SID sides)
98 {
99     int i, sum = 0;
100     for (i = 0; i < num; i++) {
101         sum += randint1(sides);
102     }
103     return (int16_t)(sum);
104 }
105
106 /*
107  * Same as above, but always maximal
108  */
109 int16_t maxroll(DICE_NUMBER num, DICE_SID sides)
110 {
111     return num * sides;
112 }
113
114 /*
115  * Given a numerator and a denominator, supply a properly rounded result,
116  * using the RNG to smooth out remainders.  -LM-
117  */
118 int32_t div_round(int32_t n, int32_t d)
119 {
120     int32_t tmp;
121
122     /* Refuse to divide by zero */
123     if (!d) {
124         return n;
125     }
126
127     /* Division */
128     tmp = n / d;
129
130     /* Rounding */
131     if ((std::abs(n) % std::abs(d)) > randint0(std::abs(d))) {
132         /* Increase the absolute value */
133         if (n * d > 0L) {
134             tmp += 1L;
135         } else {
136             tmp -= 1L;
137         }
138     }
139
140     /* Return */
141     return tmp;
142 }
143
144 /*
145  * Extract a "random" number from 0 to m-1, using the RNG.
146  *
147  * This function should be used when generating random numbers in
148  * "external" program parts like the main-*.c files.  It preserves
149  * the current RNG state to prevent influences on game-play.
150  *
151  * Could also use rand() from <stdlib.h> directly.
152  */
153 int32_t Rand_external(int32_t m)
154 {
155     if (m <= 0) {
156         return 0;
157     }
158
159     static std::optional<Xoshiro128StarStar> urbg_external;
160
161     if (!urbg_external.has_value()) {
162         /* Initialize with new seed */
163         auto seed = static_cast<uint32_t>(time(nullptr));
164         urbg_external = Xoshiro128StarStar(seed);
165     }
166
167     std::uniform_int_distribution<> d(0, m - 1);
168     return d(urbg_external.value());
169 }