This is a bit awkward but I need an array of static variables. Each time a function is called I need access to these variables. Basically I have an effects functions that is "controlled" by a single variable currently called elapsed_time.
However, I need an elapsed_time for each element that is passed to the function so I can make sure not to run effects on the same element...(for my fade function this causes a flickering effect).
I would like to use the element.id to name my variables.
I just read a post saying that associative arrays are not really associative arrays and that you should not use them here.
Below is my current function that I need to update.
/**
* Effects
*/
var Effects = function( element )
{
this.element = element;
};
Effects.prototype.fade = function( direction, max_time )
{
Effects.elapsed = 0;
/*
Effects.arrayHold = [];
Effects.arrayHold.push( this.element.id );
*/
var persist_element = this.element;
function next()
{
Effects.elapsed += 10;
if ( direction === 'up' )
{
persist_element.style.opacity = Effects.elapsed / max_time;
}
else if ( direction === 'down' )
{
persist_element.style.opacity = ( max_time - Effects.elapsed ) / max_time;
}
if ( Effects.elapsed <= max_time )
{
setTimeout( next, 10 );
}
}
next();
return true;
};
Just use an Object instead of an array
Related
So I have this creep role called storer that is supposed to go fetch energy from containers and bring it to the storage. However, currently, it finds the container closest by Path and with an energy level greater to a certain threshold so that it doesn't wait there for hours every time a miner refills the container.
The problem I have is if I lower the threshold, the storer will run back and forth to the same containers, ignoring any containers further in the room and letting them fill up.
And raising the threshold will make him sit out and wait for too long, not giving him enough time to empty containers and thus the storage will be empty almost all the time.
I need a way for the creep to determine the container with the highest energy and fill up from there.
Here's the code its running:
if ((source = creep.pos.findClosestByPath(FIND_STRUCTURES, {filter: (s) => {return (s.structureType == STRUCTURE_CONTAINER && s.store[RESOURCE_ENERGY] >= 150)}})) != undefined) {
if (creep.withdraw(source, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(source);
}
}
EDIT: here's the code I tried, but I feel like it is using too much CPU power and can be done in a better way:
for (let i = 2000; i>=0; i=i-100) {
source = creep.pos.findClosestByPath(FIND_STRUCTURES, {filter: (s) => {return s.structureType == STRUCTURE_CONTAINER && s.store[RESOURCE_ENERGY] >= i}});
if (source != undefined) {
break;
}
}
if (creep.withdraw(source, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(source);
}
}
What you can do is loop trough all the containers once, get their energy level and pick the highest one. Set a value when the creep is working so the creep dosen't move to another container if he get higher.
This is the code i made for the storer role:
module.exports = {
run: function( creep ) {
// Setting the working variable so the creep focus
// on getting the ressource or returning it.
if ( creep.memory.working && creep.carry.energy == 0 ) {
creep.memory.working = false;
}
if ( ! creep.memory.working && creep.carry.energy == creep.carryCapacity ) {
creep.memory.working = true;
creep.memory.targetContainer = false;
}
if ( creep.memory.working ) {
// Bring the ressources to the storage.
var theStorage = creep.pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: (structure) => {
return (structure.structureType == STRUCTURE_STORAGE );
}
});
if ( creep.transfer( theStorage, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo( theStorage );
}
} else {
// If the creep have a target.
if ( creep.memory.targetContainer ) {
// Go to the container.
var theContainer = Game.getObjectById( creep.memory.targetContainer );
if ( creep.withdraw( theContainer, RESOURCE_ENERGY ) == ERR_NOT_IN_RANGE ) {
creep.moveTo( theContainer );
}
} else {
// Find the container with the most energy.
var target = creep.room.find( FIND_STRUCTURES, {
filter: (structure) => {
return (structure.structureType == STRUCTURE_CONTAINER );
}
});
if ( target.length ) {
var allContainer = [];
// Calculate the percentage of energy in each container.
for ( var i = 0; i < target.length; i++ ) {
allContainer.push( { energyPercent: ( ( target[i].store.energy / target[i].storeCapacity ) * 100 ), id: target[i].id } );
}
// Get the container containing the most energy.
var highestContainer = _.max( allContainer, function( container ){ return container.energyPercent; });
console.log( 'Going for the container id "' + highestContainer.id + '" at ' + highestContainer.energyPercent + '% full.' );
// set the target in memory so the creep dosen't
// change target in the middle of the room.
creep.memory.targetContainer = highestContainer.id;
}
}
}
}
};
I do not know if this method uses more CPU, but it is much simpler to use JavaScript's built in sort method. It allows for the objects in an array to be sorted based on any property or calculation involving it. If you'd like to see the full syntax and some examples: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
This code should work for your purposes. A and B are the two pieces of the array it compares. The return portion tells it the comparison to do on the array elements.
var sources = creep.pos.findClosestByPath(FIND_STRUCTURES,
{filter: (s) => {return (s.structureType == STRUCTURE_CONTAINER &&
s.store[RESOURCE_ENERGY] >= 150)
}});
sources.sort(function(a, b) {return b.store[RESOURCE_ENERGY] - a.store[RESOURCE_ENERGY]});
Then just run your normal code to take energy from the sources. If it starts taking energy from the one with the least amount of energy, I probably got the order of a and b wrong in the return portion so just flip them.
Instead of having the filter do everything for you, why not return a list of structures that match your criteria and then figure out which structure in the list has the most energy? Something like this will find all containers, figure out which has the most energy, and if you have a tie for energy pick the closest one (direct line, doesn't account for obstacles).
var sources = creep.room.find(FIND_STRUCTURES, {
filter: (structure) => {
return (structure.structureType == STRUCTURE_CONTAINER);
}
});
var maxAmount = -1;
var maxSource = null;
var maxRange = 100;
for (var i = 0; i < sources.length; i++) {
if (sources[i].store[RESOURCE_ENERGY] >= maxAmount) {
var range = creep.pos.getRangeTo(sources[i]);
if (sources[i].store[RESOURCE_ENERGY] > maxAmount || range < maxRange) {
maxAmount = sources[i].store[RESOURCE_ENERGY];
maxSource = sources[i];
maxRange = range;
}
}
}
console.log(maxAmount);
console.log(maxSource);
console.log(maxRange);
I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.
The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}
while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.
I tried to make a function to remove all object from scene in one shoot but it removes only one object for invocation.
GeometryModel.prototype.clearScene = function(scene) {
var i;
for(i=0; i < scene.children.length; i++){
obj = scene.children[i];
scene.remove(obj);
}
}
another solution I tried and that works is this:
scene.children={};
but I am not sure if it is correct.
You have to do the opposite:
for( var i = scene.children.length - 1; i >= 0; i--) {
obj = scene.children[i];
scene.remove(obj);
}
because in each iteration the .children array changes once you do a .remove() from the start and the indexing of that array changes.
If you want to understand it better, unroll the for loop and follow what the index into the array is.
You can accomplish that with while :
while (object.children.length)
{
object.remove(object.children[0]);
}
Explanations :
object.children.length return true if object.children.length is not 0, if it's equal to 0 it return false.
So you just have to remove the first child element as long as object has children.
A preferred method is using the scene's traverse function. All objects have this function and will do a depth-first search through the parent's children.
Here's a snippet from Mr. Doob himself.
scene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
var geometry = object.geometry;
var matrixWorld = object.matrixWorld;
...
}
});
And here's a bit from r82's source:
traverse: function ( callback ) {
callback( this );
var children = this.children;
for ( var i = 0, l = children.length; i < l; i ++ ) {
children[ i ].traverse( callback );
}
}
You can also use traverseVisible in your case:
scene.traverseVisible(function(child) {
if (child.type !== 'Scene') {
scene.remove(child);
}
});
The existing answer is good, I just want to provide a fuller response for those running into this same issue. When I'm using hot module reloading with Three.js, I often want to recreate all objects other than the plane and camera. To do that I do the following:
export const reload = (/* updatedDependencies */) => {
console.info('Canceling the run loop...')
cancelAnimationFrame(runLoopIdentifier) // the return value of `requestAnimationFrame`, stored earlier
console.info('Removing all children...')
for (let i = scene.children.length - 1; i >= 0 ; i--) {
let child = scene.children[ i ];
if ( child !== plane && child !== camera ) { // plane & camera are stored earlier
scene.remove(child);
}
}
while (renderer.domElement.lastChild) // `renderer` is stored earlier
renderer.domElement.removeChild(renderer.domElement.lastChild)
document.removeEventListener( 'DOMContentLoaded', onDOMLoad )
conicalDendriteTreeSegments = require('./plantae/conical-dendrite-trees').default
initializeScene() // re-add all your objects
runLoopIdentifier = startRenderRunLoop() // render on each animation frame
console.info('Reload complete.')
}
This solution seems to work better than traversing (according to the documentation of Three.js it is not recommended to use the traverse function to make scene graph changes). Also do not forget to dispose geometries in order to release the memory. It releases first the nodes that are deep in the graph so as to avoid memory leaks.
// Remove all objects
removeObjectsWithChildren(obj){
if(obj.children.length > 0){
for (var x = obj.children.length - 1; x>=0; x--){
removeObjectsWithChildren( obj.children[x]);
}
}
if (obj.geometry) {
obj.geometry.dispose();
}
if (obj.material) {
if (obj.material.length) {
for (let i = 0; i < obj.material.length; ++i) {
if (obj.material[i].map) obj.material[i].map.dispose();
if (obj.material[i].lightMap) obj.material[i].lightMap.dispose();
if (obj.material[i].bumpMap) obj.material[i].bumpMap.dispose();
if (obj.material[i].normalMap) obj.material[i].normalMap.dispose();
if (obj.material[i].specularMap) obj.material[i].specularMap.dispose();
if (obj.material[i].envMap) obj.material[i].envMap.dispose();
obj.material[i].dispose()
}
}
else {
if (obj.material.map) obj.material.map.dispose();
if (obj.material.lightMap) obj.material.lightMap.dispose();
if (obj.material.bumpMap) obj.material.bumpMap.dispose();
if (obj.material.normalMap) obj.material.normalMap.dispose();
if (obj.material.specularMap) obj.material.specularMap.dispose();
if (obj.material.envMap) obj.material.envMap.dispose();
obj.material.dispose();
}
}
obj.removeFromParent();
return true;
}
In the following, why is (input) added after queue.shift()?
var puzzlers = [
function ( a ) { return 8*a - 10; },
function ( a ) { return (a-3) * (a-3) * (a-3); },
function ( a ) { return a * a + 4; },
function ( a ) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function( input, queue ) {
var length = queue.length;
for(var i = 0; i<length; i++){
input = queue.shift()(input);
}
return input;
};
alert(applyAndEmpty(start, puzzlers));
It's clear that moving input into the shift() parentheses, it returns the function itself instead of the correct value. But what are the guidelines for putting (input) after (I can't find documentation on this)?
Because the elements of the array are functions. Functions are objects in JavaScript; they can be passed around, stored in arrays and returned from other functions. You could write as:
var fn = queue[0]; // a function object
var input = fn(input); // execute the function
Now that you know about first class functions, another approach of doing a sequence (or a queue) is composition, where instead of executing a function, assign the value, then execute the next one, you'd create a function that is the composition of all other functions, given that their inputs and outputs match, for example:
function compose() {
return [].reduce.call(arguments, function(f,g) {
return function() {
return g(f.apply(this, arguments));
};
});
};
var puzzle = compose.apply(null, puzzlers);
var result = puzzle(2);
console.log(result); //=> 3
Like elclanrs said, the objects in your array are functions, here's an example
function yell(saying){
alert(saying);
}
function whisper(secret){
console.log(secret);
}
var waysOfSpeaking = [yell, whisper];
// let's yell
waysOfSpeaking[0]('DANGER');
// let's whisper
waysOfSpeaking[1]('I like you');
I would like to know why, in the following code, the elements of coupleFound array are deleted when the function setInterval dies. The idea is, get into the a element and check her tag name "ai".
When he accept this tag he makes a copy of the entire element starting from his parent, and put into the coupleFound array. It works, but only inside of set Interval function! I don't understand because I declared the array outside of the function! I believe this is happening because "set Interval" is not synchronized, but I don't know how fix this problem!
var clicked = 0,
totalClicks = 3,
index = 0,
listIds = new Array("289657", "2680235", "1597254", "269621"),
coupleFound = new Array( ),
videos = document.getElementById( "videos_list" );
var interval = setInterval(function( ) {
coupleList = videos.getElementsByTagName( "a" );
for(var i = coupleList.length; i--;) {
for(j=0; j < listIds.length; j++) {
if(coupleList[i].getAttribute( "ai" ) == listIds[j]) {
coupleFound[index] = coupleList[i].parentNode;
index++;
break;
}
}
videos.removeChild( videos.lastChild );
}
document.getElementById('btnMoreVideos').click();
clicked++;
if(clicked >= totalClicks) {
clearInterval( interval );
alert("I'm inside of the function. The length is:" + coupleFound.length)
}
}, 1000);
alert("The length of the array is:" + coupleFound.length);
What can I do to solve this problem?
You're more or less right. setInterval is asynchronous. Your last line of code will be ran immediately, before setInterval fires for the 3rd time.
There's no good way to sleep/block in JavaScript. You have to restructure your code so that it runs in the order you want. Normally this means using callbacks. i.e., call a function from inside your if(clicked >= totalClicks) block that does what you want, rather than putting it after setInterval.
e.g.,
var clicked = 0,
totalClicks = 3,
index = 0,
listIds = new Array("289657", "2680235", "1597254", "269621"),
coupleFound = new Array( ),
videos = document.getElementById( "videos_list" );
function allDone() {
alert("The length of the array is:" + coupleFound.length);
}
var interval = setInterval(function( ) {
coupleList = videos.getElementsByTagName( "a" );
for(var i = coupleList.length; i--;) {
for(j=0; j < listIds.length; j++) {
if(coupleList[i].getAttribute( "ai" ) == listIds[j]) {
coupleFound[index] = coupleList[i].parentNode;
index++;
break;
}
}
videos.removeChild( videos.lastChild );
}
document.getElementById('btnMoreVideos').click();
clicked++;
if(clicked >= totalClicks) {
clearInterval( interval );
alert("I'm inside of the function. The length is:" + coupleFound.length);
allDone();
}
}, 1000);
Although you should avoid global variables as much as possible. Maybe pass coupleFound or its length into allDone instead if you don't need access to too many vars.