22.18 A randomizer module

We are going to be randomizing our parameters a lot in this book. In most of our programs we'll want to have a bunch of different objects, and we will want to use randomization to keep the objects from all being the same. In our more advanced programs we may have our objects use randomization so as to give themselves an appearance of interesting behavior.

Even when there's only one object using a given set of parameters, it's not always clear which values to use. It may be that there are no 'best' values, and it's more interesting to look at a wide range of possibilities. In other cases, there may be a 'best' set of values, but the only way to try and find these values is to feel around the parameter space by trying lots of random possibilities.

The C library of standard functions supplies three relevant functions that are of significance here.

long int time(NULL) 
srand(unsigned int seed) 
short int rand() 

Randomizing with the C library

Each call to rand() returns a 15-bit positive integer that lies between 0 and +32K. The way rand works is that the C library maintains a hidden 32-bit integer variable holdrand. When your program starts, holdrand has a default value of 1. Every time you call rand(), holdrand is replaced by holdrand * 214013 + 2531011, and rand returns the low 15 bits of the high word of holdrand. (That is, the return value is (holdrand >> 16) & 0x7FFF.)

If rand starts with the same value of holdrand, it's always going to give you the same sequence of values. So what we usually do is to use the srand function to initialize the randomizer to a fresh state. What srand(seed) does is simply sets the hidden holdrand variable equal to the seed argument.

But how can our deterministic program come up with a 'random' seed to feed into srand ? What we usually do is to use the time(NULL) function. This function returns the number of seconds that have elapsed since midnight, January 1, 1970. For whatever reason, this is the official 'birth instant' of our computer era, a little like the anomalous moment when the Western calendar went from BC to 1AD.

The cRandomizer class

To make it easier to randomize things, we wrap the randomizing functions up inside a class called cRandomizer. Instead of having to use the % operator to bring numbers into range, the cRandomizer has a method random(N) that will give you a number between 0 and N ? 1, a method random(lo, hi) that will give you a number from lo to hi inclusive, a randomReal(loreal, hireal) method to give you a real number between loreal and hireal, a randomColor() method, and so on. The randomBOOL(double truthweight) is a cute function that gives lets you specify a truthweight between 0.0 and 1.0. The truthweight is the probability of the randomBOOL returning a TRUE. So randomBOOL(0.75) would return TRUE three fourths of the time and would return FALSE one fourth of the time.

The default cRandomizer constructor seeds it with the time function. You also have the option of feeding a seed number into the constructor so as initialize your cRandomizer into some specific state. When you're debugging a program it's often a good idea to have the cRandomizer always set to the same state so that it's easier for you to reproduce the exact same behavior over and over.


/* This is a set of 32-bit-based randomizing functions. The functions 
use standard C library techniques. The code is written to be portable, 
although there is one line you need to comment in or out at the head 
of Randomizer.cpp according to whether or not you use Microsoft MFC. 
    These randomizing functions are based on a modular scheme derived 
from the Microsoft implementation of the C library randomizer. In the 
Microsoft implementation, the C Library int rand() function works by 
maintaining a _holdrand variable and iterating _holdrand * 214013 + 
2531011. rand() returns (_holdrand >> 16) & 07FFF, which is a 15 bit 
positive short integer. We use the same scheme, but tailor it to 
return a 32 bit unsigned long integer. Returning _holdrand gives too 
much correlation, so we actually execute the _holdrand update twice, 
and use the high words of the two successive _holdrands as the upper 
and lower words of the value we return. 
    The Randomizer.cpp file includes a historical note at the end about 
an unsuccessful attempt to base the randomizer on Wolfram's CA Rule 30. 

#include "realnumber.h" 
    //For the Real typedef, which is double or float 

class cRandomizer 
    static cRandomizer * _pinstancesingleton; 
    unsigned long _seed; //Start value of _shiftregister 
    unsigned long _shiftregister; 
        //Used internally for the running compute process. 
    unsigned long _thirtytwobits(); 
        // The internal pseudorandom function used. 
        //Uses the C Library randomizer and seeds it with the time. 
    cRandomizer(unsigned long seednumber); 
        //C Library randomizer seeded by seednumber. 
    static cRandomizer * _pinstance(); 
    static void delete singleton (); 
    unsigned long getSeed(){return _seed;} 
    void setSeed(unsigned long seednumber); 
        //Start off in a specific state 
    unsigned long randomizeSeed(void); 
        //Seed with the time in seconds 
    unsigned long random(); //Return an unsigned long int 
    unsigned long random(unsigned long n); 
        //Return an int between 0 and n ? 1 
    long random(long lon, long hin); 
        //int between lon and hin inclusive 
    BOOL randomBOOL(Real truthweight = 0.5); 
        // Return TRUE truthweight often. 
    unsigned char randomByte(void); //Return a byte between 0 and 255 
    unsigned short randomShort(unsigned short n); 
        // Short between 0 and n-1 
    Real randomReal(void); //A real between 0.0 and 1.0 
    Real randomSignedReal(void); //A real between -1.0 and 1.0 
    Real randomReal(Real lo, Real hi); //A real between lo and hi 
    Real mutate(Real base, Real lo, Real hi, Real percent); 
        //Mutate base by percent of size. 
    int mutate(int base, int lo, int hi, Real percent); 
        //Mutate base by percent of size. 
    unsigned long mutateColor(unsigned long base, Real percent); 
        //Mutate a color. 
    Real randomSign(void); //1.0 or -1.0 
    void randomUnitDiskPair(Real *x, Real *y); 
        // Makes (x,y) a random point with distance <= 1 from (0,0) 
    void randomUnitPair(Real *x, Real *y); 
        // Makes (x,y) a random point with distance 1 from (0,0) 
    unsigned long randomColor(); //A Windows COLORREF number. 

Since we're going to use the cRandomizer a lot, you might wonder if, as time goes by, the cRandomizer will accumulate more and more functions. Later, for instance, we might have some cMyClass with parameters that we like to randomize, so will it make sense to add a void cRandomizer::randomizeMyClass(cMyClass &myclassobject) ; method to the cRandomizer class?

No, this would be a bad idea, because then we'd always be coming back and changing the Randomizer.h and Randomizer.cpp files. For sanity's sake, it's much better to try and get your basic, general-purpose files working once and for all and not be continually going back and changing them. This is one of the reasons why we don't use COLORREF as a type in Randomizer.h (instead we use the equivalent unsigned long ).

So how then can we write a method for randomizing a cMyClass with a cRandomizer ? We'll put a randomizing method that's a member of cMyClass, that is, we'll have a void cMyClass::randomize(). And then inside the implementation for this method, we'll use standard cRandomizer calls to randomize the fields of cMyClass.

The static cRandomizer::Randomizer object

So as not keep having to create new cRandomizer objects to randomize things, we give the cRandomizer class a static cRandomizer member. This means that anywhere in our Pop program we can call, say, cRandomizer::RANDOMIZER.randomReal() to get a random real number. This is very much a Java-style thing to do; Java has lots of special members and methods that are statics of its standard classes. This is an example of what's called the 'Singleton pattern.'

In C++ a static such as RANDOMIZER has to be declared in a *.h file and it has to actually be instantiated, or 'live' as an instance, inside a *.cpp file. We put the instantiation inside randomizer.cpp.

cRandomizer* cRandomizer::pinstance()/* pinstance() allocates 
    _pinstancesingleton if it's NULL, then returns it. */ 
    if (cRandomizer::_pinstancesingleton == NULL) 
        //First time pinstance() is called 
#ifndef _DEBUG //not _DEBUG means Release build 
        cRandomizer::_pinstancesingleton = new cRandomizer(); 
    /* In Release build, the default constructor seeds 
        with the time for variety. */ 
#else // _DEBUG means Debug build 
        cRandomizer::_pinstancesingleton = new cRandomizer(1); 
    // In Debug build, use a fixed seed to help replicate bugs. 
#endif //End the _DEBUG switch 
    return cRandomizer::_pinstancesingleton; 

In understanding this code, you need to know that Visual Studio has a line #define DEBUG that gets preprocessed if and only if you are making the Debug build and not the Release build. Thus in our code, the compiler chooses between the two declarations depending on whether you are doing a Debug or a Release build.

We don't want the randomizer to be so random in Debug, because there we like to be able to start up a session over and over and keep seeing the same bug in the same spot.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference