How to change as3 code in a class file - actionscript-3

I am working on some jigsaw puzzles for children on the iPad.
This code is working fine for 3 puzzles, but it is a lot of code that i repeat and i am wondering how to change the code so it will be possible to use a class file. Hope someone can help me with this.
var puzzel1:Array = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16];
var puzzel1k:Array = [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16];
for each(var current_piece:MovieClip in puzzel1){
current_piece.visible = true;
current_piece.addEventListener(MouseEvent.MOUSE_DOWN, drag_piece_puzzel1);
current_piece.addEventListener(MouseEvent.MOUSE_UP, stop_piece_puzzel1);
}
for each(var target_piece:MovieClip in puzzel1k){
target_piece.visible = true;
}
//alle pieces van puzzel 1 worden hiermee gedragd
function drag_piece_puzzel1(e: MouseEvent): void {
current_piece = MovieClip(e.currentTarget);
current_piece.startDrag();
current_piece.scaleX = 0.85;
current_piece.scaleY = 0.85;
addChild(current_piece);
if(current_piece == p1){target_piece = t1;}
if(current_piece == p2){target_piece = t2;}
if(current_piece == p3){target_piece = t3;}
if(current_piece == p4){target_piece = t4;}
if(current_piece == p5){target_piece = t5;}
if(current_piece == p6){target_piece = t6;}
if(current_piece == p7){target_piece = t7;}
if(current_piece == p8){target_piece = t8;}
if(current_piece == p9){target_piece = t9;}
if(current_piece == p10){target_piece = t10;}
if(current_piece == p11){target_piece = t11;}
if(current_piece == p12){target_piece = t12;}
if(current_piece == p13){target_piece = t13;}
if(current_piece == p14){target_piece = t14;}
if(current_piece == p15){target_piece = t15;}
if(current_piece == p16){target_piece = t16;}
}
function stop_piece_puzzel1(e: MouseEvent): void {
current_piece.stopDrag();
if (target_piece.hitTestObject(current_piece)) {
current_piece.scaleX = 1;
current_piece.scaleY = 1;
current_piece.x = target_piece.x;
current_piece.y = target_piece.y;
if (array_pieces.indexOf(current_piece) == -1) {
counter = counter +1;
puzzlepiece = current_piece;
array_pieces.push(this["puzzlepiece"]);
}
}
}

There are a lot of ways you could do this, but here is a quick one based on the fact that your puzzel1 and puzzel1k arrays are lined up:
all of the lines that are like this:
if(current_piece == p1){target_piece = t1;}
Could be replaced with a couple of lines like:
var current_piece_index:int = puzzel1.indexOf(current_piece);
target_piece = puzzel1k[current_piece_index];
Another solution would be to use a Dictionary which is like an array, but instead of being in positions like [0] and [4], the keys are other objects.
To set it up you would iterate through your arrays of pieces, using the pieces as keys and the targets as the associated values, like this:
var targetsByPieces:Dictionary = new Dictionary();
for(var i:uint = 0; i<puzzel1.length; i++){
targetsByPieces[puzzel1[i]] = puzzel1k[i];
}
Then to use it you would just look up the target so all your if(current_piece == p1){target_piece = t1;} code is replaced by one line:
target_piece = targetsByPieces[current_piece];
Neither of these solutions deal with the possibility of there not being a matched target for the piece in play - but usually jigsaws don't have 'spare pieces' so I imagine this isn't an issue for you.

Related

Maze solving algorithm using p5.js

I have generated a maze using depth first search - recursive backtracker algorithm. I also want to solve, but not getting idea about how to start solving my maze.
I am using p5.js to create my maze, and want to solve my already generated maze.
This is my javascript code for generating maze. You might want to add p5.js cdn in your html file if you want to run this code.
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
There are many algorithmsfor solving mazes. One simple way to solve mazes created with the recursive backtracker algorithm is to keep track of the solution as the maze is being generated.
Make the first cell the starting cell and push it onto the solution stack
Make the last cell the goal cell
While the solution stack does not contain the goal cell
if the next neighbor is un-visited push it onto the solution stack
if a cell has no next neighbor pop the solution stack as we are backtracking
When the goal cell is pushed onto the solution stack mark the solution complete
Adapting the questions code so that it also implements the solution algorithm we have:
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
var solution = [];
var goal;
var solutionComplete;
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
grid[grid.length - 1].goal = true;
solution.push(grid[0]);
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
if (!next.visited){
if (!solutionComplete){
solution.push(next);
if (next.goal){
solutionComplete = true;
}
}
}
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
if (!solutionComplete){
solution.pop();
}
}
if (solutionComplete){
for (let i = 0; i < solution.length; i++){
solution[i].solutionCell = true;
}
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.goal = false;
this.solutionCell = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.goal){
noStroke();
fill(0,255,0,100);
rect(x,y,w,w);
}
else if (this.solutionCell){
noStroke();
fill(255,0,0,100);
rect(x,y,w,w);
}else if(this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
It would not be difficult to break the maze generation and solution implementations apart so that the maze is completely generated before the solution is determined but unless there is a restriction that forces us to solve a completed maze it makes sense to build the solution along with the maze.
Sorry about this this is very related but coding train a coding youtuber made a maze generating algorithm, but i don't know if it used depth first search

Flash hangs when I execute this particular loop

Please do forgive me if this question is very stupid, but I couldn't figure out what to do, which is why I ask it.
Here, I declared a small white square as a movieclip symbol(Dot) and I wish to generate it after a specific gap on the entire screen.
So, when I execute this (test it) code on Flash CS6, it hangs. After that I will be forced to end the program without doing anything further.
import flash.ui.*;
stop();
Mouse.hide();
var ctX:int = 0,ctY:int = 0,done:Boolean = false;
var base:Object = MovieClip(root);
this.addEventListener(Event.ENTER_FRAME, eFrame);
function eFrame(event:Event):void
{
while (done == false)
{
var dots:Dot = new Dot ;
dots.x += (50 * ctX);
dots.y += (50 * ctY);
ctX++;
if (ctX == 11)
{
ctX = 0;
ctY++;
}
else if (ctX == 11 && ctY == 10)
{
done = true;
break;
}
stage.addChild(dots);
}
}
Thank you in advance.
I have attached a screenshot of the situation.
The loop will never finish because the condition for done=true is ctX==11, but ctX==11 causes ctX=0 in the first condition:
if (ctX == 11) // when ctX is 11
{
ctX = 0; // ctX is reset to 0
ctY++;
}
else if (ctX == 11 && ctY == 10) // so you will never have ctX=11 here
{
done = true;
break; // (Tip: you don't need `done` since `break` exits the loop)
}
You could fix this by swapping the conditions, but I think this use of a while loop is unnecessarily complex and fragile. Why not just use two for loops:
for (var ctX:int = 0; ctX < 11; ctX++) {
for (var ctY:int = 0; ctY < 11; ctY++) {
var dots:Dot = new Dot();
dots.x = (50 * ctX);
dots.y = (50 * ctY);
stage.addChild(dots);
}
}
This is much clearer and less fragile because the loops are fixed length.
You could even do it with one for loop and a little math, but you lose some clarity:
for (var i:int = 0; i < 11 * 11; i++) {
var dots:Dot = new Dot();
dots.x = (50 * (i % 11));
dots.y = (50 * int(i / 11));
stage.addChild(dots);
}

Making comparisson to items in an Object

I have the following object:
var users:Object= new Object();
users[0]["user_id"] = "1124";
users[0]["name"] = "ikke";
users[0]["age"] = "24";
users[0]["gender"] = "male";
users[1]["user_id"] = "1318";
users[1]["name"] = "test";
users[1]["age"] = "20";
users[1]["gender"] = "male";
var selectors:Object = new Object();
selectors["user_id"] = 1318;
selectors["gender"] = "male";
what i want is to use the selectors object in an if statement. In humans lanuguage it should be something like:
for (var index:String in users) {
If users[index]["gender"] == selectors[gender] && users[index]["user_id"] == "male" -> then trace "success".
}
The tricky part is that the selectors object is dynamic. Sometimes it can contain only 1 item , sometimes 3 items. Or it can also be null. In that case it should allways trace success. Anyone that can help me?
for(var i:int = 0; i < users.length; i++) {
var success:Boolean = true;
for(var key:String in selectors) {
if(users[i][key] != selectors[key]) {
success = false;
break;
}
}
if(success) {
trace('success for user ' + i);
}
}

What is the fastest way to compare two dictionaries?

I'm trying to workout the fastest way to match two dictionaries in Actionscript-3. This is what I've got so far.
function compareDictionaries(p0:Dictionary, p1:Dictionary):Boolean
{
if(p0 == p1) {
return true;
} else {
const matched:Dictionary = new Dictionary();
for(var k0:Object in p0) {
matched[k0] = k0;
if(p0[k0] != p1[k0]) {
return false;
}
}
for(var k1:Object in p1) {
if(matched[k1]) {
continue;
} else {
if(p1[k1] != p0[k1]) {
return false;
}
}
}
return true;
}
}
Obviously it's not ideal to create a new dictionary with the key for the item, but I really don't want to retest the matched item (not that I know if this is any slower or not!). This of course would be circumvented by having a length on the dictionary class, which would make the second for loop redundant.
Any better ideas over this?
EDIT
I created a gist of the benchmark to show the results for a successful match (use the release player)
(results output in (ms))
true
43
true
497
true
22
true
16
EDIT 2
I created another gist showing a miss-hit of equality.
(results output in (ms))
false
1
false
472
false
0
false
0
Something simple like this should work - no new arrays/dictionaries etc:
public function compareDictionaries( d1:Dictionary, d2:Dictionary ):Boolean
{
// quick check for the same object
if( d1 == d2 )
return true;
// check for null
if( d1 == null || d2 == null )
return false;
// go through the keys in d1 and check if they're in d2 - also keep a count
var count:int = 0;
for( var key:* in d1 )
{
// check if the key exists
if( !( key in d2 ) )
return false;
// check that the values are the same
if( d1[key] != d2[key] )
return false;
count++;
}
// now just make sure d2 has the same number of keys
var count2:int = 0;
for( key in d2 )
count2++;
// return if they're the same size
return ( count == count2 );
}
Technically you could just make a direct comparison with the values (as in don't check for the existence of the key in d2) as if you search for a key that's not there, it should resolve to null (or undefined, I'm not 100% sure), but I left it in there in the case where a value in d1 is null and it doesn't exist in d2
Start by comparing strict equality. Then, test equality of the keys in both dictionaries:
Iterate over the properties and return all keys in arrays
Test for equal array length
Sort the arrays
Iterate once more for comparison
Only if all of those tests are passed, compare the actual values:
public function areEqualDictionaries( dict1:Dictionary, dict2:Dictionary ):Boolean {
if(dict1 === dict2) return true;
var keys:Array = equalKeysArrayOrNull( dict1, dict2 );
if(keys)
return haveEqualValues( keys, dict1, dict2 );
else
return false;
}
private function equalKeysArrayOrNull( dict1:Dictionary, dict2:Dictionary ):Array {
var keys1:Array = enumerateKeys( dict1 ).sort();
var keys2:Array = enumerateKeys( dict2 ).sort();
if( keys1.length != keys2.length ) return null;
var i:int = -1;
while(++i < keys1.length)
if(keys1[i] !== keys2[i]) return null;
return keys1;
}
private function haveEqualValues ( keys:Array, dict1 : Dictionary, dict2:Dictionary) :Boolean {
for each (var key:* in keys)
if (dict1[key] != dict2[key]) return false;
return true;
}
private function enumerateKeys( dict:Dictionary ):Array {
var keys:Array = [];
for(var key:* in dict)
keys.push( key );
return keys;
}
Note that dictionaries use identity (strict equality) for matching keys, which makes it necessary to use !== for comparing them. I suppose it's okay to use != for comparing the values, though.
EDIT: Comparison of for vs. for each
Here's my own benchmark for vs. for each:
var dict : Dictionary = new Dictionary();
var keys : Array = [];
for (var i : int = 0; i < 1000000; i++) {
var n : Object = { index:i };
dict[n] = n;
keys.push (n);
}
trace ("benchmark:");
trace ("----------");
var start : int = getTimer();
for each (var key:* in keys) {
var m:* = dict[key];
}
var elapsed : int = getTimer() - start;
trace ("for each:" + elapsed);
trace ("----------");
start = getTimer();
for (var j:int = 0; j < keys.length; j++) {
var o:* = dict[keys[j]];
}
elapsed = getTimer() - start;
trace ("for:" + elapsed);
returns on my machine:
for each:213
----------
for:274

how to trace "depth" or stacking order of a display object?

How can you trace the "depth" or stacking order of a display object with AS3?
I'm trying to figure out if my sprite is behind another sprite...
container.getChildIndex(displayObject);
but that will only tell you how deep it is, not necessarily if anything is in front of it.
Function comparing two DisplayObject instances to determine which one is at a higher "depth" on the display list:
private function higher(a:DisplayObject, b:DisplayObject):DisplayObject
{
// Parent chains
var ac:Array = [a];
var bc:Array = [b];
// Pointers to individual nodes
var an:DisplayObject = a.parent;
var bn:DisplayObject = b.parent;
while (an != null) {
ac.push(an);
an = an.parent;
}
while (bn != null) {
bc.push(bn);
bn = bn.parent;
}
var acl:int = ac.length;
var bcl:int = bc.length;
var n:int = Math.min(acl, bcl);
var i:int = 0;
for (; i < n; i++) {
an = ac[acl - i - 1];
bn = bc[bcl - i - 1];
// First uncommon ancestor
if (an != bn)
break;
}
var ca:DisplayObjectContainer = an.parent;
if (!ca)
return null;
if (ca.getChildIndex(an) > ca.getChildIndex(bn))
return a;
else
return b;
}
Note: If one of the objects is not on the display list, the function returns null. You can change it to return the other object instead.
You can almost certainly optimize this, but this is a first cut.
Just a refactored version of Manish answer using vectors and which won't return weird result if you ever call higher(a,a.parent).
parents() may be used for other purpose too :
public function higher(a:DisplayObject, b:DisplayObject):DisplayObject
{
var aParents:Vector.<DisplayObject> = parents(a);
var bParents:Vector.<DisplayObject> = parents(b);
var commonDepth:int = Math.min(aParents.length, bParents.length);
for (var depth:int = 0; depth < commonDepth; depth++)
if (aParents[depth] != bParents[depth])
break;
if (depth == 0 || depth == commonDepth)
return null;
var commonAncestor:DisplayObjectContainer = aParents[depth].parent;
if (commonAncestor.getChildIndex(aParents[depth]) > commonAncestor.getChildIndex(bParents[depth]))
return a;
else
return b;
}
private function parents(d:DisplayObject):Vector.<DisplayObject>
{
var result:Vector.<DisplayObject> = new Vector.<DisplayObject>;
while (d != null)
{
result.unshift(d);
d = d.parent;
}
return result;
}
private function getDepth(clip:DisplayObject):uint
{
var depth:uint = 0;
var currentClip:DisplayObject = clip;
while (currentClip.parent && currentClip.parent != this)
{
depth++;
currentClip = currentClip.parent;
}
return depth;
}
container.getChildIndex(child) should do it, it returns the index of the child
This is a revised version of what jauboux did from a version Manish did.
Namely, adding a null return value from highestOf() when depths match.
/**
* #param ifDepthsMatchReturnObjectA
* #return Whichever DisplayObject is higher on the display list.
* Optionally returns `null` if they're at the same depth.
*/
public function highestOf(a:DisplayObject, b:DisplayObject, ifDepthsMatchReturnObjectA:Boolean = false):DisplayObject
{
var aParents:Vector.<DisplayObject> = ancestorsOf(a);
var bParents:Vector.<DisplayObject> = ancestorsOf(b);
var commonDepth:int = Math.min(aParents.length, bParents.length);
for (var depth:int = 0; depth < commonDepth; depth++)
if (aParents[depth] != bParents[depth])
break;
if (depth == 0 || depth == commonDepth)
return null;
var commonAncestor:DisplayObjectContainer = aParents[depth].parent;
var aDepthOnCommonAncestor:int = commonAncestor.getChildIndex(aParents[depth]);
var bDepthOnCommonAncestor:int = commonAncestor.getChildIndex(bParents[depth]);
if (aDepthOnCommonAncestor > bDepthOnCommonAncestor)
return a;
else if (aDepthOnCommonAncestor < bDepthOnCommonAncestor)
return b;
else
return ifDepthsMatchReturnObjectA ? a : null;
}
/**
* #return Whether a is higher than b.
*/
public function isHigher(a:DisplayObject, b:DisplayObject):Boolean
{
return highestOf(a, b) === a;
}
/**
* #return All ancestors of given display.
*/
private function ancestorsOf(display:DisplayObject):Vector.<DisplayObject>
{
var result:Vector.<DisplayObject> = new Vector.<DisplayObject>;
while (display != null)
{
result.unshift(display);
display = display.parent;
}
return result;
}