/* 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" staticfn int whichrng(int (*fn)(int)); staticfn int RND(int); staticfn void set_random(unsigned long, int (*)(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 */ }; staticfn 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); } staticfn 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 staticfn void set_random(unsigned long seed, int (*fn)(int)) { init_isaac64(seed, fn); } #else /* USE_ISAAC64 */ /*ARGSUSED*/ staticfn 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*/