/* * rnd.c -- random code, including selections from a list * * (Need usual copyleft comments and CVS ID headers around here.) * Copyright (C) 1999 Peter Amstutz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of *the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #include #include #include #include #include #include #include #include "rnd.h" /* avoid types.h differences between Linux and Solaris */ #define uint unsigned int RANDOM_STATE gfx_random; /* one for gfxDrawSky() */ RANDOM_STATE main_random; /* one for everyone else */ /* * rndList -- return a random item from the list, but not one of the last N used */ void *rndList(register RNDLIST * listp) { register int i; register int pick; register uint num; register uint lim; assert(listp); num = listp->num; /* * Take care of edge cases. */ switch (num) { case 0: return (NULL); /* NULL for nothing */ case 1: return (listp->items[0]); /* one item gets it */ case 2: return (listp->items[ARND(2)]); /* 2: random pick */ } /* * Else, use MIN(num/2, RND_NUMLAST) last items indexes. */ lim = num >> 1; if(lim > RND_NUMLAST) { lim = RND_NUMLAST; } /* * Compare a random selection against lim previous picks. If it * duplicates a previous one, try again. */ do { /* random item */ pick = RND(num); for(i = lim; --i >= 0;) { /* not recently used */ if(listp->last[i] == pick) break; /* Try again. */ } } while(i >= 0); num = listp->lastidx; /* remember string */ listp->last[num] = pick; ++num; num = num & (RND_NUMLAST - 1); listp->lastidx = num; return (listp->items[pick]); } /* * rndNewList -- alloc, init, and return a new random item list * * Args: * items = pointer to an array of opaque item pointers * num = number of elements in items array */ RNDLIST *rndNewList(int num, void **items) { register RNDLIST *listp; register int i; listp = (RNDLIST *) malloc(sizeof(RNDLIST) + (num - 1) * sizeof(void *)); assert(listp); if(!listp) { return (NULL); } listp->num = num; listp->lastidx = 0; for(i = NEL(listp->last); --i >= 0;) { /* init last array */ listp->last[i] = (u_num) ~ 0U; } for(i = num; --i >= 0;) { /* init items array */ listp->items[i] = items[i]; } return (listp); } /* * rndInit -- Initialize random(3) with a good random seed and prepare a * separate random state for gfxDrawSky(). */ void rndInit(void) { unsigned int seed; struct tms tms; /* It doesn't matter what seed we use for gfx_random; it will be */ /* srandom()ed in gfxDrawSky() anyway. */ /* (void) initstate(0, gfx_random.c, sizeof(gfx_random)); */ seed = (uint) times(&tms) + (uint) time((void *) NULL) + (getpid() << 16) + getppid(); /* (void) initstate(seed, main_random.c, sizeof(main_random)); setstate(main_random.c); */ /* * Shouldn't be needed if the random(3) man page can be believed, * still we're getting reports of identical runs of random numbers * on some systems, so do it anyway. */ srandom(seed); }