gSkinner - Home

Source Code: Seeded Random in ActionScript 3

Posted on January 23, 2008 by Grant Skinner

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.

Follow @gskinner on Twitter for more news and views on interactive media.
22 Comments

Source Code: Seeded Random in ActionScript 3

Bookmarked your post over at Blog Bookmarker.com!

Posted by: random on Jan 23, 2008 4:34pm URL: http://www.blogbookmarker.com/tags/random

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. =)

Posted by: Dmitry on Jan 23, 2008 8:29pm

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.

Posted by: Grant Skinner on Jan 23, 2008 8:36pm URL: http://gskinner.com/blog/

After heavy Mersenne Twister I'm happily using this one:

http://drawlogic.com/2007/04/30/as3-random-number-generator/

Posted by: maliboo on Jan 24, 2008 2:58am URL: http://maliboo.pl

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.

Posted by: Phillip Kerman on Jan 24, 2008 9:36am URL: http://www.phillipkerman.com/blog

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

Posted by: Joa Ebert on Jan 24, 2008 9:52am URL: http://blog.je2050.de/

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.

Posted by: Grant Skinner on Jan 24, 2008 10:26am URL: http://gskinner.com/blog/

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.

Posted by: Grant Skinner on Jan 24, 2008 10:37am URL: http://gskinner.com/blog/

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

Posted by: Durairaj on Jan 29, 2008 4:21am URL: http://www.expertbuddy.net

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

Posted by: Durairaj on Jan 29, 2008 4:22am URL: http://www.expertbuddy.net

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

Posted by: Jon on Jan 30, 2008 2:36pm

Creative way to "seed" the rng--nice work.

Posted by: Nate Chatellier on Feb 5, 2008 8:28am URL: http://blog.natejc.com

Playing around with the bitmapdata noise() function and some histograms I noticed some strange numeric effects for small seeds (~

Posted by: Mario Klingemann on Apr 13, 2008 3:56pm URL: http://www.quasimondo.com

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.............

Posted by: dorri on Apr 26, 2008 12:32pm

dude you rock. thx!

Posted by: pappo on Mar 24, 2009 2:51pm

Thank you so much, Grant! Genious class!

Posted by: SparkyFlash on Jun 4, 2010 8:10am

Great help, thumbs up!

Posted by: osc on Sep 18, 2010 12:03pm

You saved my life. Thanks!!!!

Posted by: growch on May 19, 2011 8:02am

Whenever I am trying to generate a random number, it is scaled by the seed. What am I missing?

Posted by: Mark Kessler on May 31, 2011 6:24am

http://gskinner.com/blog/assets/RndmPM.zip

you can remove
'import flash.display.BitmapData'

Posted by: oleg on Aug 8, 2011 5:08am URL: http://www.a.ru

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

Posted by: Mark B on Jun 23, 2012 11:02pm

Tanx!

Posted by: Dew on Jul 9, 2012 9:52am