1
0
Fork 0
mirror of https://codeberg.org/noisytoot/notnotdnethack.git synced 2024-11-21 16:55:06 +00:00
notnotdnethack/src/rnd.c
Ron Nazarov 1cba15796a Fix some undefined behaviour
-signed integer overflow in setrandom and check_reseed
-shift exponent 38 with a 32-bit type in makedefs (ranged_attk)

Also update makedefs ranged_attk to match the mondata version.  This
means that monsters with some ranged attacks now get correct
difficulty.  I didn't update the difficulty comments in monst.c.

# Conflicts:
#	src/hacklib.c
#	src/mondata.c
2024-05-15 12:52:14 -04:00

240 lines
4.9 KiB
C

/* SCCS Id: @(#)rnd.c 3.4 1996/02/07 */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
/* "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
static int reseed_period = 0;
static int reseed_count = 0;
void
check_reseed(void)
{
reseed_count++;
if (reseed_count > reseed_period) {
FILE *fptr = NULL;
int rndbuf[2];
fptr = fopen("/dev/urandom","r");
if (fptr) {
fread((void *)rndbuf, sizeof(int),2,fptr);
fclose(fptr);
srandom((unsigned int) (time((time_t *)0)) + rndbuf[0]);
reseed_period = (rndbuf[1] % 700) + 10;
reseed_count = 0;
} else {
reseed_count = 0;
impossible("check_reseed failed to retrieve data");
}
}
}
int
rn2( /* 0 <= rn2(x) < x */
register int x)
{
check_reseed();
if(x <= 0) x = 1; //I prefer this behavior -CM
#ifdef DEBUG
if (x <= 0) {
impossible("rn2(%d) attempted", x);
return(0);
}
x = RND(x);
return(x);
#else
return(RND(x));
#endif
}
int
rnl( /* 0 <= rnl(x) < x; sometimes subtracting Luck */
register int x /* good luck approaches 0, bad luck approaches (x-1) */)
{
register int i;
check_reseed();
if(x<=0) x=1; //fixes a crash from feeding rnd a negative number. I'd rather have this behavior.
#ifdef DEBUG
if (x <= 0) {
impossible("rnl(%d) attempted", x);
return(0);
}
#endif
i = RND(x);
if (Luck && rn2(50 - Luck)) {
i -= (x <= 15 && Luck >= -5 ? Luck/3 : Luck);
if (i < 0) i = 0;
else if (i >= x) i = x-1;
}
return i;
}
int
rnd( /* 1 <= rnd(x) <= x */
register int x)
{
check_reseed();
if(x<=0) x=1; //fixes a crash from feeding rnd a negative number. I'd rather have this behavior.
#ifdef DEBUG
if (x <= 0) {
impossible("rnd(%d) attempted", x);
return(1);
}
x = RND(x)+1;
return(x);
#else
return(RND(x)+1);
#endif
}
int
d( /* n <= d(n,x) <= (n*x) */
register int n,
register int x)
{
register int tmp = n;
check_reseed();
if (x < 0 || n < 0 || (x == 0 && n != 0)) {
impossible("d(%d,%d) attempted", n, x);
return(1);
}
while(n--) tmp += RND(x);
return(tmp); /* Alea iacta est. -- J.C. */
}
int
exploding_d(register int n, register int x, register int m)
{
register int tmp=0, cur;
check_reseed();
if(x < 2) return d(n,x) + m*n; //A die of size 1 or 0 would always explode.
#ifdef DEBUG
if (x < 0 || n < 0 || (x == 0 && n != 0)) {
impossible("exploding d(%d,%d) attempted", n, x);
return(1);
}
#endif
while(n--){
cur = RND(x) + 1;
while(cur == x){
tmp += cur+m;
cur = RND(x) + 1;
}
tmp += cur+m;
}
return(tmp); /* How do you get the average of an exploding die?
If you ask a friend who's good at math,
he'll tell you a long explanation about Sequences and Limits,
of which you'll forget half.
What you'll remember is the conclusion:
The average of an exploding Dn tends to the average of a
regular Dn (which is (n+1)/2) multiplied by n/(n-1)
-1d4chan wiki entry on exploding dice, retrieved 2/13/2012
.:, the average of this function tends to n*( ((x+1)/2 + m) * x/(x-1) )
The minimum value is n + n*m, and the maximum is unbounded
-CM */
}
int
lucky_exploding_d(register int n, register int x, register int m)
{
register int tmp=0, cur;
check_reseed();
if(x < 2) return d(n,x) + m*n; //A die of size 1 or 0 would always explode.
#ifdef DEBUG
if (x < 0 || n < 0 || (x == 0 && n != 0)) {
impossible("exploding d(%d,%d) attempted", n, x);
return(1);
}
#endif
while(n--){
cur = x-rnl(x);
// pline("damage %d",cur);
while(cur == x){
tmp += cur+m;
cur = x-rnl(x);
// pline("looping %d",cur);
}
tmp += cur+m;
}
// pline("damage total %d",tmp);
return(tmp);
}
int
unlucky_exploding_d(register int n, register int x, register int m)
{
register int tmp=0, cur;
if(x < 2) return d(n,x) + m*n; //A die of size 1 or 0 would always explode.
#ifdef DEBUG
if (x < 0 || n < 0 || (x == 0 && n != 0)) {
impossible("exploding d(%d,%d) attempted", n, x);
return(1);
}
#endif
while(n--){
cur = rnl(x);
// pline("damage %d",cur);
while(cur == x){
tmp += cur+m;
cur = rnl(x);
// pline("looping %d",cur);
}
tmp += cur+m;
}
// pline("damage total %d",tmp);
return(tmp);
}
int
rne(register int x)
{
register 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
*/
}
int
rnz(int i)
{
register long x = i;
register long tmp = 1000;
tmp += rn2(1000);
tmp *= rne(4);
if (rn2(2)) { x *= tmp; x /= 1000; }
else { x *= 1000; x /= tmp; }
return((int)x);
}
/*rnd.c*/