Is ActionScript 3 Dictionary a hashmap? - actionscript-3

http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/
The dictionary does what I need but I do need to care about performance. Does anybody know if the Dictionary is implemented as a hashtable?
Or more specifically, does it perform in O(1)?

it acts as a hashmap. in fact, every ActionScript object that is an instance of a dynamic class, acts as hashmap. of course keys can always collide with properties. this behaviour comes from JavaScript. I consider it a design failure.
Array is different in that it will perform some tricks on integer keys, and Dictionary is different in that it doesn't convert keys to strings, but uses any object value as key. Please note that Number and Boolean are both converted to String.
now why whould you care how it is implemented? if it is well implemented, you probably don't wanna know. You can benchmark it. It has O(1) for all operations and is reasonably fast (inserting costs a about twice as much time as an empty method call, deleting costs less). Any alternative implementation will be slower.
here a simple benchmark (be sure to compile it for release and run it in the right player):
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.utils.*;
public class Benchmark extends Sprite {
public function Benchmark() {
var txt:TextField = new TextField();
this.addChild(txt);
txt.text = "waiting ...";
txt.width = 600;
const repeat:int = 20;
const count:int = 100000;
var d:Dictionary = new Dictionary();
var j:int, i:int;
var keys:Array = [];
for (j = 0; j < repeat * count; j++) {
keys[j] = { k:j };
}
setTimeout(function ():void {
var idx:int = 0;
var out:Array = [];
for (j = 0; j < repeat; j++) {
var start:int = getTimer();
for (i = 0; i < count; i++) {
d[keys[idx++]] = i;
}
out.push(getTimer() - start);
}
txt.appendText("\n" + out);
start = getTimer();
for (var k:int = 0; k < i; k++) {
test();
}
txt.appendText("\ncall:"+(getTimer() - start));
idx = 0;
out = [];
for (j = 0; j < repeat; j++) {
start = getTimer();
i = 0;
for (i = 0; i < count; i++) {
delete d[keys[idx++]];
}
out.push(getTimer() - start);
}
txt.appendText("\n" + out);
},3000);//wait for player to warm up a little
}
private function test():void {}
}
}

Nope, it's not. java hashmaps are based on hash codes, while Dictionary is based on strict equality (===) of keys and therefore must not be used if you plan to put objects as keys.
java:
class MyStuff {
public final int id;
MyStuff(int i) {
this.id = i;
}
public int hashCode() {
return this.id;
}
public int equals(MyStuff o) {
return (this.id - o.id);
}
}
Map<MyStuff, Object> m1 = new HashMap<MyStuff, Object>();
m1.put(new MyStuff(1), new Object());
assert(m1.get(new MyStuff(1)) != null); //true
as3:
class MyStuff {
public var id:Number;
public function MyStuff(i:Number):void {
this.id = i;
}
//no notion of hashCode or equals in AS3 Object class,
//so we can't really control how the Objects are compared.
}
var d:Dictionary = new Dictionary();
d[new MyStuff(1)] = {};
trace(d[new MyStuff(1)]); //outputs null
I'm looking into the right way of implementing hashing in AS3, but it does look very unpromising...

The adobe documentation on associate arrays seems to imply that dictionaries are hashmaps:
"You can use the Dictionary class to create an associative array that uses objects for keys rather than strings. Such arrays are sometimes called dictionaries, hashes, or maps."
http://livedocs.adobe.com/flex/3/html/help.html?content=10_Lists_of_data_4.html

Related

How to make housie / bingo game in Flash AS3

It shows an error when written this script has a package that cannot be nested how can I resolve the problem.
If not can anyone give me a new code so that I can try to make the new file?
I have this file in previous stack AS3 Bingo ticket generator
but i couldnt understan how to use it.
package {
import flash.display.Sprite;
import flash.text.TextField;
public class Main extends Sprite{
public var boards:Array = new Array();
private static const AMAUNT_BOARDS:uint = 6;
private static const NUMBER_FIELD_SIZE:uint = 20;
public function Main() {
for(var i:uint = 0; i < AMAUNT_BOARDS; i++)
{
var numbers:Array = genNumbers();
numbers = deleteFields(numbers);
var board:Sprite = getBoard(numbers);
board.y = NUMBER_FIELD_SIZE * 4 * i;
boards.push(board);
addChild(board);
}
}
//generates a 2 Dimensional Array (3x9) with TextFields
//populates the according Numbers and returns a board Sprite
private function getBoard(n:Array):Sprite
{
var s:Sprite = new Sprite();
var a:Array = new Array();
for(var i:uint = 0; i < 3; i++)
{
var b:Array = new Array();
for(var k:uint = 0; k < 9; k++)
{
//create TextFields
var tf:TextField = new TextField();
tf.x = k * NUMBER_FIELD_SIZE;
tf.y = i * NUMBER_FIELD_SIZE;
tf.border = true;
tf.width = NUMBER_FIELD_SIZE;
tf.height = NUMBER_FIELD_SIZE;
if(n[k][i] != 0) // adds the number if the value isn't 0
tf.text = n[k][i]; // Note that i am switching k & i because the number Array is 9x3
b.push(tf);
s.addChild(tf);
}
}
return s;
}
// Turns 4 random numbers out of the lines into 0 And returns the Array
private function deleteFields(a:Array):Array
{
for(var i:uint = 0; i < 3; i++)
{
var r:RandomPlus = new RandomPlus(8,0);
for(var k:uint = 0; k < 4; k++)
{
var t:uint = r.getNum();
a[t][i] = 0;
}
}
return a;
}
// Generates and returns a 2 Dimensional Array (9x3) with random numbers
private function genNumbers():Array
{
var a:Array = new Array();
var st:uint = 1;
var en:uint = 9;
for(var i:uint = 0; i < 9; i++)
{
var line:Array = new Array();
var ran:RandomPlus = new RandomPlus(en,st);
if(i == 0)//accounting for the number differnenz at start
st--;
if(i==7)//accounting for the number differnenz at end
en ++;
st += 10;
en += 10;
for(var e:uint = 0; e < 3; e++)
line[e] = ran.getNum();
a.push(line);
}
return a;
}
}
}
TL;DR: "Main" in the class definition may have to be changed conflict of already existing name. Extends sprite may need to be changed to extends movieclip. RandomPlus either needs an import you don't have or needs to be made into a symbol and exported to Actionscript.
Ok, so a few things:
If you are really putting the code into the stage code area, then you can't use the private identifier.
Unless RandomPlus is an object that you have defined in the stage it is probably either not a method you can use or you don't have the right imports for it. Look up what imports you need for RandomPlus or if it is an object defined in the stage then you may need to turn it into a symbol or if you have already export it to flash ActionScript I think. To do this you have to check the checkbox in the middle of the symbol creation page. It will always give you the error when you do this, but don't worry it is fine that way.
Third thing is that I never extend sprite in the class definition, I always do extends movieclip(not sure of the capitalization, but you can look that up). You may also be running into an error from using "Main" as the name because it may be a conflict with a name or method already defined in flash in general.
One last thing is for the variable declaration(mostly just for making the code more readable). While it is good to not declare variables as global when you don't have to, I tend to like having most of the variables up at the top, because I like to be able to see most of the declarations in one space. It's not necessary, really just a personal opinion, and I know a lot of experienced coders will say to do what you did, but if you want to use the arrays in multiple functions then sometimes it is easier to just declare them globally rather than having to pass a million things in the function call and then having to figure out later where all the variable declarations are called and where they are being passed as arguments. Again it's more of a coder's choice, but you can also just do whatever you feel more comfortable with rather than just following the already laid out rules by people who have more experience coding.
Another optional fix to the organization might be to name variables something meaningful so as to not forget what they are all doing. I am bad at naming as well, but I think it's really better to name them something better than just a single letter or two.
Another thing that can help is placing trace(code or "text"); in different places to see what's going wrong once you have the compiler happy.

actionscript 3.0 how to incorporate algorithm to my code?

I am new to actionscript 3.0 and I am trying to make a deck of cards shuffle, I have succeeded with this but my problem is that my cards are being repeated, so I have duplicates of the same card in a 52 card deck after shuffling. I am trying to create a texas holdem game.
I found this discussion Randomize or shuffle an array but it does not tell me how to incorporate the Fisher–Yates algorithm into my code. I have tried several different methods suggested here and else where over the web and nothing is working (Think the problem is defiantly my lack of experience).
Can someone please give me an example of how to incorporate this into my code or a link to somewhere that will explain how to do this correctly.
Thanks in advance.
Paul
package src.CardDeck
{
public class CardDeck
{
public var allCards:Array = [];
public var cardNames:Array;
public var cardValues:Array;
public var gameType:String;
public var drawnCards:uint = 0;
public function CardDeck(game:String)
{
gameType = game;
cardNames = ["Ace","Two","Three",
"Four","Five","Six",
"Seven","Eight","Nine",
"Ten","Jack","Queen","King"];
if(gameType == "texasholdem")
{
cardValues = [1,2,3,4,5,6,7,8,9,10,10,10,10];
}
makeSuit("Spade");
makeSuit("Heart");
makeSuit("Diamond");
makeSuit("Club");
}
private function makeSuit(suitString:String):void
{
var card:Object;
for(var i:uint = 0; i < cardNames.length; i++)
{
card = {};
card.cardType = suitString;
card.cardName = cardNames[i];
card.cardValue = cardValues[i];
card.isDrawn = false;
allCards.push(card);
}
}
public function shuffle():Array
{
var shuffledArray:Array = [allCards.length];
var randomCard:Object;
do
{
randomCard = getRandomCard();
if(shuffledArray.indexOf(randomCard) == -1)
{
shuffledArray.push(randomCard);
}
}
while(shuffledArray.length < allCards.length)
return shuffledArray;
}
private function getRandomCard():Object
{
var randomIndex:int = Math.floor(Math.random()* allCards.length);
return allCards[randomIndex];
}
}
}
Bug Note:
var shuffledArray:Array = [allCards.length];
Makes an array with a single element which shuffledArray[0] = allCards.length.
In fact you do not need to pre allocate it just say:
var shuffledArray: Array = [];
Here is the classical Fisher–Yates version:
public function shuffleFisherYates():Array {
var shuffledArray:Array = [];
var randomCardIndex: int;
do {
randomCardIndex = Math.floor(Math.random()* allCards.length);
shuffledArray.push(allCards[randomCardIndex]); // add to mix
allCards.splice(randomCardIndex,1); // remove from deck
}while(allCards.length); // Meaning while allCards.length != 0
return shuffledArray;
}
Here is Durstenfeld's (in place) version:
public function shuffleDurstenfeld():Array {
var swap:Object;
var countdown:int = allCards.length-1;
var randomCardIndex: int;
for(i = countdown; i > 0; i--){
randomCardIndex = Math.floor(Math.random()* countdown);
swap = allCards[countdown];
allCards[countdown] = allCards[randomCardIndex];
allCards[randomCardIndex]= swap;
}
return allCards; // shuffled in place
}
Assuming your shuffling code is okay, I think a reason why you are seeing repeated cards is that within your getRandomCard() method, you are not accounting for cards that have been drawn. You randomly generate an index and return the card there in the array....but that card is still in the array and it's possible that same index can be randomly generated again, resulting in the same card being returned.

Create a different class from a string

Hello I have this bit of code, and it what it must do is when there is some class (always extending the Box class) that must be spawned if there is already one of the same type it loads that one and if there is no one it creates a new one.
private var _l:Vector.<Box> = new Vector.<Box>();
public function respawn(shapeId:int, className:String = "Box"):Class {
var l:int = _l.length, i:int;
var c:Class;
var ct:Class = getDefinitionByName(className) as Class;
for (i = 0; i < l; i++) {
c = _l[i];
if (!c.active && c.shapeId == shapeId) {
return c;
}
}
c = new ct();
_l[l] = c;
return c;
}
If I try this code it generates these errors:
Implicit coercion of a value of type com.shapes:Box to an unrelated type Class.
Implicit coercion of a value of type Class to an unrelated type com.shapes:Box
How can I fix this so I can create different types of classes that all extend the Box class and get them into a Vector.<Box>?
Object is the superclass of all ActionScript classes, not Class.
Besides, you can:
var box:Object = new Box();
But you can not:
var box:Box = new Object();
Like Max Golovanchuk said I had to target the Object and not the class.
public function respawn(shapeId:int, className:Class):Object {
var l:int = _l.length, i:int;
var c:Box;
for (i = 0; i < l; i++) {
c = _l[i];
if (c.shapeId == shapeId) {
return c;
}
}
c = new className();
_l[l] = c;
return c;
}

AS3 Closure Confusion

I have a small loop
var a:Array = [{name:Test1},{name:Test2},{name:Test3},{name:Test4}]
var b:GenericButton; //A pretty basic button component
for(var i:int = 0; i < a.length; i++){
b = new GenericButton(a[i].name, function():void { trace(i) });
this.addChild(b);
}
The function supplied to the GenericButton is executed when the button is pressed.
The problem I am having is that when the no matter what button I press the value of 4 (the length of the array) is always output.
How would I ensure that I trace 0 when the first button is pushed, 1 when the second is pushed, etc?
Well, you can simply do:
var f:* = function():void { trace(arguments.callee.index) };
f.index = i;
b = new GenericButton(a[i].name, f);
Better still:
function createDelegate(obj:Object, func:Function):Function
{
var f:* = function ():* {
var thisArg:* = arguments.callee.thisArg;
var func:* = arguments.callee.func;
return func.apply(thisArg, arguments);
};
f.thisArg = obj;
f.func = func;
return f;
}
...
for (...) {
b = new GenericButton(a[i].name,
createDelegate({index: i}, function():void { trace(this.index) }));
}
And in some (most?) cases it would be even better if you created a separate class and passed i into the constructor.
This is most basic error when using closures. You might be thinking that i is set when GenericButton is created. But closure just get direct link to variable i and uses this link when anonymous function is called. By this time, cycle is finished, and all links to i are pointing to same integer with value = 4.
To fix this, just pass value of i somehow - for example, as an additional argument to GenericButton constructor. In this case, a copy of i will be created on each step, with values of 0, 1, 2, 3 - just like you need.
...
b = new GenericButton(a[i].name, function(i:int):void { trace(i); }, i);
...
Store i in GenericButton and pass into function - this causes anonymous function to stop using context variable i (cycle counter) and forces it to use argument i.
Make a function that returns a function. Here's a FlexUnit test method that demonstrates it.
[Test]
public function closureWin():void
{
var functions:Array = [];
var mkFn:Function = function(value:int):Function
{
return function():int
{
return value;
}
}
var i:int;
for (i = 0; i < 10; i++)
{
functions.push(mkFn(i));
}
var j:int;
for(j = 0; j < 10; j++)
{
assertEquals(j, functions[j]());
}
}
Here's a test method demonstrating the behavior you are seeing:
[Test]
public function closureFail():void
{
// basically to see if this works the same way in as3 as it does in javascript
// I expect that all the functions will return 10
var i:int;
var functions:Array = [];
for (i = 0; i < 10; i++)
{
functions.push(function():int{return i});
}
var j:int;
for each (var f:Function in functions)
{
assertEquals(10, f());
}
}

How to Convert one Vector Data to another Vector Data in Actionscript 3.0

Class ShootGame implements IGame
{
}
//ShootGame Vector
var shootGames:Vector.<ShootGame> = new Vector.<ShootGame>();
for(i=0;i<20;i++)
{
shootGames.push(new ShootGame());
}
var igames:Vector.<IGame> = //What is the Simplest method to convert ShootGame to IGame Vector
//I am doing like
igames = new Vector.<IGame>();
for(i=0;i < shootGames.length;i++)
{
igames.push(shootGames[i]);
}
There is a more convenient way to do that.
var igames:Vector.<IGame> = Vector.<IGame>(shootGames);
Notice that when you use "Vector.< IGame >(shootgames)" you are not making a typecast, instead you are creating a new Vector instance and populating it with the content of the "shootGames" Vector.
For more detailed information, go here.
Sample code:
var shootGames:Vector.<ShootGame> = new Vector.<ShootGame>();
for (var i:int = 0; i < 20; i++) {
shootGames.push(new ShootGame());
}
var igames:Vector.<IGame> = Vector.<IGame>(shootGames);
trace("shootGames.length", shootGames.length); //20
trace("igames.length", igames.length); //20