how to select random objects to display on a fixed position in adobe flash - actionscript-3

I am working on a flash project and I want to select random objects from a number of objects.
For example if I have 15 objects and I want to randomly select just 4 objects and display them on the stage at fixed position.
I have searched different forums and the problems discussed on different forums are about changing the random position of objects
Note that I don't want to randomize objects position on stage I want to select random objects from multiple objects
I have no idea how to do this.
Please help me if anyone can.

The best thing is to put those objects into array, and then get random element from it.
Here is a quick example:
var objects:Array = new Array[obj1, obj2, obj3, obj4, obj5];
var random:Object = objects.splice(int(Math.random() * objects.length), 1)[0];
Have in mind that these are predefined objects - you must populate the array by yourself. Another thing is that this splices the array, which means it removes items from it.
Good luck!

Similar to Andrey Popov's answer but step by step instead of one line:
//an array of symbols
var objects:Array = new Array(red,blue,green);
//Number you need
var needed:Number = 2;
for (var i:Number = 0;i< needed;i++){
var randomPos = int(Math.random()*objects.length);
//Insert fixed position here
objects[i].x = 0;
//remove object from array
objects.splice(i,1);
}

You need some algorithm to get unique N objects from the object pool. For example try this one:
function getItemsFrom(list:Array, count:uint):Array {
var result:Array = [];
var needed:uint = count;
var available:uint = list.length;
while (result.length < count) {
if (Math.random() < needed / available) {
result.push(list[available - 1]);
needed--;
}
available--;
}
return result;
}
//Simple test, and some results are: [16,9,7,5], [14,13,10,1], etc
var test:Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
for(var i:uint = 0; i < 20; ++i){
trace(getItemsFrom(test, 4));
}
After you will get array of objects, place them where you want.
//Place items vertically at (5,5);
var startX:int = 5;
var startY:int = 5;
//Vertical padding between items
var paddingY:int = 10;
//Current Y position
var posY:int = startY;
//Ger 2 random unique items from the given collection "someListWithItems"
var itemsToPlace:Array = getItemsFrom(someListWithItems, 2);
var item:DisplayObject, i:uint, len:uint = itemsToPlace.length;
for (i; i < len; ++i) {
item = itemsToPlace[i];
item.x = startX;
item.y = posY;
//Offset current position on height of object, plus padding
posY += item.height + paddingY;
//Add item to the display list
addChild(item);
}

Related

How to add numbers in array via a loop in AS3?

var arr: Array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
I can add these numbers to an array separately like this but how do I add 1 to 50 at once instead of typing it all the way through?
for (var i:Number=1; i<=50;i++){
var arr:Array(i) = [i];
}
function randomize(a: * , b: * ): int {
return (Math.random() > .5) ? 1 : -1;
}
trace(arr.sort(randomize));
I am trying to implement something like this.
Thank you.
Pretty simple. You can address the Array's elements via square bracket notation. Works both ways:
// Assign 1 to 10-th element of Array A.
A[10] = 1;
// Output the 10-th element of A.
trace(A[10]);
Furthermore, you don't even need to allocate elements in advance, Flash Player will automatically adjust the length of the Array:
// Declare the Array variable.
var A:Array;
// Initialize the Array. You cannot work with Array before you initialize it.
A = new Array;
// Assign some random elements.
A[0] = 1;
A[3] = 2;
// This will create the following A = [1, null, null, 2]
So, your script is about right:
// Initialize the Array.
var arr:Array = new Array;
// Iterate from 1 to 50.
for (var i:int = 1; i <= 50; i++)
{
// Assign i as a value to the i-th element.
arr[i] = i;
}
Just keep in mind that Arrays are 0-based, so if you forget about index 0 it will remain unset (it will be null).

Can you create new sprite out of two or more already existing by taking their overlapping part

I have two spites created by drawPath(). Sometimes they might overlap each other and when it happens i want to create new sprite, which contains only overlapping part. Maybe it is really simple but i couldn't find any solution exept draw a rectangle of zone where they overlaps. So if it could be done i really appreciate your help.
for (var j:int = 0; j < zonesAmount; j++) {
var sp:Sprite = new Sprite();
var zoneCoord:Vector.<Number> = new Vector.<Number>(0, false);
var zoneCommands:Vector.<int> = new Vector.<int>(0, false);
var o:XML;
for each (o in xml.ZONE[j].POINT)
{
var tmpCoord:int = o.#X;
zoneCoord.push(tmpCoord);
tmpCoord = o.#Y;
zoneCoord.push(tmpCoord);
}
zoneCommands.push(GraphicsPathCommand.MOVE_TO);
for (var i:int = 0; i < (zoneCoord.length - 2)/4; i++)
zoneCommands.push(GraphicsPathCommand.CURVE_TO);
sp.graphics.beginFill(xml.ZONE[j].#COLOR);
sp.graphics.drawPath(zoneCommands, zoneCoord);
sp.graphics.endFill();
sp.alpha = 0.1;
zones[xml.ZONE[j].#ID - 1] = sp;
var picsToolt:mapTooltip = new mapTooltip(15, 15, xml.ZONE[j].SOURCE.#NMB, xml.ZONE[j].SOURCE.#SRC);
picsToolt.Register(zones[xml.ZONE[j].#ID - 1], "");
addChild(zones[xml.ZONE[j].#ID - 1]);
}
Don't bother for addChild(zones[xml.ZONE[j].#ID - 1]) it doesn't matter
Here is examples of what i'm trying to do:
first and second
Second one describes exactly in what i'm intrested, red rect is what i can do and blue line is what i want

How to randomly swap shapes' positions in specific locations

HELP! I'm trying to create a memory game and I'm not too sure how to randomize the shapes' positions in set x and y locations. They can't overlap so they must be randomized in 12 different locations every time the user starts the program.
Store your x and y positions in arrays
var xArray=new Array();
var yArray=new Array();
xArray[0]=50;
yArray[0]=50;
xArray[1]=100;
yArray[1]=50;
xArray[2]=150;
yArray[2]=50;
...
Create an array of values for each of your shapes
var valueArray=new Array(0,1,2.....);
Shuffle the values in this array - as3 random array - randomize array - actionscript 3
Set you shape's positions
shape0.x=xArray[valueArray[0]];
shape0.y=yArray[valueArray[0]];
shape1.x=xArray[valueArray[1]];
shape1.y=yArray[valueArray[1]];
shape2.x=xArray[valueArray[2]];
shape2.y=yArray[valueArray[2]];
try that:
function randomSort(a:*, b:*):Number
{
if (Math.random() < 0.5) return -1;
else return 1;
}
// Push 12 positions as new Point() in an array.
var positions:Array = [ new Point(12, 42), new Point(43, 56), new Point(43,87) ]; // ...add 12 positions
var mcs:Array = [mc1, mc2, mc3]; // ...add 12 mcs
positions.sort(randomSort);
// link randomized position to MovieClips:
for (var i:int = 0, l:int = positions.length; i < l, i++ ) {
var mc:MovieClip = mcs[i];
var point:Point = positions[i];
mc.x = point.x;
mc.y = point.y;
}

passing random values from one array to another without repetitions

I have an array (say 'origA') which contains 20 values and also another array (say "itemA" with only 1 value in it. I need to push any 10 random values of "origA" into "itemA". But i cannot push a same value which is already pushed into "itemA".
How can we do this?
You can create a copy of origA and remove from it the items you add to itemA:
Non optimized version:
var origA:Array = [1, 2, 3, 4, 5, 6, 7];
var itemA:Array = [0];
var copyA:Array = origA.concat();
var N:int = 10;
var n:int = Math.min(N, copyA.length);
for (var i:int = 0; i < n; i++) {
// Get random value
var index:int = Math.floor(Math.random() * copyA.length);
var value:int = copyA[index];
// Remove the selected value from copyA
copyA.splice(index, 1);
// Add the selected value to itemA
itemA.push(value);
}
trace(itemA);
//0,1,7,2,6,4,3,5
Optimized version (no calls to length, indexOf, splice or push inside the loop):
var origA:Array = [1, 2, 3, 4, 5, 6, 7];
var itemA:Array = [0];
var copyA:Array = origA.concat();
var copyALength:int = copyA.length;
var itemALength:int = itemA.length;
var N:int = 10;
var n:int = Math.min(N, copyALength);
for (var i:int = 0; i < n; i++) {
// Get random value
var index:int = Math.floor(Math.random() * copyALength);
var value:int = copyA[index];
// Remove the selected value from copyA
copyA[index] = copyA[--copyALength];
// Add the selected value to itemA
itemA[itemALength++] = value;
}
trace(itemA);
//0,2,5,7,4,1,3,6
Edit1: If your original array has only a few items, use my first version or any other solution in the other answers. But if it may have thousands items or more, then I recommend you use my optimized version.
Edit:2 Here is the time taken to copy 1,000 randomly chosen items from an array containing 1,000,000 items:
All other versions: 2000ms
Optimized version: 12ms
Optimized version without cloning the original array: 1ms
// Define how many random numbers are required.
const REQUIRED:int = 10;
// Loop until either the original array runs out of numbers,
// or the destination array reaches the required length.
while(origA.length > 0 && itemA.length < REQUIRED)
{
// Decide on a random index and pull the value from there.
var i:int = Math.random() * origA.length;
var r:Number = origA[i];
// Add the value to the destination array if it does not exist yet.
if(itemA.indexOf(r) == -1)
{
itemA.push(r);
}
// Remove the value we looked at this iteration.
origA.splice(i, 1);
}
Here's a real short one. Remove random items from the original array until you reach MAX, then concat to the target Array:
const MAX:int = 10;
var orig:Array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var target:Array = [];
var tmp:Array = [];
var i : int = -1;
var len : int = orig.length;
while (++i < MAX && len > 0) {
var index:int = int( Math.random()*len );
tmp[i] = orig[index];
orig[index] = orig[--len];
}
target = target.concat(tmp);
EDIT
Adopted #sch's way of removing items. It's his answer that should be accepted. I just kept this one for the while-loop.

AS3: Random Point on Irregular Shape

I have a MovieClip holding an irregular shape such as this one:
I need to generate a random point on this shape.
I can use brute force by generating points within the bounding box and then hitTesting to see if they reside on the irregular shape. However, I'm sure there's a more efficient way to tackle this problem.
What is the most efficient way to generate a random point on an irregular shape?
You mentioned hitTest, but I assume you meant hitTestPoint().
If so, a function go get the random points you mention, would look a bit like this:
function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
var width:Number = target.width,height:Number = target.height;
for(var i:int = 0; i < numPoints ; i++){
var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
else i = i-1;//nope, go back one step - > retry above until it is inside
}
return points;
}
The other I hinted at in my comment involves looping through non transparent pixels in a bitmap data of your object. This method would insure you don't have many duplicates, as opposed to the previous method, but it also means, you have less control over the number of points created and there's extra memory used for creating the bitmap. Still, for documentation purposes, here is the function:
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>();
var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
for(var i:int = 0; i < numPixels; i+=res) {
x = i%bmd.width;
y = int(i/bmd.width);
alpha = pixels[i] >>> 24;
if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
}
return points;
}
function random(from:Number,to:Number):Number {
if (from >= to) return from;
var diff:Number = to - from;
return (Math.random()*diff) + from;
}
And here'a very basic test:
var pts:Vector.<Point> = getRandomPointsInClip(mc,300);
//var pts:Vector.<Point> = getGridPointsInClip(mc,100,4);
for(var i:int = 0 ; i < pts.length; i++) drawCircle(pts[i].x,pts[i].y,3,0x009900);
function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
var width:Number = target.width,height:Number = target.height;
for(var i:int = 0; i < numPoints ; i++){
var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
else i = i-1;//nope, go back one step - > retry above until it is inside
}
return points;
}
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>();
var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
for(var i:int = 0; i < numPixels; i+=res) {
x = i%bmd.width;
y = int(i/bmd.width);
alpha = pixels[i] >>> 24;
if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
}
return points;
}
function random(from:Number,to:Number):Number {
if (from >= to) return from;
var diff:Number = to - from;
return (Math.random()*diff) + from;
}
function drawCircle(x:Number,y:Number,radius:Number,color:uint):void{
graphics.lineStyle(1,color);
graphics.drawCircle(x-radius,y-radius,radius);
}
HTH
If you think of some non-blob like shapes, it's clear the check random pixel, try again method isn't really a good way. The bounding box area could be huge compared to the shape area.
What you could do to improve the effectiveness is getting a vector of the BitmapData of the shape. It should contain all pixels of the bounding box. Update - it would be nice now if we could pick a random point, and remove it from the vector if it isn't inside the shape. Unfortunately the vector only contains the pixels' colour, not the position which is implicit and only correct if we don't change the vector's length. Since we don't need to know the actual colour, we can omit all transparent pixels and store an inside pixel's position as it's value in the vector. This way we don't need to create a new object for each pixel of the shape (that would be quite expensive!).
var v:Vector.<uint> shapeBoxBitmap.getVector(shapeBoxBitmap.rect);
var pixelNum:int = v.length;
for(var i:uint = 0; i < pixelNum; i++) {
if( v[i] && 0xFF000000 == 0) { // transparent pixel, outside off shape
v.splice(i,1);
} else {
v[i] = i;
}
}
//get random point
var randomPixel:int = v[Math.floor(Math.random()*v.length)];
var point:Point = new Point(randomPixel%shapeBitmap.width,int(randomPixel/shapeBitmap.width));