1 #pragma once
2
3 #include <algorithm> // iter_swap
4 #include <iterator> // advance
5 #include <map>
6 #include <vector>
7
8 #include "debug.h"
9 #include "fixedvector.h"
10 #include "hash.h"
11 #include "rng-type.h"
12 #include "pcg.h"
13
14 class CrawlVector;
15
16 namespace rng
17 {
18 class generator
19 {
20 public:
21 generator(rng_type g);
22 generator(branch_type b);
23 ~generator();
24 private:
25 rng_type previous;
26 };
27
28 class subgenerator
29 {
30 public:
31 subgenerator();
32 subgenerator(uint64_t seed);
33 subgenerator(uint64_t seed, uint64_t sequence);
34 ~subgenerator();
35 private:
36 PcgRNG current;
37 PcgRNG *previous;
38 rng_type previous_main;
39 };
40
41 rng_type get_branch_generator(const branch_type b);
42 CrawlVector generators_to_vector();
43 void load_generators(const CrawlVector &v);
44 vector<uint64_t> get_states();
45 PcgRNG *get_generator(rng_type r);
46 PcgRNG ¤t_generator();
47
48 void seed();
49 void seed(uint64_t seed);
50 void seed(uint64_t[], int);
51 void reset();
52
53 uint32_t get_uint32(rng_type generator);
54 uint64_t get_uint64(rng_type generator);
55 uint32_t get_uint32();
56 uint64_t get_uint64();
57 uint32_t peek_uint32();
58 uint64_t peek_uint64();
59
60 class ASSERT_stable
61 {
62 public:
ASSERT_stable()63 ASSERT_stable() : initial_peek(peek_uint64()) { }
~ASSERT_stable()64 ~ASSERT_stable()
65 {
66 ASSERT(peek_uint64() == initial_peek);
67 }
68
69 private:
70 uint64_t initial_peek;
71 };
72 }
73
74 bool coinflip();
75 int div_rand_round(int num, int den);
76 int rand_round(double x);
77 int div_round_up(int num, int den);
78 bool one_chance_in(int a_million);
79 bool x_chance_in_y(int x, int y);
80 int random2(int max);
81 int maybe_random2(int x, bool random_factor);
82 int maybe_random_div(int nom, int denom, bool random_factor);
83 int maybe_roll_dice(int num, int size, bool random);
84 int random_range(int low, int high);
85 int random_range(int low, int high, int nrolls);
86 double random_real();
87
88 int random2avg(int max, int rolls);
89 int biased_random2(int max, int n);
90 int random2limit(int max, int limit);
91 int binomial(unsigned n_trials, unsigned trial_prob, unsigned scale = 100);
92 bool bernoulli(double n_trials, double trial_prob);
93 int fuzz_value(int val, int lowfuzz, int highfuzz, int naverage = 2);
94 int roll_dice(int num, int size);
95 bool decimal_chance(double percent);
96
97 int ui_random(int max);
98
99 /** Chooses one of the objects passed in at random (by value).
100 * @return One of the arguments.
101 *
102 * @note All the arguments must be convertible to the type of the first.
103 */
104 template <typename T, typename... Ts>
random_choose(T first,Ts...rest)105 T random_choose(T first, Ts... rest)
106 {
107 const T elts[] = { first, rest... };
108 return elts[random2(1 + sizeof...(rest))];
109 }
110
111 /** Chooses one of the objects passed in at random (by reference).
112 *
113 * @return A reference to one of the arguments.
114 *
115 * @note All the arguments must be of a type compatible with the type of the
116 * first. Specifically, it must be possible to implicitly convert a
117 * pointer to each argument into the same type as a pointer to the first
118 * argument. So, for example, if the first argument is non-const, none
119 * of the subsequent subsequent arguments may be const.
120 */
121 template <typename T, typename... Ts>
random_choose_ref(T & first,Ts &...rest)122 T& random_choose_ref(T& first, Ts&... rest)
123 {
124 return *random_choose(&first, &rest...);
125 }
126
127 template <typename C>
128 auto random_iterator(C &container) -> decltype(container.begin())
129 {
130 int pos = random2(container.size());
131 auto it = container.begin();
132 advance(it, pos);
133 return it;
134 }
135
136 /**
137 * Get a random weighted choice.
138 *
139 * Weights are assumed to be non-negative, but are allowed to be zero.
140 * @tparam V A map, vector of pairs, etc., with the values of the
141 * map or the second elements of the pairs being integer
142 * weights.
143 *
144 * @param choices The collection of choice-weight pairs to choose from.
145 *
146 * @return A pointer to the item in the chosen pair, or nullptr if all
147 * weights are zero. The pointer is const only if necessary.
148 */
149 template <typename V>
150 auto random_choose_weighted(V &choices) -> decltype(&(begin(choices)->first))
151 {
152 int total = 0;
153 for (const auto &entry : choices)
154 total += entry.second;
155 int r = random2(total);
156 int sum = 0;
157 for (auto &entry : choices)
158 {
159 sum += entry.second;
160 if (sum > r)
161 return &entry.first;
162 }
163 return nullptr;
164 }
165
166 /**
167 * Get an index for a random weighted choice using a fixed vector of
168 * weights.
169 *
170 * Entries with a weight <= 0 are skipped.
171 * @param choices The fixed vector with weights for each item.
172 *
173 * @return A index corresponding to the selected item, or -1 if all
174 * weights were skipped.
175 */
176 template <typename T, int SIZE>
random_choose_weighted(const FixedVector<T,SIZE> & choices)177 int random_choose_weighted(const FixedVector<T, SIZE>& choices)
178 {
179 int total = 0;
180 for (auto weight : choices)
181 if (weight > 0)
182 total += weight;
183
184 int r = random2(total);
185 int sum = 0;
186 for (int i = 0; i < SIZE; ++i)
187 {
188 if (choices[i] <= 0)
189 continue;
190
191 sum += choices[i];
192 if (sum > r)
193 return i;
194 }
195 return -1;
196 }
197
198 template <typename T>
random_choose_weighted(int,T curr)199 T random_choose_weighted(int, T curr)
200 {
201 return curr;
202 }
203
204 template <typename T, typename... Args>
random_choose_weighted(int cweight,T curr,int nweight,T next,Args...args)205 T random_choose_weighted(int cweight, T curr, int nweight, T next, Args... args)
206 {
207 return random_choose_weighted<T>(cweight + nweight,
208 random2(cweight+nweight) < nweight ? next
209 : curr,
210 args...);
211 }
212
213 struct dice_def
214 {
215 int num;
216 int size;
217
dice_defdice_def218 constexpr dice_def() : num(0), size(0) {}
dice_defdice_def219 constexpr dice_def(int n, int s) : num(n), size(s) {}
220 int roll() const;
221 };
222
223 constexpr dice_def CONVENIENT_NONZERO_DAMAGE{42, 1};
224
225 dice_def calc_dice(int num_dice, int max_damage, bool random = true);
226
227 // I must be a random-access iterator.
228 template <typename I>
shuffle_array(I begin,I end)229 void shuffle_array(I begin, I end)
230 {
231 size_t n = end - begin;
232 while (n > 1)
233 {
234 const int i = random2(n);
235 n--;
236 iter_swap(begin + i, begin + n);
237 }
238 }
239
240 template <typename T>
shuffle_array(T & vec)241 void shuffle_array(T &vec)
242 {
243 shuffle_array(begin(vec), end(vec));
244 }
245
246 template <typename T>
shuffle_array(T * arr,int n)247 void shuffle_array(T *arr, int n)
248 {
249 shuffle_array(arr, arr + n);
250 }
251
252 /**
253 * A defer_rand object represents an infinite tree of random values, allowing
254 * for a much more functional approach to randomness. defer_rand values which
255 * have been used should not be copy-constructed. Querying the same path
256 * multiple times will always give the same result.
257 *
258 * An important property of defer_rand is that, except for rounding,
259 * `float(r.random2(X)) / X == float(r.random2(Y)) / Y` for all `X` and `Y`.
260 * In other words:
261 *
262 * - The parameter you use on any given call does not matter.
263 * - The object stores the fraction, not a specific integer.
264 * - random2() is monotonic in its argument.
265 *
266 * Rephrased: The first time any node in the tree has a method called on
267 * it, a random float between 0 and 1 (the fraction) is generated and stored,
268 * and this float is combined with the method's parameters to arrive at
269 * the result. Calling the same method on the same node with the same
270 * parameters will always give the same result, while different parameters
271 * or methods will give different results (though they'll all use the same
272 * float which was generated by the first method call). Each node in the
273 * tree has it's own float, so the same method+parameters on different
274 * nodes will get different results.
275 */
276 class defer_rand
277 {
278 vector<uint32_t> bits;
279 map<int, defer_rand> children;
280
281 bool x_chance_in_y_contd(int x, int y, int index);
282 public:
283 // TODO It would probably be a good idea to have some sort of random
284 // number generator API, and the ability to pass RNGs into any function
285 // that wants them.
x_chance_in_y(int x,int y)286 bool x_chance_in_y(int x, int y) { return x_chance_in_y_contd(x,y,0); }
one_chance_in(int a_million)287 bool one_chance_in(int a_million) { return x_chance_in_y(1,a_million); }
288 int random2(int maxp1);
289
290 int random_range(int low, int high);
291 int random2avg(int max, int rolls);
292
293 defer_rand& operator[] (int i);
294 };
295
296 template<typename Iterator>
choose_random_weighted(Iterator beg,const Iterator end)297 int choose_random_weighted(Iterator beg, const Iterator end)
298 {
299 int totalweight = 0;
300 int result = -1;
301 for (int count = 0; beg != end; ++count, ++beg)
302 {
303 totalweight += *beg;
304 if (random2(totalweight) < *beg)
305 result = count;
306 }
307 ASSERT(result >= 0);
308 return result;
309 }
310