OSDN Git Service

Merge pull request #1755 from Thujopsis/feature/add-monster-Rabbit-of-Caerbannog
[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     std::normal_distribution<> d(mean, stand);
87     auto result = std::round(d(w_ptr->rng));
88     return static_cast<int16_t>(result);
89 }
90
91 /*
92  * Generates damage for "2d6" style dice rolls
93  */
94 int16_t damroll(DICE_NUMBER num, DICE_SID sides)
95 {
96     int i, sum = 0;
97     for (i = 0; i < num; i++)
98         sum += randint1(sides);
99     return (int16_t)(sum);
100 }
101
102 /*
103  * Same as above, but always maximal
104  */
105 int16_t maxroll(DICE_NUMBER num, DICE_SID sides)
106 {
107     return (num * sides);
108 }
109
110 /*
111  * Given a numerator and a denominator, supply a properly rounded result,
112  * using the RNG to smooth out remainders.  -LM-
113  */
114 int32_t div_round(int32_t n, int32_t d)
115 {
116     int32_t tmp;
117
118     /* Refuse to divide by zero */
119     if (!d)
120         return (n);
121
122     /* Division */
123     tmp = n / d;
124
125     /* Rounding */
126     if ((std::abs(n) % std::abs(d)) > randint0(std::abs(d))) {
127         /* Increase the absolute value */
128         if (n * d > 0L)
129             tmp += 1L;
130         else
131             tmp -= 1L;
132     }
133
134     /* Return */
135     return (tmp);
136 }
137
138 /*
139  * Extract a "random" number from 0 to m-1, using the RNG.
140  *
141  * This function should be used when generating random numbers in
142  * "external" program parts like the main-*.c files.  It preserves
143  * the current RNG state to prevent influences on game-play.
144  *
145  * Could also use rand() from <stdlib.h> directly.
146  */
147 int32_t Rand_external(int32_t m)
148 {
149     if (m <= 0) {
150         return 0;
151     }
152
153     static std::optional<Xoshiro128StarStar> urbg_external;
154
155     if (!urbg_external.has_value()) {
156         /* Initialize with new seed */
157         auto seed = static_cast<uint32_t>(time(nullptr));
158         urbg_external = Xoshiro128StarStar(seed);
159     }
160
161     std::uniform_int_distribution<> d(0, m - 1);
162     return d(urbg_external.value());
163 }