new .h files: hacklib.h selvar.h stairs.h
new .c files: calendar.c, getpos.c, report.c, selvar.c, stairs.c,
strutil.c, wizcmds.c
cleanup of hacklib.c and mdlib.c
hacklib contains functions that do not have to link with the core
relocate wiz commands from cmd.c to wizcmds.c
relocate CRASHREPORT stuff to report.c
relocate getpos stuff from do_name.c to getpos.c
remove temporary struct definition from extern.h
cross-compile PRE-section split into cross-pre1.370 and cross-pre2.370
Windows sys/windows/Makefile.nmake and sys/windows/Makefile.mingw32 and
visual studio project file updates
Unix sys/unix/Makefile.src, sys/unix/Makefile.utl
populate selvar.c and selvar.h
build on MS-DOS (not cross-compile) Makefile updates
for sys/msdos/Makefile.GCC (untested)
vms updates for above (untested)
312 lines
6.6 KiB
C
312 lines
6.6 KiB
C
/* NetHack 3.7 rnd.c $NHDT-Date: 1596498205 2020/08/03 23:43:25 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.30 $ */
|
|
/* Copyright (c) 2004 by Robert Patrick Rankin */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
#ifdef USE_ISAAC64
|
|
#include "isaac64.h"
|
|
|
|
static int whichrng(int (*fn)(int));
|
|
static int RND(int);
|
|
|
|
#if 0
|
|
static isaac64_ctx rng_state;
|
|
#endif
|
|
|
|
struct rnglist_t {
|
|
int (*fn)(int);
|
|
boolean init;
|
|
isaac64_ctx rng_state;
|
|
};
|
|
|
|
enum { CORE = 0, DISP = 1 };
|
|
|
|
static struct rnglist_t rnglist[] = {
|
|
{ rn2, FALSE, { 0 } }, /* CORE */
|
|
{ rn2_on_display_rng, FALSE, { 0 } }, /* DISP */
|
|
};
|
|
|
|
static int
|
|
whichrng(int (*fn)(int))
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SIZE(rnglist); ++i)
|
|
if (rnglist[i].fn == fn)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
init_isaac64(unsigned long seed, int (*fn)(int))
|
|
{
|
|
unsigned char new_rng_state[sizeof seed];
|
|
unsigned i;
|
|
int rngindx = whichrng(fn);
|
|
|
|
if (rngindx < 0)
|
|
panic("Bad rng function passed to init_isaac64().");
|
|
|
|
for (i = 0; i < sizeof seed; i++) {
|
|
new_rng_state[i] = (unsigned char) (seed & 0xFF);
|
|
seed >>= 8;
|
|
}
|
|
isaac64_init(&rnglist[rngindx].rng_state, new_rng_state,
|
|
(int) sizeof seed);
|
|
}
|
|
|
|
static int
|
|
RND(int x)
|
|
{
|
|
return (isaac64_next_uint64(&rnglist[CORE].rng_state) % x);
|
|
}
|
|
|
|
/* 0 <= rn2(x) < x, but on a different sequence from the "main" rn2;
|
|
used in cases where the answer doesn't affect gameplay and we don't
|
|
want to give users easy control over the main RNG sequence. */
|
|
int
|
|
rn2_on_display_rng(int x)
|
|
{
|
|
return (isaac64_next_uint64(&rnglist[DISP].rng_state) % x);
|
|
}
|
|
|
|
#else /* USE_ISAAC64 */
|
|
|
|
/* "Rand()"s definition is determined by [OS]conf.h */
|
|
#if defined(UNIX) || defined(RANDOM)
|
|
#define RND(x) ((int) (Rand() % (long) (x)))
|
|
#else
|
|
/* Good luck: the bottom order bits are cyclic. */
|
|
#define RND(x) ((int) ((Rand() >> 3) % (x)))
|
|
#endif
|
|
int
|
|
rn2_on_display_rng(int x)
|
|
{
|
|
static unsigned seed = 1;
|
|
seed *= 2739110765;
|
|
return (int) ((seed >> 16) % (unsigned) x);
|
|
}
|
|
#endif /* USE_ISAAC64 */
|
|
|
|
/* 0 <= rn2(x) < x */
|
|
int
|
|
rn2(int x)
|
|
{
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
if (x <= 0) {
|
|
impossible("rn2(%d) attempted", x);
|
|
return 0;
|
|
}
|
|
x = RND(x);
|
|
return x;
|
|
#else
|
|
return RND(x);
|
|
#endif
|
|
}
|
|
|
|
/* 0 <= rnl(x) < x; sometimes subtracting Luck;
|
|
good luck approaches 0, bad luck approaches (x-1) */
|
|
int
|
|
rnl(int x)
|
|
{
|
|
int i, adjustment;
|
|
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
if (x <= 0) {
|
|
impossible("rnl(%d) attempted", x);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
adjustment = Luck;
|
|
if (x <= 15) {
|
|
/* for small ranges, use Luck/3 (rounded away from 0);
|
|
also guard against architecture-specific differences
|
|
of integer division involving negative values */
|
|
adjustment = (abs(adjustment) + 1) / 3 * sgn(adjustment);
|
|
/*
|
|
* 11..13 -> 4
|
|
* 8..10 -> 3
|
|
* 5.. 7 -> 2
|
|
* 2.. 4 -> 1
|
|
* -1,0,1 -> 0 (no adjustment)
|
|
* -4..-2 -> -1
|
|
* -7..-5 -> -2
|
|
* -10..-8 -> -3
|
|
* -13..-11-> -4
|
|
*/
|
|
}
|
|
|
|
i = RND(x);
|
|
if (adjustment && rn2(37 + abs(adjustment))) {
|
|
i -= adjustment;
|
|
if (i < 0)
|
|
i = 0;
|
|
else if (i >= x)
|
|
i = x - 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/* 1 <= rnd(x) <= x */
|
|
int
|
|
rnd(int x)
|
|
{
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
if (x <= 0) {
|
|
impossible("rnd(%d) attempted", x);
|
|
return 1;
|
|
}
|
|
#endif
|
|
x = RND(x) + 1;
|
|
return x;
|
|
}
|
|
|
|
int
|
|
rnd_on_display_rng(int x)
|
|
{
|
|
return rn2_on_display_rng(x) + 1;
|
|
}
|
|
|
|
/* d(N,X) == NdX == dX+dX+...+dX N times; n <= d(n,x) <= (n*x) */
|
|
int
|
|
d(int n, int x)
|
|
{
|
|
int tmp = n;
|
|
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
if (x < 0 || n < 0 || (x == 0 && n != 0)) {
|
|
impossible("d(%d,%d) attempted", n, x);
|
|
return 1;
|
|
}
|
|
#endif
|
|
while (n--)
|
|
tmp += RND(x);
|
|
return tmp; /* Alea iacta est. -- J.C. */
|
|
}
|
|
|
|
/* 1 <= rne(x) <= max(u.ulevel/3,5) */
|
|
int
|
|
rne(int x)
|
|
{
|
|
int tmp, utmp;
|
|
|
|
utmp = (u.ulevel < 15) ? 5 : u.ulevel / 3;
|
|
tmp = 1;
|
|
while (tmp < utmp && !rn2(x))
|
|
tmp++;
|
|
return tmp;
|
|
|
|
/* was:
|
|
* tmp = 1;
|
|
* while (!rn2(x))
|
|
* tmp++;
|
|
* return min(tmp, (u.ulevel < 15) ? 5 : u.ulevel / 3);
|
|
* which is clearer but less efficient and stands a vanishingly
|
|
* small chance of overflowing tmp
|
|
*/
|
|
}
|
|
|
|
/* rnz: everyone's favorite! */
|
|
int
|
|
rnz(int i)
|
|
{
|
|
long x = (long) i;
|
|
long tmp = 1000L;
|
|
|
|
tmp += rn2(1000);
|
|
tmp *= rne(4);
|
|
if (rn2(2)) {
|
|
x *= tmp;
|
|
x /= 1000;
|
|
} else {
|
|
x *= 1000;
|
|
x /= tmp;
|
|
}
|
|
return (int) x;
|
|
}
|
|
|
|
/* Sets the seed for the random number generator */
|
|
#ifdef USE_ISAAC64
|
|
|
|
static void
|
|
set_random(unsigned long seed,
|
|
int (*fn)(int))
|
|
{
|
|
init_isaac64(seed, fn);
|
|
}
|
|
|
|
#else /* USE_ISAAC64 */
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
set_random(unsigned long seed,
|
|
int (*fn)(int) UNUSED)
|
|
{
|
|
/*
|
|
* The types are different enough here that sweeping the different
|
|
* routine names into one via #defines is even more confusing.
|
|
*/
|
|
# ifdef RANDOM /* srandom() from sys/share/random.c */
|
|
srandom((unsigned int) seed);
|
|
# else
|
|
# if defined(__APPLE__) || defined(BSD) || defined(LINUX) \
|
|
|| defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */
|
|
# if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4)
|
|
(void)
|
|
# endif
|
|
srandom((int) seed);
|
|
# else
|
|
# ifdef UNIX /* system srand48() */
|
|
srand48((long) seed);
|
|
# else /* poor quality system routine */
|
|
srand((int) seed);
|
|
# endif
|
|
# endif
|
|
# endif
|
|
}
|
|
#endif /* USE_ISAAC64 */
|
|
|
|
/* An appropriate version of this must always be provided in
|
|
port-specific code somewhere. It returns a number suitable
|
|
as seed for the random number generator */
|
|
extern unsigned long sys_random_seed(void);
|
|
|
|
/*
|
|
* Initializes the random number generator.
|
|
* Only call once.
|
|
*/
|
|
void
|
|
init_random(int (*fn)(int))
|
|
{
|
|
set_random(sys_random_seed(), fn);
|
|
}
|
|
|
|
/* Reshuffles the random number generator. */
|
|
void
|
|
reseed_random(int (*fn)(int))
|
|
{
|
|
/* only reseed if we are certain that the seed generation is unguessable
|
|
* by the players. */
|
|
if (has_strong_rngseed)
|
|
init_random(fn);
|
|
}
|
|
|
|
/* randomize the given list of numbers 0 <= i < count */
|
|
void
|
|
shuffle_int_array(int *indices, int count)
|
|
{
|
|
int i, iswap, temp;
|
|
|
|
for (i = count - 1; i > 0; i--) {
|
|
if ((iswap = rn2(i + 1)) == i)
|
|
continue;
|
|
temp = indices[i];
|
|
indices[i] = indices[iswap];
|
|
indices[iswap] = temp;
|
|
}
|
|
}
|
|
|
|
/*rnd.c*/
|