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.
Source Code: Seeded Random in ActionScript 3
Bookmarked your post over at Blog Bookmarker.com!
I thought about randomizing but in a lil bit different way – i need soft “glimpses”, and decided to use perlinNoise instead of just noise.
Your class makes me think i’m moving in the right direction ,)) Thanks alot. =)
Dmitry – cool. That’s definitely one of the advantages to me of these random classes. All of the random number generation occurs in a single place, so if I want to switch it to perlin noise, or a server generated random number sequence, I can without rewriting any of my application code. Cheers.
After heavy Mersenne Twister I’m happily using this one:
http://drawlogic.com/2007/04/30/as3-random-number-generator/
Nice. This kind of thing is also useful in these p2p games I worked on where you want “random” but you want multiple players in synch (and you don’t have a server to do the random()).
My solution was much simpler, though for certain not as elegant… but all I did was first (while authoring) used a loop to generate a list of a few hundred random numbers (using Math.random()–though I rounded off a little I think). I traced that, hardwired the list into my class. The class simply returned the next number in the list. To synch up the multiple users (and to ensure the game didn’t play the SAME every time) I let one player randomly pick which index to begin on. Then, every time anyone needed a “random” number, they asked the class for the next item in the list. I added one more twist by including a “skip factor” so it could skip ahead 1,2,3 (and so on) indexes at a time.
Pretty low-budget really. The flaw was just that my .swf was bigger because of that list (I suppose the ram too)… but it wasn’t much. For a card game I did, I figured this system produced over 10,000 unique shuffles.
If I understand correct you are using the noise() of a BitmapData to get random values based on a seed? Why not use the Park-Miller-Carta PRNG implemented by Michael from polygonal labs? It should be much faster (and better imho).
http://lab.polygonal.de/index.php?s=seed
Joa,
You are understanding correctly.
My reasoning was that in my testing the two implementations are almost identical in terms of speed, with the Bmpd implementation running about 10% slower (47ms versus 52ms to generate and access 200k numbers). The PMPRNG approach has the advantage of using less memory (because it doesn’t need to store it’s value set), and being non-repeating (my approach repeats after 200k values).
The downside is that this PMPRNG implementation has half the value range of the bitmap approach (31 versus 32 bits), and a very predictable relationship between adjacent values in the series (though this may also be true for noise, I haven’t graphed it).
On revisiting this topic though, I notice that I was testing by using the PM_PRNG class, not implementing the algorithm within the Rndm class directly. Because the greatest cost is in accessing the values, not generating them (this is true for the bitmap approach as well), it might provide a significant speed boost over my current implementation. I’ll post back after some testing.
Joa,
I modified my class to use the Parker-Miller approach, and it does run faster (75ms versus 122ms for 200k numbers). I’ll make a version of Rndm available that uses this approach.
Update: added to the main post.
Grant
Really your posts were very cool and thanks for that. Its strange to see a flash guru blog lacking some fix to the “click to activate this control and use this control” activex control fix.
Thanks
Grant
Really your posts were very cool and thanks for that. Its strange to see a flash guru blog lacking some fix to the “click to activate this control and use this control” activex control fix.
Thanks
Interesting approach you took. Though I don’t particularly like those RNGs because they’ve got pretty short periods.
In my work I use a Mersenne Twister algorithm in AS3 to do the same stuff.
It has a period greater than almost any RNG. You can also set it up so that your outputs from the algorithm are entirely reproducible – and it’s platform independent of course.
cheers,
jon
Creative way to “seed” the rng–nice work.
Playing around with the bitmapdata noise() function and some histograms I noticed some strange numeric effects for small seeds (~
tell me how to find different angles in action script3?send me code for ball of paddle in which ball bounces in any direction and calculates angle n comes back to the reverse direction.
paddle moves on mouse move,just left right n right to left.and when ball hits to brick,bricks get discared………….
dude you rock. thx!
Thank you so much, Grant! Genious class!
Great help, thumbs up!
You saved my life. Thanks!!!!
Whenever I am trying to generate a random number, it is scaled by the seed. What am I missing?
http://gskinner.com/blog/assets/RndmPM.zip
you can remove
‘import flash.display.BitmapData’
Hi, I realize I’m reviving a rather old comment thread, but I was taking a look at the latest (non-bitmapdata noise) version and I noticed that the reset() function was doing:
_seed = _currentSeed;
Maybe I have the wrong idea about the usage of the function, but if it’s indeed to reset the RNG to the initial state, shouldn’t it be:
_currentSeed = _seed?
Let me know if I’m completely wrong about this or what that function is for, which is probably the case 🙂
Thanks,
Mark
Tanx!