#include "AppHdr.h"
#include <stdint.h>
#include "random.h"
int random_range(int low, int high)
{
ASSERT(low <= high);
return (low + random2(high - low + 1));
}
int random_range(int low, int high, int nrolls)
{
ASSERT(nrolls > 0);
int sum = 0;
for (int i = 0; i < nrolls; ++i)
sum += random_range(low, high);
return (sum / nrolls);
}
// Chooses one of the numbers passed in at random. The list of numbers
// must be terminated with -1.
int random_choose(int first, ...)
{
va_list args;
va_start(args, first);
int chosen = first, count = 1, nargs = 100;
while (nargs-- > 0)
{
const int pick = va_arg(args, int);
if (pick == -1)
break;
if (one_chance_in(++count))
chosen = pick;
}
ASSERT(nargs > 0);
va_end(args);
return (chosen);
}
// Chooses one of the strings passed in at random. The list of strings
// must be terminated with NULL.
const char* random_choose_string(const char* first, ...)
{
va_list args;
va_start(args, first);
const char* chosen = first;
int count = 1, nargs = 100;
while (nargs-- > 0)
{
char* pick = va_arg(args, char*);
if (pick == NULL)
break;
if (one_chance_in(++count))
chosen = pick;
}
ASSERT(nargs > 0);
va_end(args);
return (chosen);
}
int random_choose_weighted(int weight, int first, ...)
{
va_list args;
va_start(args, first);
int chosen = first, cweight = weight, nargs = 100;
while (nargs-- > 0)
{
const int nweight = va_arg(args, int);
if (!nweight)
break;
const int choice = va_arg(args, int);
if (random2(cweight += nweight) < nweight)
chosen = choice;
}
ASSERT(nargs > 0);
va_end(args);
return (chosen);
}
#define UINT32_MAX ((uint32_t)(-1))
int random2(int max)
{
if (max <= 1)
return (0);
uint32_t partn = UINT32_MAX / max;
while (true)
{
uint32_t bits = random_int();
uint32_t val = bits / partn;
if (val < (uint32_t)max)
return ((int)val);
}
}
bool coinflip(void)
{
return (static_cast<bool>(random2(2)));
}
// Returns random2(x) if random_factor is true, otherwise the mean.
int maybe_random2(int x, bool random_factor)
{
if (random_factor)
return (random2(x));
else
return (x / 2);
}
int roll_dice(int num, int size)
{
int ret = 0;
int i;
// If num <= 0 or size <= 0, then we'll just return the default
// value of zero. This is good behaviour in that it will be
// appropriate for calculated values that might be passed in.
if (num > 0 && size > 0)
{
ret += num; // since random2() is zero based
for (i = 0; i < num; i++)
ret += random2(size);
}
return (ret);
}
int dice_def::roll() const
{
return roll_dice(this->num, this->size);
}
dice_def calc_dice(int num_dice, int max_damage)
{
dice_def ret(num_dice, 0);
if (num_dice <= 1)
{
ret.num = 1;
ret.size = max_damage;
}
else if (max_damage <= num_dice)
{
ret.num = max_damage;
ret.size = 1;
}
else
{
// Divide the damage among the dice, and add one
// occasionally to make up for the fractions. -- bwr
ret.size = max_damage / num_dice;
ret.size += x_chance_in_y(max_damage % num_dice, num_dice);
}
return (ret);
}
// Attempts to make missile weapons nicer to the player by reducing the
// extreme variance in damage done.
void scale_dice(dice_def &dice, int threshold)
{
while (dice.size > threshold)
{
dice.num *= 2;
// If it's an odd number, lose one; this is more than
// compensated by the increase in number of dice.
dice.size /= 2;
}
}
// Calculates num/den and randomly adds one based on the remainder.
int div_rand_round(int num, int den)
{
return (num / den + (random2(den) < num % den));
}
int bestroll(int max, int rolls)
{
int best = 0;
for (int i = 0; i < rolls; i++)
{
int curr = random2(max);
if (curr > best)
best = curr;
}
return (best);
}
// random2avg() returns same mean value as random2() but with a lower variance
// never use with rolls < 2 as that would be silly - use random2() instead {dlb}
int random2avg(int max, int rolls)
{
int sum = random2(max);
for (int i = 0; i < (rolls - 1); i++)
sum += random2(max + 1);
return (sum / rolls);
}
// originally designed to randomise evasion -
// values are slightly lowered near (max) and
// approach an upper limit somewhere near (limit/2)
int random2limit(int max, int limit)
{
int i;
int sum = 0;
if (max < 1)
return (0);
for (i = 0; i < max; i++)
if (random2(limit) >= i)
sum++;
return (sum);
}
// Generate samples from a binomial distribution with n_trials and trial_prob
// probability of success per trial. trial_prob is a integer less than 100
// representing the % chancee of success.
// This just evaluates all n trials, there is probably an efficient way of
// doing this but I'm not much of a statistician. -CAO
int binomial_generator(unsigned n_trials, unsigned trial_prob)
{
int count = 0;
for (unsigned i = 0; i < n_trials; ++i)
if (::x_chance_in_y(trial_prob, 100))
count++;
return count;
}
bool one_chance_in(int a_million)
{
return (random2(a_million) == 0);
}
bool x_chance_in_y(int x, int y)
{
if (x <= 0)
return (false);
if (x >= y)
return (true);
return (random2(y) < x);
}
int fuzz_value(int val, int lowfuzz, int highfuzz, int naverage)
{
const int lfuzz = lowfuzz * val / 100,
hfuzz = highfuzz * val / 100;
return val + random2avg(lfuzz + hfuzz + 1, naverage) - lfuzz;
}
// This is used when the front-end randomness is inconclusive. There are
// never more than two possibilities, which simplifies things.
bool defer_rand::x_chance_in_y_contd(int x, int y, int index)
{
if (x <= 0)
return (false);
if (x >= y)
return (true);
do
{
if (index == int(bits.size()))
bits.push_back(random_int());
uint64_t expn_rand_1 = uint64_t(bits[index++]) * y;
uint64_t expn_rand_2 = expn_rand_1 + y;
uint64_t expn_minimum_fail = uint64_t(x) << 32;
if (expn_minimum_fail <= expn_rand_1)
return (false);
if (expn_rand_2 <= expn_minimum_fail)
return (true);
// y = expn_rand_2 - expn_rand_1; no-op
x = expn_minimum_fail - expn_rand_1;
} while(1);
}
int defer_rand::random2(int maxp1)
{
if (maxp1 <= 1)
return (0);
if (bits.empty())
bits.push_back(random_int());
uint64_t expn_rand_1 = uint64_t(bits[0]) * maxp1;
uint64_t expn_rand_2 = expn_rand_1 + maxp1;
int val1 = int(expn_rand_1 >> 32);
int val2 = int(expn_rand_2 >> 32);
if (val2 == val1)
return (val1);
// val2 == val1 + 1
uint64_t expn_thresh = uint64_t(val2) << 32;
return x_chance_in_y_contd(int(expn_thresh - expn_rand_1),
maxp1, 1)
? val1 : val2;
}
defer_rand& defer_rand::operator[](int i)
{
return children[i];
}
int defer_rand::random_range(int low, int high)
{
ASSERT(low <= high);
return (low + random2(high - low + 1));
}
int defer_rand::random2avg(int max, int rolls)
{
int sum = (*this)[0].random2(max);
for (int i = 0; i < (rolls - 1); i++)
sum += (*this)[i+1].random2(max + 1);
return (sum / rolls);
}