A lot of my experiments utilize a *lot* of randomness. This is great because it produces very organic looking results, and often leads to unexpected emergent patterns or behaviours that are more interesting than the intended system.
The downside of this is that if I get a particularly beautiful output from an experiment I have no way of capturing or reproducing it besides taking a screenshot. I can’t easily reproduce it because the Flash Player’s Math.random() function is not seeded, meaning there is no way to get the same series of numbers from it again. Thus, every time I run the experiment I get a completely unique result.
To solve this, I decided to build a class to generate random numbers based on a seed number. This is also handy for other uses like statistics, testing, and game development (ex. synchronizing or replaying game play). It was quite straightforward, because Flash Player already has a mechanism for generating a series of random numbers based on a seed hidden away in its APIs â BitmapData.noise().
The noise function can be used to populate a BitmapData instance with random 32 bit colors based on a seed. By pulling these color values out successively you get a series of random numeric values from 0-0xFFFFFFFF (4294967295). Dividing the numbers by the maximum value 0xFFFFFFFF gives you a series of random float values from 0-1 with an increment of about 0.000000000232. While that’s not a perfect random number generator, it’s granular enough to work in virtually any Flash application. It also appears to return the same number series regardless of platform (OS, browser, player version).
The Rndm class is easy to use, and shares the same API as my Rnd non-seeded random class, so that it’s easy to swap them. It can also be used either via a static interface (as with Rnd), or instantiated so that you can have multiple instances with different seeds.
Here’s the interface:
// Note that all members can be accessed either through the static interface or through an instance. // static example: Rndm.seed = 20; var num:Number = Rndm.random(); // instance example: var myRndm:Rndm = new Rndm(20); var num:Number = myRndm.random(); // seed = Math.random()*0xFFFFFF; // sets a random seed // seed = 50; // sets a static seed public var seed:uint; // trace(pointer); // traces the current position in the number series // pointer = 50; // moves the pointer to the 50th number in the series public var pointer:uint; // reset(); // resets the number series, setting pointer to 0 and retaining the same seed public function reset():void // random(); // returns a number between 0-1 exclusive. public function random():Number // float(50); // returns a number between 0-50 exclusive // float(20,50); // returns a number between 20-50 exclusive public function float(min:Number,max:Number=NaN):Number // boolean(); // returns true or false (50% chance of true) // boolean(0.8); // returns true or false (80% chance of true) public function boolean(chance:Number=0.5):Boolean // sign(); // returns 1 or -1 (50% chance of 1) // sign(0.8); // returns 1 or -1 (80% chance of 1) public function sign(chance:Number=0.5):int // bit(); // returns 1 or 0 (50% chance of 1) // bit(0.8); // returns 1 or 0 (80% chance of 1) public function bit(chance:Number=0.5):int // integer(50); // returns an integer between 0-49 inclusive // integer(20,50); // returns an integer between 20-49 inclusive public function integer(min:Number,max:Number=NaN):int
While this allows you to get a completely reproducible series of random numbers, it does not guarantee that your system will run the same each time. Obviously different inputs (user interaction, timer dependencies, improperly reset objects) will alter the output. There are also more subtle forces, such as garbage collection, and the non-deterministic order of for…in loops. When you use a for..in loop in ActionScript, the order that items are iterated in is non-deterministic (ie. you can’t determine which order they will come back in). This became evident in my tree experiments, where the tree would draw differently on subsequent draws (and even on first draw when running in Windows) because I am using Dictionary objects to store references to my Branch instances, and iterating them with for…in.
This issue is significant, because once a single element gets a random value out of order, it disrupts the random values for all subsequent calls (because the pointer has moved from its expected location).
The solution is to use arrays and normal “for” loops, because the order of access is then dictated by your code. A little bit of a tangent from the main discussion in this post, but I found it interesting, and hopefully others will too.
Here’s a quick demo. Clicking it will cause it to redraw. All of the lines are drawn using random x, y, color, size, and alpha values from Rndm using a static seed. Compare the result to the screenshot below. It should look identical. If it doesn’t, that means that there is a platform discrepancy â let me know what OS, player version, and browser you are using in the comments.
You can download the Rndm class here.
UPDATE Jan 24, 2008: At the prompting of Joa in the comments, I reevaluated my decision to use Bmpd versus a Parker-Miller psuedo-random number generator implementation, and for reasons outlined in the comments decided to provide a version of the Rndm class using the PMPRNG approach. It should be a little lighter in memory and CPU (though neither version uses much in the way of resources). You can download it here. Thanks to Michael Baczynski, www.polygonal.de, for the PMPRN implementation in AS3.