/* randP.nqh v1.1 Rik Blok, August 12, 2001 http://rikblok.shorturl.com/nqc A pseudo-random number generator. The built-in function Random() is unsuitable for multiple bots because it always begins with the same seed so each bot generates the same sequence of random numbers. This file contains two functions for using random numbers with NQC to get around the problem: rand(var,mod); // set val in 0..mod-1 (inclusive) srand(); // seed the number generator randomly var is a variable name, which takes on a random value between 0 and abs(mod)-1 (inclusive) with the same sign as mod. Eg. rand(var,-4) randomly sets var to a value of 0,-1,-2, or -3. Although the numbers generated are not truly random they are produced quickly and are fairly unpredictable. Further, the initial seed (which uniquely determines the sequence of numbers generated) is genuinely random. Notice it takes about 1/2 second to generate the first number (the seed) but this can be avoided by calling srand() manually early in the main() task. Before #including this file the user may optionally #define RAND_SENSOR to the index of the desired sensor to use to generate fluctuations (the index is base 0 so 0==SENSOR_1, etc.). It is best to use a sensor like a temperature or light sensor, which has a lot of intrinsic noise. Touch sensors will also work but will be slower. If not specified RAND_SENSOR defaults to 0. The fluctuations are only used to generate the initial seed. Thereafter numbers are generated using a pseudo-random number generator. If the user #defines RAND_COMPATMODE before #including this file, the less efficient version of rand() will be used. Since the default version moves the processing into a (memory-efficient) subroutine, rand() cannot then be called from subroutines. If you need to call rand() from subroutines, #define RAND_COMPATMODE before #including this file. Sample usage: ------------ #define RAND_SENSOR 1 // optional #include "randP.nqh" task main() { srand(); int r; int i=0; CreateDatalog(100); // generate 100 random numbers in 0..9 do { rand(r,10); AddToDatalog(r); } while (i<100); } Resources consumed: ------------------ 3 variables (1 global, 2 local) 2 functions (expanded inline) +3 global variables (#ifdef RAND_COMPATMODE) +1 subroutine (#ifdef RAND_COMPATMODE) Revisions: --------- v1.1 August 12, 2001 - can optionally use more efficient (but less versatile) version of rand() by #defining RAND_COMPATMODE before #including this file. Constraint: won't let you call from rand() from within subroutines. (Work in progress.) - no longer depends on bitshift.nqh. If using NQC v2.4+ then uses more efficient form of bitshifting with operators instead of function calls v1.0 February 25, 2001 - initial release */ #ifndef _RANDP_NQH_ #define _RANDP_NQH_ #define RAND_COMPATMODE // other mode isn't working yet /* RAND_SENSOR is the index of the sensor which generates fluctuations for randSensor(). Must be in the range 0..2 (default=0). */ #ifndef RAND_SENSOR #define RAND_SENSOR 0 #endif // #include "bitshift.nqh" (expanded inline, below) //--------------------------------------------------------------------- #ifndef _BITSHIFT_H_ #define _BITSHIFT_H_ #if __NQC__ >= 204 #define bitshiftLeft(in, bits, out) out = in << (bits) #define bitshiftRight(in, bits, out) out = in >> (bits) #else // not __NQC__ >= 204 void bitshiftLeft(int in, int bits, int &out) { out = in; repeat (bits) out *= 2; } void bitshiftRight(int in, int bits, int &out) { out = in; repeat (bits) { // special handling for sign bit if (out < 0) out = ((out & 0x7fff) / 2) | 0x4000; else out /= 2; } } #endif // __NQC__ >= 204 #endif // _BITSHIFT_H_ //--------------------------------------------------------------------- int randSeed = 0; //--------------------------------------------------------------------- void srand() /* Fills randSeed with bits as necessary, then quits when stack full. Takes ~1/2 second to get sixteen bits. (This should be a function not a sub because it may be called from within a subroutine.) */ { int newSensor = SensorValueRaw(RAND_SENSOR); int oldSensor = newSensor; repeat (16) { newSensor = SensorValueRaw(RAND_SENSOR); if (newSensor!=oldSensor) { bitshiftLeft(randSeed,1,randSeed); randSeed |= newSensor & 0x1; // is new bit 0 or 1? (50/50 chance) oldSensor = newSensor; } } } //--------------------------------------------------------------------- #ifndef RAND_COMPATMODE // use sub version of rand(). More efficient but less versatile. int randMod; int randValue; int randSubBusy = 0; //--------------------------------------------------------------------- void rand(int &var, const int mod) // a shell to call randSub(). { until (!randSubBusy); randMod = mod; randSub(); var = randValue; } //--------------------------------------------------------------------- sub randSub() /* Simple pseudo-random number generator calculates next number (16-bit) in sequence. Problem: always flip/flops between odd and even. */ { randSubBusy = 1; // error trap if (abs(randMod) < 2) { randValue=0; return; } // srand() may accidentally get called (rarely) but that's ok... if (!randSeed) srand(); do { randSeed = 25173 * randSeed + 13849; // use highest bits because they're most random if (abs(randMod) < 3) bitshiftRight(randSeed,15,randValue); else if (abs(randMod) < 5) bitshiftRight(randSeed,14,randValue); else if (abs(randMod) < 9) bitshiftRight(randSeed,13,randValue); else if (abs(randMod) < 17) bitshiftRight(randSeed,12,randValue); else if (abs(randMod) < 33) bitshiftRight(randSeed,11,randValue); else if (abs(randMod) < 65) bitshiftRight(randSeed,10,randValue); else if (abs(randMod) < 129) bitshiftRight(randSeed, 9,randValue); else if (abs(randMod) < 257) bitshiftRight(randSeed, 8,randValue); else if (abs(randMod) < 513) bitshiftRight(randSeed, 7,randValue); else if (abs(randMod) < 1025) bitshiftRight(randSeed, 6,randValue); else if (abs(randMod) < 2049) bitshiftRight(randSeed, 5,randValue); else if (abs(randMod) < 4097) bitshiftRight(randSeed, 4,randValue); else if (abs(randMod) < 8193) bitshiftRight(randSeed, 3,randValue); else if (abs(randMod) < 16385) bitshiftRight(randSeed, 2,randValue); else bitshiftRight(randSeed, 1,randValue); } while (randValue >= abs(randMod)); // discard if out-of-range (keeps deviate uniform) randValue = randValue * sign(randMod); randSubBusy = 0; } //--------------------------------------------------------------------- #else // RAND_COMPATMODE // use function version of rand(). Less efficient but more versatile. //--------------------------------------------------------------------- void rand(int &var, const int mod) /* Simple pseudo-random number generator calculates next number (16-bit) in sequence. Problem: always flip/flops between odd and even. */ { // error trap if (abs(mod) < 2) { var=0; return; } // srand() may accidentally get called (rarely) but that's ok... if (!randSeed) srand(); do { randSeed = 25173 * randSeed + 13849; // use highest bits because they're most random if (abs(mod) < 3) bitshiftRight(randSeed,15,var); else if (abs(mod) < 5) bitshiftRight(randSeed,14,var); else if (abs(mod) < 9) bitshiftRight(randSeed,13,var); else if (abs(mod) < 17) bitshiftRight(randSeed,12,var); else if (abs(mod) < 33) bitshiftRight(randSeed,11,var); else if (abs(mod) < 65) bitshiftRight(randSeed,10,var); else if (abs(mod) < 129) bitshiftRight(randSeed, 9,var); else if (abs(mod) < 257) bitshiftRight(randSeed, 8,var); else if (abs(mod) < 513) bitshiftRight(randSeed, 7,var); else if (abs(mod) < 1025) bitshiftRight(randSeed, 6,var); else if (abs(mod) < 2049) bitshiftRight(randSeed, 5,var); else if (abs(mod) < 4097) bitshiftRight(randSeed, 4,var); else if (abs(mod) < 8193) bitshiftRight(randSeed, 3,var); else if (abs(mod) < 16385) bitshiftRight(randSeed, 2,var); else bitshiftRight(randSeed, 1,var); } while (var >= abs(mod)); // discard if out-of-range (keeps deviate uniform) var = var * sign(mod); } //--------------------------------------------------------------------- #endif // RAND_COMPATMODE #endif //_RANDP_NQH_