Array values are not being read correctly inside gameloop - javascript

Im creating a program that displays random dots on a canvas, but I am running into problems when I give it rules specifying which dots are supposed to stay lit. I created a canvas with a matching array so for X number of pixels exist an object inside a 2D array. The array is created as follows:
<code>
//create array
ctx.grid = []
for (var i = 0; i < canvasSize; i++) {
ctx.grid.push([]);
for (var e = 0; e < canvasSize; e++) {
ctx.grid[i].push({light:false, clean:true})
}
}
</code>
The gameloop starts creates a random number from 0 to canvasSize*canvasSize
then maques a divicion to filter this number and get an exact location on my array ctx.grid and sets the light property to true.
the following loop finds all the objects in the array that have the property light set to true and draws a square on the canvas by calling the drawPoint() function.
<code>
setInterval(gameLoop,10);
function gameLoop() {
//Get a random number from 0 to canvasSize
var light = Math.floor(Math.random()*(canvasSize*canvasSize));
var row = parseInt(light/canvasSize);
var col = light%canvasSize;
ctx.grid[row][col].light = true;
//Find which points need to be drawn
for (var i = 0; i < ctx.grid.length; i++) {
for (var e = 0; e < ctx.grid[i].length; e++) {
if (ctx.grid[i][e].light) {
drawPoint(i,e);
findCorner(i,e);
clearPoint(i,e);
}
}
}
}
</code>
I have not included all the program's code because they are not essential to solving this problem.
Next is the beginning of what is supposed to be a set of rules,specified by findCorner(), that tell the program when some specific points are supposed to remain drawn on the canvas.
<code>
function findCorner(y,x) {
//Avoid corners
if (y != 0 || x != 0 || y != canvasSize || x != canvasSize) {
if (ctx.grid[y-1][x].light) { //Cannot read property '9' of undefined
//another set of rules
}
// console.log(ctx.grid);
// console.log(y);
// console.log(x);
// console.log(ctx.grid[y-1][x]);
</code>
When I run the program without the findCorners() function, it runs smoothly meaning my array is set up all right, but it is that if statement that is giving me problems. On the developer tools I get the following error:
Cannot read property '9' of undefined
This appears on the line I commented on my code, and the 'number' is always different.
I added the last console logs on my code because strangely enough when I run my program with those lines, the program runs without any errors though it does run very slowly and makes my browser crash after a while.

So it turns out I was wrong in my other comment.
The non-detection of edges was causing you to index into unset parts of your array causing those weird messages and was all of your problems
I scaled it back a bit to look at it
see https://jsfiddle.net/goujdwog/2/
the important thing to get from that is fixing the check at the start of findCorners():
if (y > 0 && x > 0 && y < canvasSize -1 && x < canvasSize -1)

Related

Strange bug in JS code (maybe some variable state caching)

I'm trying to make a small game using JS and Canvas. It is the implementation from old "Stack Attack" 2D platformer from Siemens cellphones.
So I implemented some basic logic to spawn, move around and throw boxes.
Then I came across a very strange bahaviour: sometimes boxes are thrown at the same position within a small time slice, and my special "protecting" function that checks if the column is "available" for throwing at the current moment does not help. Thus the boxes overlap and are going down partly overlapping each other.
I don't know how to efficiently debug this, so I kindly ask to help me with this error. Maybe there is a simple logical mistake in my code that I did not notice.
I made a codepen here: https://codepen.io/anon/pen/ajrqMB
Function moving the boxes down:
function moveColumnDown(pos) {
var b = []
for (var i = 0; i < boxes.length; i++) {
if (boxes[i].x / cw == pos) {
b.push(boxes[i])
}
}
for (var i = 0; i < b.length; i++) {
if (i == 0 && b[i].y < height-3-ch ||
i > 0 && b[i].y + ch < b[i-1].y) {
for (var j = i; j < b.length; j++) {
b[j].y += spd_y
}
break;
}
}
cols_state[pos] = (b.length == 0 || b[b.length-1].y >= y_offset+ch)
}
I'm looking at this code and can't unserstand how even the top overlapping box is going down: the condition i > 0 && b[i].y + ch < b[i-1].y where ch is a single box height should not be satisfied for it anyway...
Also, this line
cols_state[pos] = (b.length == 0 || b[b.length-1].y >= y_offset+ch)
in the end of the function updates the column state. When the box get dropped, column state is set to false. After the box has moved down a little bit in the function moveColumnDown() the state is updated - to false again, I suppose. But still the next box gets dropped for some unknown reason. I thought that maybe they get dropped at the same tick, when the column state is still true - but even in this case assigning false in the drop() function should have mitigated the problem. But the problem still persists.
UPDATE: It seems that the number of dropped boxes in the model doesn't match the real number of displayed boxes (it is much bigger). That's also very strange. On this picture there is 46 boxes (not on the conveyor), but the boxes[] array contains 53 elements.

Using a grid of points to detect if a object is in range

I have a grid of points and I made them using a for loop at the beginning of my app. Each point has two arrays, one is named objectsAroundMe and the other is called pointsAroundMe.
The objective is to detect if the object is near the point (using for loop for both objects and points)
After detection then if the object is in range we push it to the point.objectsAroumdMe array.
I have all of this fine and working, but the problem is getting the point to release the reference when the object is no longer near, I've tried running an if statement to do it and make the reference null but it doesn't work. If there were an efficient way of doing this that made it so only one reference was moving from array to array then that would be perfect. next I'm gonna try using array.splice and slice to copy amd paste references. But for now I've tried using array.filter and indexof and findindex none worked. But I'm newish to classes so if their is a difference between using a for loop iteration Id and using the"this" statement to clarify the object then please give me an example of how I would find the index of a "this" object and delete it's reference from the point array.
onHitTest(){
for (let ii = 0; ii < jsEngine.pointGrid.length; ii++) {
let point = jsEngine.pointGrid[ii];
let distanceBetween = calcTotalDistance(this.transform.x,this.transform.y,point.x,point.y);
let pointPosition = point.x + point.y;
if (!point.objectsAroundMe.includes(this)) {
if ( distanceBetween < mapWidth/density*1.4) {
point.objectsAroundMe.push(this);
this.hitTestArray = point.objectsAroundMe;
this.pointArray = point.pointsAroundMe;
//console.log(this.hitTestArray);
}
if(point.objectsAroundMe.includes(this)) {
if (pointPosition - distanceBetween > 100000) {
let indx= point.objectsAroundMe.indexOf(this);
point.objectsAroundMe[indx] = null;
}
}
}
}
//// second for loop for hit testing the passed array from the point to the object.
for (let i = 0 ; i < this.hitTestArray.length; i++){
let hitTestObject = this.hitTestArray[i];
if(hitTestObject.transform=== null)
continue;
if(hitTestObject === this)
continue;
let distance = calc_distance(this.transform,hitTestObject.transform);
if (distance < hitTestObject.transform.width + hitTestObject.transform.width
&& distance < this.transform.height + this.transform.height){
//console.log("hit!")
}
}
}
Mapwidth = 1000000 and density is 10.
distanceBetween: The distance between the object and the point using: return Math.sqrt((x1 - x2)**2 + (y1 - y2)**2);
this = the object in question (to avoid double for loop)
pointGrid= a grid of points with a total of 90 points equally spaced by mapwidth/density
I found out after finally giving up on this technique after 2 weeks that it really was not performing as well as expected, now I am going to take a similar approach and I will upload my code via: (functions and order of operation) shortly.

Neural Network, gradient descent only finds the average of the outputs?

This problem is more conceptual than in the code, so the fact that this is written in JS shouldn't matter very much.
So I'm trying to make a Neural Network and I'm testing it by trying to train it to do a simple task - an OR gate (or, really, just any logic gate). I'm using Gradient Descent without any batches for the sake of simplicity (batches seem unnecessary for this task, and the less unnecessary code I have the easier it is to debug).
However, after many iterations the output always converges to the average of the outputs. For example, given this training set:
[0,0] = 0
[0,1] = 1
[1,0] = 1
[1,1] = 0
The outputs, no matter the inputs, always converge around 0.5. If the training set is:
[0,0] = 0,
[0,1] = 1,
[1,0] = 1,
[1,1] = 1
The outputs always converge around 0.75 - the average of all the training outputs. This appears to be true for all combinations of outputs.
It seems like this is happening because whenever it's given something with an output of 0, it changes the weights to get closer to that, and whenever it's given something with an output of 1, it changes the weights to get closer to that, meaning that overtime it will converge around the average.
Here's the backpropagation code (written in Javascript):
this.backpropigate = function(data){
//Sets the inputs
for(var i = 0; i < this.layers[0].length; i ++){
if(i < data[0].length){
this.layers[0][i].output = data[0][i];
}
else{
this.layers[0][i].output = 0;
}
}
this.feedForward(); //Rerun through the NN with the new set outputs
for(var i = this.layers.length-1; i >= 1; i --){
for(var j = 0; j < this.layers[i].length; j ++){
var ref = this.layers[i][j];
//Calculate the gradients for each Neuron
if(i == this.layers.length-1){ //Output layer neurons
var error = ref.output - data[1][j]; //Error
ref.gradient = error * ref.output * (1 - ref.output);
}
else{ //Hidden layer neurons
var gradSum = 0; //Find sum from the next layer
for(var m = 0; m < this.layers[i+1].length; m ++){
var ref2 = this.layers[i+1][m];
gradSum += (ref2.gradient * ref2.weights[j]);
}
ref.gradient = gradSum * ref.output * (1-ref.output);
}
//Update each of the weights based off of the gradient
for(var m = 0; m < ref.weights.length; m ++){
//Find the corresponding neuron in the previous layer
var ref2 = this.layers[i-1][m];
ref.weights[m] -= LEARNING_RATE*ref2.output*ref.gradient;
}
}
}
this.feedForward();
};
Here, the NN is in a structure where each Neuron is an object with inputs, weights, and an output which is calculated based on the inputs/weights, and the Neurons are stored in a 2D 'layers' array where the x dimension is the layer (so, the first layer is the inputs, second hidden, etc.) and the y dimension is a list of the Neuron objects inside of that layer. The 'data' inputted is given in the form [data,correct-output] so like [[0,1],[1]].
Also my LEARNING_RATE is 1 and my hidden layer has 2 Neurons.
I feel like there must be some conceptual issue with my backpropagation method, as I've tested out the other parts of my code (like the feedForward part) and it works fine. I tried to use various sources, though I mostly relied on the wikipedia article on backpropagation and the equations that it gave me.
.
.
I know it may be confusing to read my code, though I tried to make it as simple to understand as possible, but any help would be greatly appreciated.

Clearing svg multiple children with a loop not working

I am currently trying to create a reset code so that when the player collides with the enemy, it clears all of the collectibles (I called them pellets) from the svg and then remakes them with a loop I have. For some reason it is clearing every pellet but one. It also does not recreate the pellets like it is supposed to for some reason. The svg variables are score, player, ghost(the enemy) and pellet.
This is the reset code:
function destroyPlayer()
{
alert("Game Over");
score = 0;
scoreElement.textContent = 'score: ' + score;
pelletCount=0;
constantCount = 1;
//need code to take out all pellets from svg
for(i = 0; i < 100; i++)
{
if(svg.children[i].id != "ghost" &&
svg.children[i].id != "score" &&
svg.children[i].id != "player")
{
svg.removeChild(svg.children[i]);
}
}
positionPellet();
positionGhost();
}
And this is the code that remakes the pellets: (the position pellet method)
function positionPellet()
{
while(pelletCount < constantCount*3)
{
var pellet = document.createElementNS( xmlns, 'circle' );
pellet.setAttribute( 'cx', Math.random() * 900 );
pellet.setAttribute( 'cy', Math.random() * 400 );
pellet.setAttribute( 'r' , 10 );
pellet.className.baseVal = "pelClass";
pelletCount++;
svg.appendChild(pellet);
}
}
Have you checked your browser developer console (press F12)? It should be displaying errors it finds in your Javascript.
If that's not helping much, you can log variables to the console to see why your code isn't working.
For instance, I can see immediately why your positionPellet() function isn't working. As a hint try adding some logging to check the value of pelletCount and constantCount.
function positionPellet()
{
console.log("pelletCount = "+pelletCount);
console.log("constantCount= "+constantCount);
while(pelletCount < constantCount*3)
{
...
When you run the code again, you should see those debugging lines with values showing as undefined. Why would that be?
Your other problem is a little more subtle. I'll give you a hint and say that when you remove element 0 from an array, the array is updated immediately. The array item that used to be at children[1] is now at children[0]. But in your next time through the loop, you will be looking at children[1], which won't be the next child any more, it will actually be the one that was after that (originally children[2]).

How to compare Array value to result of 'for' loop in javascript

I have an empty array (called zoomthumbsarray) which gets values pushed to it whilst a 'for' loop is running. This 'for' loop is checking if a thumbnail image is present in the backend against the particular product the user is viewing. If there is an image it gets added into a vertical slider. The current issue is there are non colour specific images (like lifestyle shots) that are being added into the slider multiple times.
So I need to check if the image found in the for loop is currently stored in the array. If it is present, the image has already been generated and I don't want it to get pulled into the slider again. If it hasn't then the image will get added.
Below is the code I am working on. I would presume indexOf would be used but can't get this to work.
Any help would be really appreciated.
var zoomthumbsarray = [] // Empty array which gets populated by .push below during loop
for (var i = 0; i < storeImgsArr.length; i++) { // storeImgsArr finds the quantity of attributes present against the product. This loops and increments counter if there is another attibute image
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) { // Loop and increment counter if there is a Large image
zoomthumbsarray.push(storeImgsArr[i].images.imgS[e].slice(-16)); // Slices off last 16 characters of image path i.e. _navy_xsmall.jpg or 46983_xsalt1.jpg and pushes this into 'zoomthumbsarray' array
// if statement sits here to build the html to add the image to the slider
}
}
zoomthumbsarray = [] // Resets array to zero
ANSWER
As answered by Chris I used $.unique to only keep unique values in the array.
Then wrap an if statement around the code to build the thumb image html if the array === 0 or if the current image isn't already in the array.
Updated code below:
var zoomthumbsarray = [] // Empty array which gets populated by .push below during loop
for (var i = 0; i < storeImgsArr.length; i++) { // storeImgsArr finds the quantity of attributes present against the product. This loops and increments counter if there is another attibute image
if (zoomthumbsarray === 0 || zoomthumbsarray.indexOf(storeImgsArr[i].images.imgS[e].slice(-16)) < 0) { // If statement is true if array === 0 or if the current image isn't already in the array
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) { // Loop and increment counter if there is a Large image
zoomthumbsarray.push(storeImgsArr[i].images.imgS[e].slice(-16)); // Slices off last 16 characters of image path i.e. _navy_xsmall.jpg or 46983_xsalt1.jpg and pushes this into 'zoomthumbsarray' array
zoomthumbsarray = $.unique(zoomthumbsarray); //Keeps only unique elements
// if statement sits here to build the html to add the image to the slider
}
}
}
zoomthumbsarray = [] // Resets array to zero
Some cheap and dirty ideas:
Using underscore/lodash:
zoomthumbsarray = _.uniq(zoomthumbsarray); //Keeps only unique elements
jQuery has one as well:
zoomthumbsarray = $.unique(zoomthumbsarray); //Keeps only unique elements
then you loop through the array and build HTML.
Update:
There's something a bit odd about the rest of the JS. Might this work (if you're using a new enough browser)?
var zoomthumbsarray = [];
storeImgsArr
.map(function(item) { return item.images.imgS; })
.forEach(function(imgS) {
zoomthumbsarray = zoomthumbsarray.concat(imgS.map(function(imagePath) {
return imagePath.slice(-16);
}));
});
zoomthumbsarray = $.unique(zoomthumbsarray);
I have tried indexOf (see first if statement below) but this doesn't work.
As #elclanrs said, indexOf does return the index in the array not a boolean. You only will need to see if it's >= 0 to test whether an image is already contained in the array.
var zoomthumbsarray = [];
for (var i = 0; i < storeImgsArr.length; i++) {
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) {
var image = storeImgsArr[i].images.imgS[e].slice(-16);
if (zoomthumbsarray.indexOf(image) < 0) { // not yet in the array
zoomthumbsarray.push();
// and build the html to add the image to the slider
}
}
}
If you have really lots of images and notice this starts slowing the page down, then there are too many images in your page anyway. No, joke aside; …then check the optimisation by #Ivey.
instead of using an array you can use an object to store the images as keys and a dummy value (possibly true). then you can extract the keys from this object.
var images = {};
for (var i = 0; i < storeImgsArr.length; i++) {
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) {
images[storeImgsArr[i].images.imgS[e].slice(-16))] = true;
}
}
var zoomthumbsarray = [];
for(var k in images) {
zoomthumbsarray.push(k);
// build the html to add the image to the slider
}
EDIT: Added build html comment

Categories

Resources