I'm having problems with my javascript+canvas implementation of Conways Game of Life.
The cells are being created just fine and the canvas boxed representing the cells are also being rendered fine. But somewhere along the way all cells seems to be set alive & aren't toggling.
For the life of me I can't understand what's wrong.
The javascript code is here and my implementation is here.
Can someone please tell me where I went wrong.
************************EDIT************************
I think I've figured out whats wrong. I'm passing an element of the 2d array to the reallyToggle() function, like so -
var reallyToggle = function(a) {
//code
}
reallyToggle(cell[i][j]);
I think the problem lies in this part of the code. Can anyone tell me how can I pass an element of an array to a function?
So your code is pretty obscure and overly-complicated to be honest. Creating a grid of custom Javascript function objects is way over-engineering: all you need is a 2D boolean array.
Problems like this are often easiest to solve if thought of as two separate problems: the problem space and the world space. The problem space is the area in which you solve the actual problem. The world space is the problem space mapped to a visual outcome. To separate it out for your problem, think of the problem space as the two dimensional array of booleans and then the world space is your canvas.
If you would like to clean up your simulation a bit, here is an approach that may help:
//Have some parameters that you can change around
var cellSize = 10;
var cellsWide = 100;
var cellsHigh = 100;
//Instantiate and initialize a 2d array to false
var theGrid = new Array();
for (var i=0; i<cellsWide; i++) {
theGrid.push(new Array());
for (var j=0; j<cellsHeight; j++) {
theGrid[i].push(false);
}
}
//Attach a click event to your canvas (assuming canvas has already been dropped on page
//at the assigned width/height
$('#simCanvas').click(function(e) {
var i = Math.floor((e.pageX - this.offsetLeft)/cellSize);
var j = Math.floor((e.pageY - this.offsetTop)/cellSize);
theGrid[i][j] = !theGrid[i][j];
});
This would be a much more succinct way for you to handle the problem space. The mapping of problem space to world space is a bit more straight-forward, but if you need help with that just let me know.
Cheers
This line is rather suspect :)
if (!run) run == true;
First of all, check MoarCodePlz answer. And about Your code:
Uncaught exception: ReferenceError: Undefined variable: gameLoop when pressing start.
All Your functions that are defined in initialize are not in global scope, so they are not reachable in global scope.
Please, use Firebug; Chrome Developer Tools, Opera Dragonfly or something to find out such errors. These tools really help while developing JS.
Related
Sadly, I can't put all of the code in this window, but here's the link to the project:
https://editor.p5js.org/thing1/sketches/KIsvdFvPt
You can duplicate it to edit it and look at all of the files if you so wish. I'm sorry this is such a vague question, but I've worked so hard on this program and now it feels like it's all been for nothing. Any help is appreciated :) I really appreciate any help you can provide. Many thanks!
Firstly, as mentioned in the comments you're removing from an array whilst iterating through it. This is often needed in game development and the common practice is to iterate through the array backwards:
for (let i = comets.length - 1; i >= 0; i--) {
// do your removing
}
However, this is only part of the problem. The reason your sketch is freezing, is because you've got the following code in your Comet class:
polygon(this.x,this.y,this.health*2,this.health/5)
So you're setting the radius and npoints of the polygon to values dependent upon the health of the asteroid, you can imagine why it doesn't like it when a negative value is set!
Wrap this code in a simple if:
if (this.health > 0) {
polygon(this.x, this.y, this.health*2, this.health/5)
}
I am learning coding and am a novice. I am currently trying to convert a Processing(java) sketch to a p5(javascript) sketch to put on my first website.
I'm having trouble translating the Vector and Array syntax from the Processing sketch.
This is the Vector from the processing Sketch (working):
for (int i = pts.size()-1; i >= 0; i --){
PVector pt = (PVector)pts.get(i);
......
PVector pt2 = (PVector)pts.get(j);
if (pt.dist(pt2) < 20){
......
Here is how I've been trying to translate it in p5 (not working)
for (var i = pts.size()-1; i >= 0; i --){
pt = p5.Vector.pts.get(i);
...........
var pt2 = (PVector)pts.get(j);
if (pt.dist(pt2) < 20){
line(pt.x, pt.y, pt2.x, pt2.y);
}
}
You shouldn't try to translate code by going line-by-line and translating the syntax. Instead, you need to take a step back and convert the program into English first. Then you take that English and try to implement it in the targed language. That might sound dumb, but that English is called an algorithm.
So, you should have a description of your program like this:
"Show 10 circles bouncing around the screen. If a circle touches the edge of the screen, it should bounce off by..."
That's just an example, but you get the idea. Then you'd take that and implement it in P5.js.
So instead of saying "how do I convert this array syntax to P5.js", you should be asking yourself "how do arrays work in JavaScript" or even "how do variables work in JavaScript". From there you can read tutorials to figure out how to implement your algorithm in P5.js.
Then if you get stuck on a specific syntax error, please look in the JavaScript console for any errors you're getting. We can't really help if all you tell us is that it's not working. What error are you getting? Where is your MCVE?
All of that being said, I'll try to help with your specific question. Let's take the original line:
PVector pt = (PVector)pts.get(i);
This line is declaring a variable named pt and is pointing it at whatever is returned from pts.get(i), which it casts to a PVector because Java is statically typed so everything needs a type.
Compare that to what you're trying to do in P5.js:
pt = p5.Vector.pts.get(i);
First off, where did you declare the pt variable? Secondly what is p5.Vector.pts? This syntax doesn't make any sense. You need to read up on how variables and arrays work in JavaScript.
Similarly, let's look at this line in your P5.js code:
var pt2 = (PVector)pts.get(j);
Again, where is pts declared? And you never have to cast anything in JavaScript, because it's dynamically typed. Again, you need to go back and read up on how variables in JavaScript work.
Shameless self-promotion: I've written a series of tutorials geared towards Processing developers trying to learn JavaScript, available here.
I am currently trying to implement a small fluid simulation on P5js. I tried to render 20K squares with a random colour. I got a frame rate of 2.xxx.
var sim;
var xdim = 200; var xLength;
var ydim = 100; var yLength;
function setup() {
createCanvas(800,400);
sim = new Sim(xdim, ydim);
}
function draw() {
xLength = width/xdim;
yLength = height/ydim;
for (var i = 0; i < xdim; ++i) for (var j = 0; j < ydim; ++j) {
fill(100);
rect(i*xLength, j*yLength, xLength, yLength);
}
console.log(frameRate());
}
What is the problem behind? Is the library not good enough? Or, the poor configuration of my computer? Or, javascript is not suitable for these kinds of implementation?
We can't help debug your code without an MCVE. Specifically, you haven't provided the Sim class, so we can't run your code at all.
But you need to take a step back and ask yourself this: what performance do you expect? You can't really complain about performance if you didn't have any expectations going in.
Also, you might want to figure out how many squares you can display before seeing a performance hit.
From there it's a game of looking for optimizations. You're going to have to do some profiling to understand exactly where your performance hit is. Maybe you display fewer squares, or maybe you lower the framerate, or maybe you do some pre-rendering. Again, what you do depends on exactly what your expectations and goals are.
I will say that you should take that call to console.log() out of your draw() loop. You should only use that for debugging, and it's not going to improve your performance to call that every single frame.
I have 5000+ LatLng points, and for each of them I would like to find out which feature (region) they belong to. The features come from a kmz layer by Philippe Ivaldi, converted to GeoJSON.
Currently, I am doing this with turfjs in a double for loop. As expected, the calculation freezes the browser for ten minutes, which ain't very convenient.
Here's my code :
function countCeaByLayer(geoJsonLayer){
jQuery.getJSON('http://localhost/server/retrieveData.php', function(data){
var turfPoints = [];
for(var i = 0; i < data.length; i++){
turfPoints.push(turf.point([data[i].longitudeWGS84, data[i].latitudeWGS84]));
}
var features = geoJsonLayer.toGeoJSON().features;
for(var i = 0; i < features.length; i++){
var turfPointsNew = [];
for(var j = 0; j < turfPoints.length; j++){
var isInside = turf.inside(turfPoints[j], features[i]);
if(!isInside) turfPointsNew.push(turfPoints[j]);
}
turfPoints = turfPointsNew;
}
console.log("done");
});
}
What can I do to avoid freezing the browser?
Make it async?
Do the calculation with node and turfjs on a server?
Or deploy leafletjs on a server with node and leaflet-headless?
...or should I just deal with it?
Thanks!
To optimize your code, you should do something like this.
Loop over the points.
For each point, when you iterate over polygons to know if the point is inside one of them, first get the polygon Bounds and see if the point is within the bounds.
If not, you can skip going further and go to the next polygons.
If it's within the bounds, go for a plain check if it is inside the polygon itself.
If it's the case, break the loop iterating over polygons and switch to the next point.
For example, it could be:
points.forEach(function(point) {
polygons.some(function(polygon) {
if (polygon.getBounds().contains(point)) { // or other method if you are not playing with Leaflet features
if (turf.isInside(polygon, point) { // for example, not sure this method actually exists but you get the concept
// point is within the polygon, do tuff
return true; // break the some loop
}
}
});
});
I've myself developped something that exactly does the same thing also based on turf, I run it on the client side (and my loops are made with .some, not classical for loop, so it could even go further in terms of performance) and I never experienced freeze.
From my point of view, 5000 points is peanut for browser to handle, but if your polygons are really complex (dozen of hundreds of thousands of vertices), this can slow down the process of course.
Br,
Vincent
If Stranded Kid's answer is overkill for you,
geoJsonLayer.eachLayer(function(layer){
var within = turf.within(turf.featureCollection(turfPoints),turf.featureCollection([layer.toGeoJSON()]));
console.dir(within);
});
And make sure your coordinates are floats and not strings, because that's what caused the slowdown for me.
I am trying to replicate Conway's Game of Life and though my code seems to be kosher it appears not to behave like it should.
This is the basic meat of it:
if (pressingSpace) {
running = true;
} else {
running = false;
};
if (running) {
for (var i=0; i<tiles.length; i++) {
var tile = tiles[i];
if (tile.alive && (tile.neighborsAlive() < 2 || tile.neighborsAlive() > 3)) {
tile.die();
};
if (!tile.alive && tile.neighborsAlive() == 3) {
tile.comeToLife();
};
};
};
for (var i=0; i<tiles.length; i++) {
var key = tiles[i];
if (!key.alive && mouseDown && key.sprite.contains([cursorX,cursorY]) && cursorX != null) {
key.comeToLife();
}
};
All the functions in play have been thoroughly tested and seem to work as expected. But when this is run, the "alive" squares seem to overrun the screen much more easily than they should. In particular when a column is made three tiles high, it vanishes on the next frame when it should, per the rules, produce two "alive" tiles on either side at the same time.
I suspect this has something to do with the order of operations. Do I need to "mark" appropriate tiles for a state change, instead of change them on the spot? I know there are a lot of implementations of Conway out there but I'm trying to develop this on my own-ish. That said, any nudges in the right direction would be greatly appreciated. Thanks!
You can see it in action here: http://www.eggborne.com/tiles
As far as I can see, it's because of you change tilemap while iterating over it.
E.g. I assume .comeToLife() method changes .alive field to true, and if .neighborsAlive() return non-cached value, but calculates .alive tiles around, you are essentially changing your playfield while iterating, and newly changed cells ruin the whole picture.
Easiest solution will be to create 'old' and 'new' tilemaps, and iterate over 'old' one, while bringing changes to 'new' only. Caching 'neighborsAlive' is essentially creating two arrays, just in different way — that way you're also creating a tilemap, with each tile holding a value of how many neighbors are alive at this moment — and you have to determine this value before everything else changes. If you don't, you will have the same issue, as you currently have.
For demonstration of your problem, make your playfield update with each tile change — you will see your problem animated :)
Hope this helps your issue.