PDA

View Full Version : Array/hash tag help



lmbarns
12-16-2011, 06:36 PM
var lootedItem;

var loot = {
"silver": 3,
"bronze": 3,
"copper": 3,
"platinum": 1,
"iron": 5,
"stone": 5
};
var playerLoot = {
"silver": 0,
"bronze": 0,
"copper": 0,
"platinum": 0,
"iron": 0,
"stone": 0
};



function mining() {
//gold++;
for(var v=0; v <loot.length; v++){
lootedItem = loot[Math.floor(Math.random() * loot.length)];
playerLoot[v]+= lootedItem[v];
}
console.log(lootedItem); //RETURNS 0 every time
console.log(playerLoot); //returns the players array of 0's
}


The array "loot[]" is supposed to be a loot table with a corresponding value. When the function mining() is called, it should randomly return 1 of the values from the array, with the corresponding value.

So the first time it's called, maybe it randomly gives iron, it should give 5 iron to the player's iron variable.

So if it randomly gives you the loot[5] entry as loot, it should increment the corresponding playerLoot[5].


Anyone point me in the right direction? Been glued to the array documentation but not able to get it working....

For right now if I could just get it to return a random entry I can figure out how to assign it to the corresponding entry in the other array...Any ideas how to get it to work right?


lootedItem = loot[Math.floor(Math.random() * loot.length)]; returns 0 every time, I tried various combinations adding
lootedItem[v]= loot[Math.floor(Math.random() * loot.length)][0];
and
lootedItem[v][0]= loot[Math.floor(Math.random() * loot.length)][0];
lootedItem[0][0]= loot[Math.floor(Math.random() * loot.length)][0]; //etc various combinations...

thinking that would return the first value from the random entry but it doesnt work either.

clueful
12-17-2011, 03:16 AM
The array "loot[]" You don't have an array called loot, it is an object which has no length property and its properties cannot be addressed by a numeric index.

You could use for-in to build an array of the properties, then select from it:


<script type="text/javascript">

var loot =
{
"silver": 3,
"bronze": 3,
"copper": 3,
"platinum": 1,
"iron": 5,
"stone": 5
};

var propertyArray = [];

for( var i in loot )
propertyArray.push( i );

alert( propertyArray[ Math.floor( Math.random() * propertyArray.length ) ] );

</script>

jscheuer1
12-17-2011, 04:03 AM
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
var lootedItem;

var loot = {
silver : 3,
bronze : 3,
copper : 3,
platinum: 1,
iron : 5,
stone : 5
};

var playerLoot = {
silver : 0,
bronze : 0,
copper : 0,
platinum: 0,
iron : 0,
stone : 0
};

var lootArray = [];

for (var p in loot){
if(loot.hasOwnProperty(p)){
lootArray.push(p);
}
}

function addLoot(){
lootedItem = lootArray[Math.floor(Math.random() * lootArray.length)];
playerLoot[lootedItem] += loot[lootedItem];
}

function queryLoot(){
alert('item : ' + lootedItem + '\nplayer\'s value : ' + playerLoot[lootedItem]);
}

function allLoot(){
var r = ['Player Loot\n'];
for(var i = 0; i < lootArray.length; ++i){
r.push(lootArray[i] + ' : ' + playerLoot[lootArray[i]]);
}
alert(r.join('\n'));
}

</script>
</head>
<body>
<input type="button" value="add" onclick="addLoot();"><br>
<input type="button" value="query" onclick="queryLoot();"><br>
<input type="button" value="all" onclick="allLoot();">
</body>
</html>

lmbarns
12-17-2011, 05:45 PM
Thank you so much guys. I realized I was confused, initially I thought I was using arrays, then after endless reading through searches it was apparent that hash tables != arrays....


Before seeing this ^^ above from John I had ended up just randomly generating a random number of all of the different ores, with the rarest one only having a chance of randomly giving 0-1 and the commonest ones range between 0-10 but this will clean it up a lot and I want the functionality for a lot of other things, to randomly spit out something different as achievements.....

jscheuer1
12-17-2011, 06:03 PM
A little more on the subject, hopefully it will help in your understanding.

The technical term in javascript for loot and playerLoot is Object. In general programming parlance the concept is also known as an associative array. That is an array consisting of keys and values. It's different than a javascript array, which is known as an indexed array and can contain only values, which are indexed via the order in which they appear in the array.

In some languages an array can behave partly, and in some completely as both, having keys and indexes, but not in javascript. In javascript there is no crossover between the two. But as you see we can approximate aspects of it (a crossover) to suit our purposes.

In javascript an Array is made with the new Array() constructor or by the preferred method - literally placing values in the square brackets. A javascript Object is made either with the new Object() constructor, or as in our example, and the preferred method - literally placing key: value pairs inside the curly brackets.

lmbarns
12-20-2011, 01:30 AM
Are the individual "properties" objects as well? Reason I ask is because

function test() {
localStorage.setItem('lootIndex0', playerLoot[lootArray[0]]);

localStorage.getItem('lootIndex0'); // returns null

console.log(localStorage.lootIndex0); //DOES return correct amount from playerLoot[lootArray[0]]
}


Will output the correct amount of ore currently in playerLoot[lootArray[2]] in the console making me think it's saving it to localStorage, but the second I try to retrieve it, or compare it to the current playerLoot[lootArray[2]] (after dying and resetting) I get null or undefined.

But if I reset it and just do console.log(localStorage.lootIndex0) on it's own without setItem it returns null so I guess it's not saving but just returning it into the console because it's in the same function?

So I guess I should try json stringify?

I was trying to do this and it returns null as well:

if(localStorage.lootIndex0 < playerInventory[lootArray[0]]) {
localStorage.setItem('lootIndex0',playerInventory[lootArray[0]])
}


Edit:: I'm trying with json and it's the same deal. It'll log the right numbers in the console but when I replay the game the previous localStorage values are null....


localStorage.setItem('index0', JSON.stringify(playerLoot[lootArray[0]]));

var index0 = localStorage.getItem('index0');
console.log('retrievedIndex0: ', JSON.parse(index0)); //returns the correct number

but

var index0 = localStorage.getItem('index0');
if(index0 > JSON.stringify(playerLoot[lootArray[0]])){
localStorage.setItem('index0', JSON.stringify(playerLoot[lootArray[0]]));
}
returns null. I'm trying to compare the current amount to the amount in storage and if the current one is higher then write it to the relevant storage.

jscheuer1
12-20-2011, 05:34 AM
No, the individual values are numbers and can easily type convert to and from strings in the operations you're doing there.

Now, localStorage only works if the page is live. Assuming you have the syntax right, are you doing this live or on your own computer. If the page isn't live, that could be the problem. Or, you may be expecting the functions to do something they're not set up for. For example, this 'works' live:


function test() {
localStorage.setItem('lootIndex0', playerLoot[lootArray[0]]);

alert(localStorage.getItem('lootIndex0')); // alerts correct value
}

But where you have:



function test() {
localStorage.setItem('lootIndex0', playerLoot[lootArray[0]]);

localStorage.getItem('lootIndex0'); // returns null

console.log(localStorage.lootIndex0); //DOES return correct amount from playerLoot[lootArray[0]]
}

What makes you think the highlighted line returns null? You're not doing anything with the value, at least not so far as I can see. How would you know?

lmbarns
12-20-2011, 06:36 AM
Sorry it was

var retrievedIndex0 = localStorage.getItem('index0');
console.log('retrieved0: '+retrievedIndex0 ); //retrieved0: null is result


Closest thing I've gotten towards working is this:



if(localStorage.getItem('index0') < playerLoot[lootArray[0]]){ console.log('bingo! ');
}

That actually now throws 'bingo!' when I get a higher amount of ore this round than before.......but

if(localStorage.getItem('index0') < playerLoot[lootArray[0]]){ console.log('bingo! ');
localStorage.setItem('index0', JSON.stringify(playerLoot[lootArray[0]])); }

the bold never triggers which I assume should overwrite the original entry??

I threw out a few other variables just to see what they returned


var addThis = playerLoot[lootArray[0]]+localStorage.getItem('index0'); returns nn where n is the number, 0 if I don't get any ore, otherwise it's the number twice

I commented out anywhere else that was writing it to localStorage to make sure it wasn't clearing things out and just added a line where "bingo" triggers thinking if it triggers it should then write to localStorage because it's the higher value.

At this point I wouldn't even mind just += adding the new ores gathered during this game to some global amount that I retrieve when the game is loaded.

I just tried it in IE and it throws an error in the console
SCRIPT5007: Unable to get value of the property 'index0': object is null or undefined

clueful
12-20-2011, 10:59 AM
Try
playerLoot[ "lootArray" ][ 0 ];

lmbarns
12-20-2011, 03:38 PM
Try
playerLoot[ "lootArray" ][ 0 ];

That gave a new error
Permission denied for <http://view.atdmt.com> to call method Location.toString

I don't have a clue what that url is to ^^ atdmt.com has nothing to do with anything I'm writing...must be from the json library? I downloaded the json2 file off google code.

This is a screenshot of my console: http://pugetsoundtraining.com/Portfolio/images/jsonError.png

I'm not sure what the difference is between the "null"'s are, if you notice there are 2 different types, 2 are bolder while the rest are italicized and lighter in color....

This is the code that triggers those console logs..

var retrievedIndex0 = localStorage.getItem('index0');
console.log('retrieved0000:'+retrievedIndex0 );
var addThis = playerLoot[lootArray[0]]+localStorage.getItem('index0');
var thisIndex0 = playerLoot[lootArray[0]];
console.log('retrievedIndex0:', JSON.parse(retrievedIndex0));
console.log('playInv: '+playerLoot[lootArray[0]]);
console.log('addThis: '+addThis);

if(localStorage.getItem('index0') < playerLoot[lootArray[0]]){ console.log('bingo! ');
localStorage.setItem('index0', JSON.stringify(playerLoot['lootArray'][0])); }

console.log('newshit: '+localStorage.index0);

I've tried the bolded both ways, the new way and the original way...new way gives new error, old way just doesn't do anything though it outputs "bingo!" only when I have a new high score.............


Elsewhere in my code where I implemented John's amazing array above ^^ I access the array objects as:

function incOres() {
addLoot();
var goDiv = document.getElementById("gold");
goDiv.innerHTML = "<span class='indent'>Gold: "+playerLoot[lootArray[6]]+"</span>";
}

Which adjusts a div on my screen to accurately show the amount of ores in playerLoot[lootArray[6]] so I figured I could just go to my gameOver() function and write all these entries to localStorage....

clueful
12-20-2011, 05:17 PM
I assumed that lootarray was an object property, which it isn't.

Remember that values stored in localStorage are always strings and shouldn't be numerically compared or added.

jscheuer1
12-20-2011, 05:19 PM
You may be over thinking this with logging to the console, the console may be part of the problem. In any case, this works (must be live):


<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
var lootedItem, maxLikelihood = 0;

var loot = {
silver : 3,
bronze : 3,
copper : 3,
platinum: 1,
iron : 5,
stone : 5
};

var playerLoot = {
silver : 0,
bronze : 0,
copper : 0,
platinum: 0,
iron : 0,
stone : 0
};

var lootArray = [];

for (var p in loot){
if(loot.hasOwnProperty(p)){
lootArray.push(p);
maxLikelihood = Math.max(maxLikelihood, loot[p]);
}
}

function addLoot(){
lootArray.sort(function(){return 0.5 - Math.random();});
lootedItem = Math.random() * maxLikelihood;
for(var i = 0; i < lootArray.length; ++i){
if(loot[lootArray[i]] > lootedItem){
lootedItem = lootArray[i];
break;
}
}
playerLoot[lootedItem] = +playerLoot[lootedItem] + 1;
allLoot();
storeIt();
}

function allLoot(){
var r = ['Player Loot\n'];
for(var p in playerLoot){
if(playerLoot.hasOwnProperty(p)){
r.push(p + (p.length < 8? '\t : ' : ' : ') + playerLoot[p]);
}
}
document.getElementById('allloot').firstChild.nodeValue = r.join('\n');
}

function storeIt(){
for(var i = 0; i < lootArray.length; ++i){
localStorage.setItem(lootArray[i], playerLoot[lootArray[i]]);
}
}

function retrieveIt(){
var key;
for(var i = 0; i < localStorage.length; ++i){
key = localStorage.key(i);
if(playerLoot.hasOwnProperty(key)){
playerLoot[key] = localStorage.getItem(key);
}
}
}

function resetPlayer(){
var key;
for(var i = 0; i < lootArray.length; ++i){
key = lootArray[i];
playerLoot[key] = 0;
localStorage.removeItem(key);
}
allLoot();
}
</script>
<style type="text/css">
pre {
font-family: consolas, monospace;
}
</style>
</head>
<body>
<input type="button" value="add" onclick="addLoot();"><br>
<input type="button" value="reset" onclick="resetPlayer();">
<pre id="allloot">&nbsp;</pre>
<script type="text/javascript">
retrieveIt();
allLoot();
</script>
</body>
</html>

Once the player accumulates points for any of the ores, they cannot be removed by refresh. You can remove them with the resetPlayer() function (reset button).

Oh and putting a number into localStorage will automatically type convert it to a string. As a result, it must be type converted back to a number to perform addition on it. I have done so in the above page:


function addLoot(){
lootArray.sort(function(){return 0.5 - Math.random();});
lootedItem = Math.random() * maxLikelihood;
for(var i = 0; i < lootArray.length; ++i){
if(loot[lootArray[i]] > lootedItem){
lootedItem = lootArray[i];
break;
}
}
playerLoot[lootedItem] = +playerLoot[lootedItem] + 1;
allLoot();
storeIt();
}

Preceding a string or number value with a + sign and no space converts it back to a number if it's a string and makes no change if it was already a number. And contrary to what one might think, though it wouldn't come into play here, it doesn't change a negative into a positive value.

Note: This version adds only one point each time and sorts through the loot randomly while assigning the probability of a random ore on the basis of its number in the loot Object. Higher numbers will be picked on average more frequently. In a small sample size this might not always be the case.

lmbarns
12-20-2011, 10:24 PM
Ugh, thank you so much John. Honestly you need to make games, you could pump them out in no time......

Was it because I was testing locally that it didn't work? The code you just posted doesnt work locally but works beautifully the second I uploaded it(per your instructions) to my bluehost account.........

I had that happen when I was doing stuff with ajax where I spent hours trying to get it to work locally with IE only to find out it requires it to be uploaded.

Guess uploading elsewhere should be a first step in my troubleshooting workflow........

edit:: also I'm still digesting this, the pre tag is pretty cool too, I always used span to change text but I think pre is probably a lot better. :)

jscheuer1
12-21-2011, 04:01 AM
There are a number of things that can trip you up testing locally as opposed to live. I had mentioned that about localStorage back in post #7 in this thread:


Now, localStorage only works if the page is live.

AJAX, bookmarks, certain types of javascript in iframe and frames are other things in that category. But those all vary by browser. I don't believe any browser does localStorage unless the page is live.

The pre tag can be useful because its text is pre-formatted (renders all tabs, spaces and line breaks). But that's up to the coder/designer. In this case the same information could be presented in a table. And this is one of those instances where a table is appropriate.

It just seemed easier here to use a pre tag.

I had thought to annotate the addLoot() function with comments a little:


function addLoot(){
lootArray.sort(function(){return 0.5 - Math.random();}); // shuffle lootArray so it will be looped through in a random fashion each time
lootedItem = Math.random() * maxLikelihood; // set a random threshold each time for which items might qualify
for(var i = 0; i < lootArray.length; ++i){
if(loot[lootArray[i]] > lootedItem){ // at the first item encountered in the shuffled array that breaks the random threshold
lootedItem = lootArray[i]; // set that as the item to increment and
break; // exit the loop
}
}
playerLoot[lootedItem] = +playerLoot[lootedItem] + 1; // increment that item
allLoot(); // update the report
storeIt(); // update the storage
}

lmbarns
01-01-2012, 07:12 AM
I had this going so well and hit another roadblock that seems like it should be way simpler than I'm making it.

I made several other arrays following this format and they're amazing. I made a main, giant loot table, then can generate random calls from ranges within the table for different groups of items. It was all going perfectly, when you kill a monster you get a looted item from the relative type...warrior drops warrior items, mage drops magic items, ranger drops ranger items, etc the mine gives you random selection of ores, and finally, when you enter a "trading zone" I update a div that draws entries from all these different arrays, with buttons to buy/sell

For some reason up until this point, it's been letting me just access the item of the array like this: playerLoot[lootArray[1]]-=1; to subtract 1 from that entry in the array.
Example:
function sellIt(what){
if(what==0){ //arrows
if(playerItems[itemArray[4]] > 9) {
playerItems[itemArray[4]]-=10; //arrows
playerLoot[lootArray[7]]+=25; //gold coins
}
else {alert("You dont have one of those!");}
}
else if(what==1){ //bandages
if(playerItems[itemArray[7]] > 9) {
playerItems[itemArray[7]]-=10; //bandages
playerLoot[lootArray[7]]+=50; //gold coins
}
else {alert("You dont have one of those!");}
} //ETC CONTINUED MANY TIMES FOR MANY ITEMS
}
console.log(typeof(playerLoot[lootArray[7]])); //returns number at first
}


Elsewhere I was using playerLoot[lootArray[7]]+=parseInt(Math.random() *25); to add gold to it when I killed a monster.

If I start a game, run around killing monsters, it increments that number just fine, if I go to the vendor and start a transaction I can buy/sell items from a couple different arrays, but the second I click addLoot() to add some ingots to the array, it converts the array entries to string. Then when I try to add/subract, instead of 2+2=4 it's 22...

In the trade div, i have a button that runs sellIt(what); which decrements that particular ingot entry from the array, and increments the array entry for gold_coins.

So I need to know if there is an easy way to access the array entries as numbers, like some command like parseInt(array[i]); which didn't work when I tried, or some other voodoo to access these entries????

I tried a few things so far...adding additional variables to use as a setter/getter but it didn't work. I would do something like:


var gold_coin;
function addSomeGold() {
gold_coin = playerLoot[lootArray[7]];
gold_coin+=10;
playerLoot[lootArray[7]=gold_coin;
}


Doesn't work :( in the console I used typeof to see what it has and when you first log in it's type number, and you can buy/sell things from different arrays but the second you run the addLoot() function for the array that has the ingots and gold_coin entry, it converts to "string"

Seems there should be an easy way to convert back to number????

edit:: Also tried/currently trying

Number(playerLoot[lootArray[7]]);
Number('playerLoot[lootArray[7]]');
playerLoot[Number(lootArray[7])];
playerLoot[Number('lootArray[7]')];
parseFloat('playerLoot[lootArray[7]]')
//etc combinations


All still strings....

jscheuer1
01-01-2012, 08:08 AM
If you know it's a number, albeit represented as a string, the easiest way to ensure it's treated as a number is to precede it by a + sign. So for:



playerLoot[lootArray[7]]+=parseInt(Math.random() *25)

You already know that parseInt(Math.random() *25) is a true number. So, if there is a problem with playerLoot[lootArray[7]], like if it was retrieved from localStorage, which would have made it a string, you can do:


playerLoot[lootArray[7]] = +playerLoot[lootArray[7]] + parseInt(Math.random() *25);

Or for:



var gold_coin;
function addSomeGold() {
gold_coin = playerLoot[lootArray[7]];
gold_coin+=10;
playerLoot[lootArray[7]=gold_coin;
}

You could do:


var gold_coin;
function addSomeGold() {
gold_coin = +playerLoot[lootArray[7]];
gold_coin+=10;
playerLoot[lootArray[7]=gold_coin;
}

But, if that's the only place that string is creeping in - retrieving from localStorage, you could leave the rest of the code alone and nip that part in the bud by converting back to a number when retrieving:


function retrieveIt(){
var key;
for(var i = 0; i < localStorage.length; ++i){
key = localStorage.key(i);
if(playerLoot.hasOwnProperty(key)){
playerLoot[key] = +localStorage.getItem(key);
}
}
}

lmbarns
01-01-2012, 08:18 AM
Thank you for such a quick response. Happy new year.

Exactly what I was looking for. Never seen the "+" used after the = other than to concatenate(sp?) things together or increment stuff.

djr33
01-01-2012, 08:23 AM
You could also use a mathematical formula that doesn't change the value:
var=var+0;
var=var*1;

But I think that just signing the number (as John suggested) is slightly faster to process.