Go to notes and algorithms index. ~ Go to home page.

small_random()


My small_random function gets a (pseudo) random integer number between 0 and 255 inclusive. It is not a real random number -- it is looked-up from a table of numbers. You can only get random numbers in the range 0 to 255, you can't specify other ranges... but you could for example divide or bit-shift the result to get smaller number ranges eg: "result >>= 4;" if 'result' is you variable holding your random number from small_random() then the range would be 0 to 15 instead.

This is different from normal random number generators in that every 256 calls it will have geven every number between 0 and 255 inclusive as a result once. So you could instead think of it as a function that goes through the range 0 to 255 in a random order. A normal random number generator may well miss out some numbers and repeat some.

I developed it for a game, where I wanted different types of enemies attack you. I wanted the type of enemy to be selected randomly, but I did not want any of the enemies to be missed-out. Instead I could for example have kept an array of flags, one for each enemy, and set an enemy's flag when its type appeared so that the program can tell what enemies have still to be used.

PHASES: In the source code it uses what I call 'phases'... by this I mean each set of 256 numbers that are all different. The first time you call small_random() you'll get the first number from the first phase, then the second number from the first phase, and so on. When you have called it 256 times then you will have received all numbers from 0 to 255 (inclusive), and the next call you'll get the first number in the second phase and so on. You cannot guarantee to get all different 256 numbers if your 256 calls span two phases -- you have to complete one whole phase to get every number... but this means that 512 calls starting any where in a phase will guarantee you get every number.

If call the small_randoms_make_thread_phases_consistant() routine the phases will be gone though in order. Otherwise every time a phase is completed the next phase will not necessarily be next in sequence (this is to make games seem using it seem more random).

In actual fact this routine only has a random number table of 256 numbers -- only enough for one phase, but it make-up other phases by performing some sort of operation on the numbers, effectively jumbling them up. This is done to save memory. You can see this in the switch statement near the end of the random_number() function -- you'll see that the first phase uses the numbers from the table 'as is'.

THREADS: A thredding mechanism is provided so that you can have different things in your game using the small_random() function without them effecting each other's random number sequences... for example you could specify thread 0 for getting a random number for the X position where an alien enters at the top of the screen on a shoot-'em-up, and specify 1 as the thread number for getting a random number for what type of alien it should be.

USAGE: First call initialize_small_randoms(), then if required call small_randoms_make_thread_phases_consistant() -- see above. Call void small_randoms_frame_iterate() every iteration of your game loop (or othe places) -- it is just there to vary the phase number that will be next selected. Call small_random() as require to get your random numbers. call



   // SMALL RANDOM NUMBER CODE...
//
unsigned char small_randoms[256] = {
   181, 137, 148,  74,  77, 198,   4, 195, 209, 182,  12, 106, 221, 202,  96, 246,
   223,  14, 243,  93, 134, 196, 152, 120,  76, 159, 166,  68,  72, 212, 211, 151,
   252, 233,  58, 178, 251,  62,  27, 255, 173, 147,  26, 205,  73,  98, 103,  71,
    41,  42, 105,  84, 162,  53,  48, 149,  21, 117, 232,  67, 201,  97, 235, 161,
   110,  25, 144, 234, 214,   6, 139, 174, 129, 132, 119,  90, 104,  69,  16, 100,
   125,  40, 121,  66, 240, 168, 130, 118, 193, 153, 213,   5,  54,  19,  85,  33,
     0,  49, 116,  91,  38, 180, 238, 136,  23, 194, 126,  24,  43, 237, 114,  70,
   192, 172,   8,  83,  60, 123,  65,  87, 124,  29, 236,  89,  56, 254,   7,  88,
   140, 138, 217, 185, 127,  46, 108, 109, 227,  95,  75,  57, 150, 224, 122, 175,
   191, 157, 200, 207,  52, 245,  17, 203,  31,  30,  44, 183,  55, 102, 231, 107,
   188, 111, 242, 165,  47,  20, 249, 101,  36, 247, 143, 113, 177, 179,  13, 133,
   206,  32, 244, 204, 230, 197, 225, 156, 220, 222,   1, 190, 215, 208, 112,  80,
    39, 241,  63, 135, 229, 167,  59,  50, 228, 210,  45,  28,   3, 187,  37, 115,
   142, 239, 253,  79, 169, 171,  94, 155, 219, 128,  11,  15, 154,   9,   2,  10,
    81, 131,  18,  64,  61, 170, 184, 164,  92,  22, 158,  78, 218,  82,  35, 176,
   141, 145,  51,  86, 189, 160,  99, 146, 248,  34, 226, 216, 186, 199, 250, 163  }; // (Table output from SM_RND.BAS)

#define MAX_SMALL_RANDOM_THREADS 15
#define MAX_SMALL_RANDOM_PHASES 8

typedef struct {
   unsigned char call_count;
   unsigned char phase_num;
   unsigned char consistant_flag;
   unsigned char fill1;
   } small_randoms_thread_struct;

typedef struct {
   unsigned char overall_call_count;
   unsigned char frame_count;
   unsigned char fill0;
   unsigned char fill1;
   small_randoms_thread_struct threads[MAX_SMALL_RANDOM_THREADS];
   } small_randoms_struct;

small_randoms_struct small_randoms_data;  // global data area for threads

void initialize_small_randoms( unsigned char p_start_point )
   {
   // Jon P.  The initialization routine for small_random()

   small_randoms_thread_struct *thread_ptr;
   long initial_count;
   short phase_num;

   small_randoms_data.overall_call_count = p_start_point;
   small_randoms_data.frame_count = 255 - p_start_point;


   for ( thread_ptr = &small_randoms_data.threads[0], initial_count = p_start_point, phase_num = 0;
         thread_ptr < &small_randoms_data.threads[MAX_SMALL_RANDOM_THREADS];
         thread_ptr++, initial_count += 100, phase_num++ )
      {
      if ( initial_count > 255 ) initial_count -= 256;
      thread_ptr->call_count = (unsigned char) initial_count;
      if ( phase_num >= MAX_SMALL_RANDOM_PHASES ) phase_num = 0;
      thread_ptr->phase_num = (unsigned char) phase_num;
      thread_ptr->consistant_flag = 0; // default to occationally skipping a phase etc
      }

   }


void small_randoms_frame_iterate( void )
   {
   // Call this routine every frame.
   // (currently it only makes the next phase to be selected a bit more random
   //  so not needed if you have the consistant flag set)

   if ( small_randoms_data.frame_count < 255 )
      small_randoms_data.frame_count++;
   else
      small_randoms_data.frame_count = 0;
   }


void small_randoms_make_thread_phases_consistant( unsigned char p_thread_num )
   {
   // Set this thread to always go though the phases consistantly every time...
   #if DIAGS
      if ( p_thread_num >= MAX_SMALL_RANDOM_THREADS ) err( 5135 );
   #endif

   small_randoms_data.threads[p_thread_num].consistant_flag = 1;
   }


unsigned char small_random( unsigned char p_thread_num )
   {
   // This function returns a random number between 0 and 255.
   // The number is not stictly speaking random... instead 256 numbers
   // are mixed randomy accross 256 elements of the look-up table
   // thus over 256 calls (to the same thread number) any one number
   // between 0 and 255 is guaranteed to come-up once and once only.
   //
   // There are a number of different phases (or banks) of 256
   // numbers (actually the same look-up table jumbled-up), so
   // the same sequence of random numbers won't re-occur for well-over
   // a thousand calls.
   //
   // by Jon P.  www.ProperBostin.co.uk
   //
   small_randoms_thread_struct *thread_ptr;
   unsigned char result;

   #if DIAGS
      if ( p_thread_num >= MAX_SMALL_RANDOM_THREADS ) err( 5135 );
   #endif

   if ( small_randoms_data.overall_call_count < 255 )
      small_randoms_data.overall_call_count++;
   else
      small_randoms_data.overall_call_count = 0;

   thread_ptr = &small_randoms_data.threads[p_thread_num];


   if ( thread_ptr->call_count < 255 )
      {
      thread_ptr->call_count++;
      }
   else
      {
      thread_ptr->call_count = 0;
      thread_ptr->phase_num++;
      if ( thread_ptr->consistant_flag == 0 )
         {
         if ( (small_randoms_data.frame_count & 7) == 0 ) thread_ptr->phase_num++;
         if ( (small_randoms_data.overall_call_count & 15) == 0 ) thread_ptr->phase_num++;
         }
      if ( thread_ptr->phase_num >= MAX_SMALL_RANDOM_PHASES ) thread_ptr->phase_num = 0;
      }

   // Increase the effective size of the random number look-up table
   // by having a number of phases ... where the in first phase
   // the look-up table is run through sequentialy and in the second phase
   // it may be run through backwards, and in subsequent phases in different
   // ways.
   switch( thread_ptr->phase_num )
      {
      case 0:
         result = small_randoms[thread_ptr->call_count];
         break;
      case 1:
         result = small_randoms[small_randoms[thread_ptr->call_count]];
         break;
      case 2:
         result = small_randoms[255 - thread_ptr->call_count];
         break;
      case 3:
         result = small_randoms[255 - small_randoms[thread_ptr->call_count]];
         break;
      case 4:
         result = small_randoms[small_randoms[255 - thread_ptr->call_count]];
         break;
      case 5:
         result = (( small_randoms[thread_ptr->call_count] & 15 ) << 4 );   // swap nibbles
         result |= (( small_randoms[thread_ptr->call_count] & 240 ) >> 4 );
         break;
      case 6:
         result = small_randoms[255 - small_randoms[255 - thread_ptr->call_count]];
         break;
      case 7:
         result = (( small_randoms[thread_ptr->call_count] & 63 ) << 6 );
         result |= (( small_randoms[thread_ptr->call_count] & 192 ) >> 6 );
         break;
      #if DIAGS
        default:
           err( 5137 );
      #endif
      }


   return result;
   }