Related
I am new to stack and not quite sure how to use it. But here I am. I am working on a ecosystem project, and I have an animal class with 2 different genders(0 for female, 1 for male). When 2 different genders intersect each other than I want to remove those 2 objects and add a couple(different object, static) object on that position. I kinda did it but it only works for a couple of seconds. Then the code just breaks. In the console it says,
“Uncaught TypeError: Cannot read property ‘intersects’ of undefined”
Here's the question on the processing forum in case I get a solution which might help others: https://discourse.processing.org/t/multiple-object-intersection-and-removal/22900/2
Here's the sketch.js file:
var animats = [];
var couples = [];
function setup() {
frameRate(30);
createCanvas(displayWidth, 470);
for(var i = 0; i < 50; i++)
{
animats[i] = new Animat(random(0, width), random(0, height));
}
}
function draw() {
background(255,100,100);
for(var i = animats.length-1; i >= 0; i--) {
animats[i].birth();
animats[i].grow();
for(var j = i; j >= 0; j--) {
if(j != i && animats[i].intersects(animats[j])) {
animats.splice(i, 1);
animats.splice(j, 1);
}
}
}
}
Here's the animat class file:
function Animat(x, y) {
this.x = x;
this.y = y;
this.gender;
var g = random(0,1);
var c;
if(g > 0.5) {
c = 0;
this.gender = 0;
} else {
c = 255;
this.gender = 1;
}
this.speed = 1;
this.age = 0;
this.length = 0.5;
this.birth = function() {
//gender
//create
noStroke();
fill(c);
text("n", this.x, this.y, this.length, this.length);
ellipse(this.x, this.y, this.length * 2, this.length * 2);
//move
switch(floor(random(0,4))) {
case 0:
this.x += this.speed;
break;
case 1:
this.y += this.speed;
break;
case 2:
this.x -= this.speed;
break;
case 3:
this.y -= this.speed;
break;
default:
this.x++;
this.y--;
}
//bounce
if(this.x > width || this.x < 4){
this.speed *= -1;
}
if(this.y > height || this.y < 4){
this.speed *= -1;
}
}
this.grow = function() {
this.age += 0.01;
this.length += 0.05;
//age checks
if(this.age > 10) {
this.speed + 5;
} else if(this.age > 21) {
this.length = 25;
this.speed = this.speed
//console.log("max age:" + this.age)
} else if(this.age > 70) {
//die
} else {
}
//length checks
if(this.length > 25) {
this.length = 25;
//console.log("max length");
}
}
//relationship
this.intersects = function(other) {
var d = dist(this.x, this.y, other.x, other.y);
var r = this.length + other.length;
if(d < r) {
if(((this.gender == 0) && (other.gender == 1)) || ((this.gender == 1) && (other.gender == 0))) {
return true;
} else {
this.speed *= -1;
}
} else {
return false;
}
}
//mate
this.couple = function() {
if(((this.gender == 0) && (other.gender == 1)) || ((this.gender == 1) && (other.gender == 0))) {
return true;
} else {
this.speed *= -1;
}
}
//die
/*this.die = function() {
if(this.age > 50) {
return true;
} else {
return false;
}
}*/
}
Here's the codepen link for results I am getting:
https://codepen.io/AbrarShahriar/pen/XWXwLPM
Try changing your nested for loop to:
for (var i = animats.length - 1; i >= 0; i--) {
animats[i].birth();
animats[i].grow();
for (var j = i; j >= 0; j--) {
if (j != i && animats[i].intersects(animats[j])) {
animats.splice(i, 1);
animats.splice(j, 1);
break; //exit the inner loop after a match
}
}
}
In other words, add a break; after two animats have coupled and have been removed from the array. The error was probably caused by you trying to call the intersects method of an animat that had already been removed.
you need to keep animats[i] as long as the for-loop for j has not finished.
Here the code you need to fix:
for(var i = animats.length-1; i >= 0; i--) {
animats[i].birth();
animats[i].grow();
let remove = false; // mark 'i' as NOT REMOVE by default
for(var j = i; j >= 0; j--) {
if(j != i && animats[i].intersects(animats[j])) {
remove = true; // mark 'i' as to remove when collision detected
animats.splice(j, 1);
}
}
if (remove) { // remove 'i' after compared to all 'j'
animats.splice(i, 1);
}
}
I am trying to implement John Conway's Game of Life in HTML using canvas and JavaScript.
In a 2D array I store the cell's x-y position and state (alive or dead).
I avoid the outer most cells to avoid having to worry about boundary conditions.
Using requestAnimationFrame, I clear the canvas, update the cells for the next generation based on the number of neighbors, then draw them.
I console log the states of the cells, which do change but for some reason they don't get updated in the canvas.
I have already used this link for reference, but to no avail:
Canvas: X value changing in console but not in canvas
Here is the JavaScript code:
var cv = document.querySelector('#cv');
var c = cv.getContext('2d');
var h = cv.height;
var w = cv.width;
//Cell class
function Cell(alive, x, y) {
this.alive = alive;
this.x = x;
this.y = y;
this.draw = function () {
//c.beginPath();
this.x = x;
this.y = y;
if(alive == true) {
c.fillStyle = 'black';
c.fillRect(this.x, this.y, 10, 10);
}
else if(alive == false){
c.fillStyle = 'white';
c.fillRect(this.x, this.y, 10, 10);
}
//c.stroke();
}
}
//2d array to contain Cell objects
var cellArray = new Array(100);
for (var i = 0; i < cellArray.length; i++) {
cellArray[i] = new Array(70);
}
//initial drawing
for(var i = 0; i < cellArray.length; i++) {
for(var j = 0; j < 100; j++) {
var b = Math.round(Math.random() - 0.4);
if(b == 1) {
cellArray[i][j] = new Cell(true, i * 10, j * 10);
cellArray[i][j].draw();
}
else {
cellArray[i][j] = new Cell(false, i * 10, j * 10);
cellArray[i][j].draw();
}
}
}
//find number of neghbor cells
function neighborSum(cell, i, j) {
this.cell = cell;
this.i = i;
this.j = j;
var sum = 0;
if(cellArray[i - 1][j - 1].alive == true) {
sum += 1;
}
if(cellArray[i][j - 1].alive == true) {
sum += 1;
}
if(cellArray[i - 1][j].alive == true) {
sum += 1;
}
if(cellArray[i + 1][j - 1].alive == true) {
sum += 1;
}
if(cellArray[i - 1][j + 1].alive == true) {
sum += 1;
}
if(cellArray[i + 1][j].alive == true) {
sum += 1;
}
if(cellArray[i][j + 1].alive == true) {
sum += 1;
}
if(cellArray[i + 1][j + 1].alive == true) {
sum += 1;
}
return sum;
}
//animate function
function play() {
requestAnimationFrame(play);
c.clearRect(0, 0, w, h);
//check surrounding neighbor cells
for(var i = 1; i < cellArray.length - 1; i++) {
for(var j = 1; j < 70 - 1; j++) {
if( cellArray[i][j].alive == true && ( neighborSum(cellArray[i][j], i, j) > 3 || neighborSum(cellArray[i][j], i, j) < 2 ) ) {
cellArray[i][j].alive = false;
}
else if( cellArray[i][j].alive == true && ( neighborSum(cellArray[i][j], i, j) == 3 || neighborSum(cellArray[i][j], i, j) == 2 ) ) {
cellArray[i][j].alive = true;
}
else if(cellArray[i][j].alive == false && neighborSum(cellArray[i][j], i, j) == 3 ) {
cellArray[i][j].alive = true;
}
}
}
//console.log(cellArray)
//redraw cells alive or dead
for(var i = 1; i < cellArray.length - 1; i++) {
for(var j = 1; j < 70 - 1; j++) {
cellArray[i][j].draw();
}
}
}
requestAnimationFrame(play);
Here is a JSFiddle: https://jsfiddle.net/ew3046st/1/
Okay, so I was able to fix the problem by removing the draw() function and instead I just directly call the fillRect() function in the requestAnimationFrame function. Also, fixed the behavior of the cells by adding another 2d array of the same size to store the state of the cells for the next generation:
...
for(var i = 1; i < cellArray.length - 1; i++) {
for(var j = 1; j < 70 - 1; j++) {
//cellArray[i][j].draw();
c.beginPath();
//cellArray[i][j].draw();
if(cellArray[i][j].alive == true) {
c.fillStyle = 'black';
c.fillRect(cellArray[i][j].x, cellArray[i][j].y, 10, 10);
}
else if(cellArray[i][j].alive == false){
c.fillStyle = 'white';
c.fillRect(cellArray[i][j].x, cellArray[i][j].y, 10, 10);
}
c.stroke();
}
}
...
Here is a working jsfiddle: https://jsfiddle.net/e7u19xpq/1/
I want to create a drawing in sugar thing, much the same as you would throw some sugar on a table and using your fingers to "erase" the sugar particles to form an image.
Does anyone know of a js type tool I can use to make this happen?
I suppose I can take a photo of a desk, and a photo of desk with some sugar on it, and then just erase the top layer, but I'm worried that this won't give a real effect.
I'm currently thinking of having a photo of desk, and then using JS to generate a lot of "sugary" particles, which I can then erase. This sounds incredibly hard to do though. Is it? Can someone point me in a good direction?
Sand or is it sugar?
An interesting problem that I had to give a little time.
This works by creating several buffers to hold grains of sand (sugar) and give them life when they need to move.
There is no way that Javascript could do a whole screen of a million plus grains so this demo cheats by only updating a very few and prioritising for new movement rather than allow older moving grains to hog CPU time.
The arrays active, sandStatus, holds the sand gains. active has the pixel address as a 32Bit int and sandStatus has age. The Array sand holds the amount of sand at each pixel and is used to calculate the shadow effect (shadow could be much better using a webGL shader) and to work out which direction sand should slide if disturbed or dropped to the surface.
the var activeMax holds the max number of active sand grains. Increase for a better effect, decrease if the sim runs to slow.
To drop sand use the right mouse button. Hold at one spot to make a pile. Left button pushes the sand about. When you hit a bigger pile the machine may lag (depending on CPU power and browser (best in firefox)).
The push function checks the sand array for any sand. If found it pushes the sand away from the center and piles it up around the edge. Some sand will fall back.
The function sprinkle adds grains of sand (one are a time by pixel coordinate or by index). The function push does the sand drawing FX. update moves the sand grains by checking surrounding pixels heights and moving grains down hill. renderPix handles rendering grains, creating the shadows and deactivating sand grains. The Array shadowChange holds the index of pixels that have had changes so that the shadows can be updated.
Bottom half of the demo is just boilerplate for mouse and canvas setup. All the code in regard to the answer is in the first half.
"use strict";
var activeMax = 2280; // this is the number of sand grains that are processed at
// at time. Increase for better looking effect. decrease
// if the machine is not keeping up with the load
var cw;
var ch;
var w; //
var h;
var canvasBuf = document.createElement("canvas");
var ctxB
var globalTime; // global to this
var pixels
var sand;
var sandToFall;
var sandToFallCount = 36000;
var shadow; // shadow pixels
var activeMax = 2280;
var active; // index of pixel for active grain
var sandStatus; // status of active grain
var shadowChange; // holds index of pixels that have a shadow change
var pixels;
var buf;
var grain = 0xFFFFFFFF;
var shadowGrain = 0x00000000;
var ready = false;
var sandReady = 0;
var nextActive = 0;
var nextActiveShadow = 0;
var onResize = function(){
cw = canvas.width;
ch = canvas.height;
w = cw; //
h = ch;
pixels = w*h;
canvasBuf.width = w;
canvasBuf.height = h;
ctxB = canvasBuf.getContext("2d");
sand = new Uint8ClampedArray(pixels);
shadow = new Uint8ClampedArray(pixels); // shadow pixels
sandToFall = new Uint32Array(sandToFallCount);
activeMax = 2280;
active = new Uint32Array(activeMax); // index of pixel for active grain
sandStatus = new Uint16Array(activeMax); // status of active grain
shadowChange= new Uint32Array(activeMax); // holds index of pixels that have a shadow change
sandStatus.fill(0); // clear
active.fill(0);
shadowChange.fill(0);
ctxB.clearRect(0,0,w,h);
ctxB.fillStyle = "white";
ctxB.font = "84px arial";
ctxB.textAlign = "center";
ctxB.globalAlpha = 0.01;
for(var i = 0; i < 12; i ++){
ctxB.fillText("Sand Doodler!",w/2 + (Math.random()-0.5)*5,h/2 + (Math.random()-0.5)*5);
}
ctxB.globalAlpha = 1;
pixels = ctxB.getImageData(0,0,w,h);
buf = new Uint32Array(pixels.data.buffer);
for(i = 0; i < buf.length; i += 3){
if(buf[i] !== 0){
var c = buf[i] >>> 24;
buf[i] = 0;
while(c > 0){
var ind = Math.floor(Math.random()*sandToFallCount);
if(sandToFall[ind] === 0){
sandToFall[ind] = i;
}
c = c >>> 1;
}
}
}
buf.fill(0);
offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
shadowOffsets = [-w-1,-w,-1];
ready = true;
sandReady=0;
}
function sprinkle(x,y){
var ind;
if(y === undefined){
ind = x;
}else{
ind = x + y*w;
}
var alreadyExists = active.indexOf(ind);
var ac = nextActive;
if(alreadyExists > -1){
sand[ind] += 1;
shadow[ind] = 0;
sandStatus[alreadyExists] = 66;
}else{
active[nextActive] = ind;
sandStatus[nextActive] = 66;
shadowChange[nextActiveShadow];
nextActiveShadow = (nextActiveShadow+1)%activeMax;
nextActive = (nextActive +1)%activeMax;
sand[ind] += 1;
shadow[ind] = 0;
}
return ac;
}
var offsets = [1,w-1,w,w+1,-w-1,-w,-w+1,-1];
var offsetCount = 8;
function update(){
var min,max,minDir,maxDir,dir,start,jj,j,ind,level,i,l1;
for( i = 0; i <activeMax; i ++){
if(sandStatus[i] !== 0){
ind = active[i];
level = sand[ind];
if(level === 1){
sandStatus[i] = 1; // deactive is cant move (level ground)
}else{
min = level;
var d;
minDir = offsets[Math.floor(Math.random()*16)];
dir = null;
start = Math.floor(Math.random()*16); // start at a random direction
for(j=0;j < offsetCount; j++){
jj = offsets[(j + start)%offsetCount];
l1 = sand[ind+jj];
if(l1 < min){
min = l1;
minDir = jj;
d = (j + start)%offsetCount;
}
}
dir = null;
if(min >= level - 1){ // nowhere to move
sandStatus[i] = 1;
}else
if(min < level-1){ // move to lowest
dir = minDir
}
if(dir !== null){
var lv = level-min;
while(lv > 2){
active[i] = ind + dir;
if(sand[ind] > 1){
sand[ind] -= 2;
sprinkle(ind)
}else{
sand[ind] -=1;
}
ind = ind+dir;
sand[active[i]] += 1;
if(sand[active[i] + offsets[d]] >=level){
d+= Math.random()<0.5? 1 : offsetCount -1;
d %=offsetCount;
}
lv -= 1;
}
if(sand[ind]>0){
active[i] = ind + dir;
sand[ind] -= 1;
}
sand[active[i]] += 1;
}
}
}
}
}
var shadowOffsets = [-w-1,-w,-1];
var shadowCols = [0xFFf0f0f0,0xFFd0d0d0,0xFFb0b0b0,0xFF909090];
var shadowDist = [0xf0000000,0xd0000000,0xb0000000,0x90000000]; // shadow col no sand
// renders grains and adds shadows. Deactivates gains when they are done
function renderPix(){
var ac = 0;
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
ac += 1;
var ind = active[i];
buf[ind] = grain;
}
}
for(var i = 0; i < activeMax; i ++){
if(sandStatus[i] !== 0){
var ind = active[i];
var level = sand[ind];
var col =0;
if(sand[ind + shadowOffsets[0]] > level ){
col = 2;
}else
if(sand[ind + shadowOffsets[1]] > level ){
col =1;
}else
if(sand[ind + shadowOffsets[2]] > level ){
col = 1;
}
buf[ind] = grain; // add a sand grain to the image
shadow[ind] = col;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
var c = 4;
while(c > 0){
c-=1;
ind += w + 1;
var s = sand[ind];
var dif = level - s;
if(dif > 0){
c-= dif;
}
shadow[ind] += 1;
shadowChange[nextActiveShadow] = ind;
nextActiveShadow = (nextActiveShadow + 1)%activeMax;
}
sandStatus[i] -= 1;
if(sandStatus[i] === 1){
sandStatus[i] = 0;
active[i] = 0;
}
}
}
// add calculated shadows
for(var i = 0; i < activeMax; i ++){
if(shadowChange[i] !== 0){
var ind = shadowChange[i];
var s = shadow[ind] <4 ? shadow[ind]-1:3;
if(sand[ind] > 0){
buf[ind]=shadowCols[s];
}else{
buf[ind]=shadowDist[s];
}
shadowChange[i] = 0;
}
}
}
// push sand about
function push(x,y,radius){
var iyy,iny
var rr = radius * radius ;
x = Math.floor(x);
y = Math.floor(y);
for(var iy = -radius + 1; iy < radius; iy ++){
iyy = iy * iy;
iny = (y+iy) * w;
for(var ix = -radius + 1; ix < radius; ix ++){
if(ix*ix + iyy <= rr){ // is inside radius
var ind = (x + ix) + iny;
if(sand[ind] > 0){
var dir = Math.random() * Math.PI * 2;
dir = Math.atan2(iy,ix)
var r = radius + Math.random() * radius *0.2
var xx = Math.cos(dir) * r;
var yy = Math.sin(dir) * r;
buf[ind] = 0x000000;
sand[ind] = 0;
ind = Math.floor(xx + x) + Math.floor(yy + y) * w;
sprinkle(ind);
}else{
buf[ind] = 0;
}
}
}
}
}
function showHeight(){ // for debugging only
for(var i = 0; i < sand.length; i ++){
buf[i] = 0xff000000;
var k = sand[i];
buf[i] +=(k <<16) + (k<<8) + (k);
}
}
// main update function
function display(){
if(!ready){ // only when ready
return;
}
//ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.clearRect(0,0,cw,ch);
var mx = Math.floor((mouse.x/cw)*w); // canvas buf mouse pos
var my = Math.floor((mouse.y/ch)*h);
// drop sand
if(mouse.buttonRaw & 4){
for(var i = 0; i < 120; i ++){
var dir = Math.random()*Math.PI;
var dist = ((Math.random()+Math.random()+Math.random())/3-0.5) * 62;
var x = Math.cos(dir) * dist;
var y = Math.sin(dir) * dist;
x += mx;
y += my;
x = Math.floor(x); // floor
y = Math.floor(y); // floor
sprinkle(x,y);
}
}else{
// drop sand for intro FX
if(sandReady <sandToFallCount){
for(var i = 0; i < 120; i ++){
if(sandToFall[sandReady] !== 0){
sprinkle(sandToFall[sandReady] + offsets[Math.floor(Math.random()*8)%offsets.length]);
}
sandReady += 1;
}
}
}
// push sand about.
if(mouse.buttonRaw & 1){
push(((mouse.x/cw)*w),((mouse.y/ch)*h),32); // scale mouse to canvasBuf size
}
update();
renderPix();
//showHeight();
ctxB.putImageData(pixels,0,0);
ctx.drawImage(canvasBuf,0,0,cw,ch);
}
//==================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// This code is not intended to be part of the answer unless specified and has been formated to reduce
// display size. It should not be used as an example of how to write a canvas interface.
// By Blindman67
const U = undefined;const RESIZE_DEBOUNCE_TIME = 100;
var canvas,ctx,mouse,createCanvas,resizeCanvas,setGlobals,globalTime=0,resizeCount = 0;
createCanvas = function () { var c,cs; cs = (c = document.createElement("canvas")).style; cs.position = "absolute"; cs.top = cs.left = "0px"; cs.zIndex = 1000; document.body.appendChild(c); return c;}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); } canvas.width = window.innerWidth; canvas.height = window.innerHeight; ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); } if (typeof onResize === "function"){ resizeCount += 1; setTimeout(debounceResize,RESIZE_DEBOUNCE_TIME);}
}
function debounceResize(){ resizeCount -= 1; if(resizeCount <= 0){ onResize();}}
setGlobals = function(){ cw = w = canvas.width; ch = h = canvas.height; mouse.updateBounds(); }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, over : false, bm : [1, 2, 4, 6, 5, 3],
active : false,bounds : null, crashRecover : null, mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.clientX - m.bounds.left; m.y = e.clientY - m.bounds.top;
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
if((m.buttonRaw & 2) && m.crashRecover !== null){ if(typeof m.crashRecover === "function"){ setTimeout(m.crashRecover,0);}}
e.preventDefault();
}
m.updateBounds = function(){
if(m.active){
m.bounds = m.element.getBoundingClientRect();
}
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
m.active = true;
m.updateBounds();
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
m.active = false;
}
}
return mouse;
})();
function main(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(main);
}
resizeCanvas();
mouse.start(canvas,true);
window.addEventListener("resize",resizeCanvas);
requestAnimationFrame(main);
body {
background:#49D;
}
.help {
text-align : center;
font-family : Arial,"Helvetica Neue",Helvetica,sans-serif;
font-size : 18px;
}
<div class="help">Right mouse to drop sand, left button to push it around.</div>
I've been trying to work out this simple javascript tic tac toe game. I'm trying to make the board size a variable and number needed in a row a variable. I've created a global variable for board size and number in a row. However it doesn't seem to be working. It makes the board the size I want but only a few of the squares will actually result in a 'Win'. Could someone please help me out ;).
Here is an image of one of the issues: http://imgur.com/oNK9ErK
I imagine the error occurs here:
function checkWinner(mArr) {
var winner = [false, ""];
for (var i = 0; i < mArr.length; i++) {
var hor = [],
ver = [],
diag = [];
if (mArr[i][3] !== "") {
//horizontal
if (i % 3 === 0) {
for (var j = 0; j < 3; j++) {
hor.push([mArr[i + j][3], i + j]);
}
if (hor.length === numinrow) {
winner = isWinner(hor);
if (winner[0]) {
return winner;
}
}
}
//vertical && diag/anti diag
if (i < 3) {
for (var j = 0; j + i < mArr.length; j += 3) {
ver.push([mArr[i + j][3], i + j]);
}
if (ver.length === numinrow) {
winner = isWinner(ver);
if (winner[0]) {
return winner;
}
}
if (i !== 1) {
for (var z = 0; z + i < mArr.length - i; z += (4 - i)) {
diag.push([mArr[i + z][3], i + z]);
}
if (diag.length === numinrow) {
winner = isWinner(diag);
if (winner[0]) {
return winner;
}
}
}
}
}
}
return winner;
}
Here is the entire .js
(function () {
var bsize = 5;
var numinrow = 3;
function Board(id, c, r) {
if (this instanceof Board) {
this.CANVAS = document.getElementById(id);
this.CTX = this.CANVAS.getContext("2d");
this.WIDTH = this.CANVAS.width || 0;
this.HEIGHT = this.CANVAS.height || 0;
this.COLS = c || bsize;
this.ROWS = r || bsize;
this.TILEWIDTH = (this.WIDTH / this.COLS);
this.moveCount = 0;
this.board = this.gameBoard(this.TILEWIDTH, this.COLS, this.ROWS);
this.CANVAS.addEventListener('selectstart', function (e) {
e.preventDefault();
return false;
}, false);
this.winner = [false, ""];
this.boardDisabled = false;
} else {
return new Board(id, c, r);
}
}
Board.prototype.draw = function () {
var ctx = this.CTX;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = "#168dd9";
// Draw column dividers
for (var i = 1; i <= this.COLS - 1; i++) {
ctx.moveTo(this.TILEWIDTH * i, 0);
ctx.lineTo(this.TILEWIDTH * i, this.HEIGHT);
}
//Draw horizontal dividers
for (var i = 1; i <= this.ROWS - 1; i++) {
ctx.moveTo(0, this.TILEWIDTH * i);
ctx.lineTo(this.WIDTH, this.TILEWIDTH * i);
}
ctx.stroke();
};
Board.prototype.gameBoard = function (t, c, r) {
var b = [],
count = 0;
// Create gameboard array with the following data:
// [x pos, y pos, tile count, empty string for move symbol (x or o)]
for (var y = 0; y < r; y++) {
for (var x = 0; x < c; x++) {
b.push([x * t, y * t, count++, ""]);
}
}
return b;
};
Board.prototype.updateScore = function () {
if (supports_html5_storage()) {
var p = sessionStorage.score || {
"score_x": 0,
"score_o": 0,
"score_tie": 0
},
w = "score_" + (this.winner[1][0] || "tie");
if (sessionStorage.score) {
p = JSON.parse(p);
}
p[w] ++;
sessionStorage.score = JSON.stringify(p);
this.updateScoreBoard();
}
};
Board.prototype.updateScoreBoard = function () {
if (supports_html5_storage()) {
var p = sessionStorage.score ? JSON.parse(sessionStorage.score) : {
"score_x": 0,
"score_o": 0,
"score_tie": 0
};
for (var s in p) {
if (p.hasOwnProperty(s)) {
document.getElementById(s).innerHTML = p[s];
}
}
}
};
Board.prototype.reset = function (x) {
var timer = x || 4000;
window.setTimeout(function () {
window.location.reload(false);
}, timer);
};
Board.prototype.resetScore = function () {
if (supports_html5_storage()) {
sessionStorage.removeItem("score");
this.updateScoreBoard();
}
};
Board.prototype.move = function (coor) {
var width = this.TILEWIDTH,
ctx = this.CTX,
board = this.board,
blen = board.length;
//Loop through and find tile that click was detected on
for (var i = 0; i < blen; i++) {
if (coor.x > board[i][0] && coor.y > board[i][1] && coor.x < board[i][0] + width && coor.y < board[i][1] + width) {
var x = board[i][0],
y = board[i][1],
validTile = board[i][3] === "";
if (validTile) {
if (this.moveCount++ % 2 === 1) {
moveO(x, y, width, ctx);
board[i][3] = "o";
} else {
moveX(x, y, width, ctx);
board[i][3] = "x";
}
}
//Check board for winner if move count is 5 or more
if (this.moveCount > 4) {
this.winner = checkWinner(board);
var w = this.winner,
winner = w[0],
shape = w[1][0],
boardDisabled = this.boardDisabled;
//If there is a winner, redraw winning tiles in red
if (winner && !boardDisabled) {
if (shape === "o") {
for (var j = 1; j < 4; j++) {
moveO(board[w[j][1]][0], board[w[j][1]]
[1], width, ctx, "red", 5);
}
} else {
for (var j = 1; j < 4; j++) {
moveX(board[w[j][1]][0], board[w[j][1]]
[1], width, ctx, "red", 5);
}
}
}
if ((winner || this.moveCount === board.length) && !boardDisabled) {
if (!winner) {
//If tie, redraw all moves in red
for (var j = 0; j < board.length; j++) {
if (board[j][3] === "o") {
moveO(board[j][0], board[j][1], width, ctx, "red", 5);
} else {
moveX(board[j][0], board[j][1], width, ctx, "red", 5);
}
}
}
this.boardDisabled = true;
this.updateScore();
this.reset();
}
}
break;
}
}
};
function checkWinner(mArr) {
var winner = [false, ""];
for (var i = 0; i < mArr.length; i++) {
var hor = [],
ver = [],
diag = [];
if (mArr[i][3] !== "") {
//horizontal
if (i % 3 === 0) {
for (var j = 0; j < 3; j++) {
hor.push([mArr[i + j][3], i + j]);
}
if (hor.length === numinrow) {
winner = isWinner(hor);
if (winner[0]) {
return winner;
}
}
}
//vertical && diag/anti diag
if (i < 3) {
for (var j = 0; j + i < mArr.length; j += 3) {
ver.push([mArr[i + j][3], i + j]);
}
if (ver.length === numinrow) {
winner = isWinner(ver);
if (winner[0]) {
return winner;
}
}
if (i !== 1) {
for (var z = 0; z + i < mArr.length - i; z += (4 - i)) {
diag.push([mArr[i + z][3], i + z]);
}
if (diag.length === numinrow) {
winner = isWinner(diag);
if (winner[0]) {
return winner;
}
}
}
}
}
}
return winner;
}
function isWinner(arr) {
arr.sort();
var w = arr[0][0] && arr[0][0] === arr[arr.length - 1][0] ? [true].concat(arr) : [false, ""];
return w;
}
function moveO(x, y, r, ctx, fill, lineW) {
var x = x + r / 2,
y = y + r / 2,
r = r / 2 - (r * 0.15);
ctx.beginPath();
ctx.lineWidth = lineW || 3;
ctx.strokeStyle = fill || "#333";
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.stroke();
}
function moveX(x, y, w, ctx, fill, lineW) {
var pad = w * 0.15,
lineCoor = [
[
[x + pad, y + pad], //line 1 start
[x + w - pad, y + w - pad] //line 1 end
],
[
[x + pad, y + w - pad], //line 2 start
[x + w - pad, y + pad] //line 2 end
]
];
ctx.beginPath();
ctx.lineWidth = lineW || 3;
ctx.strokeStyle = fill || "#333";
for (var i = 0; i < 2; i++) {
ctx.moveTo(lineCoor[i][0][0], lineCoor[i][0][1]);
ctx.lineTo(lineCoor[i][1][0], lineCoor[i][1][1]);
}
ctx.stroke();
}
function clickTouch(e) {
var coor = b.CANVAS.relMouseCoords(e);
if (!b.winner[0]) {
b.move(coor);
}
}
function clickTouchReset(e) {
var target = e.target.id;
if (target === "resetScore" && confirm("Are you sure you want to reset the score?")) {
b.resetScore();
} else if (target === "resetGame") {
b.reset(1);
}
}
// Initialize Game
//BOARD SIZE
var b = new Board("game", bsize, bsize),
resetcon = document.getElementById("reset");
b.draw();
b.updateScoreBoard();
//Add event listeners for click or touch
window.addEventListener("click", clickTouch, false);
window.addEventListener("touchstart", clickTouch, false);
resetcon.addEventListener("click", clickTouchReset, false);
resetcon.addEventListener("touchstart", clickTouchReset, false);
})();
/*****
Get Mouse click coordinates within canvas
Modified to include touch events
Source: http://stackoverflow.com/a/9961416
******/
HTMLCanvasElement.prototype.relMouseCoords = function (event) {
var totalOffsetX = 0,
totalOffsetY = 0,
canvasX = 0,
canvasY = 0,
touch = event.touches,
currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft;
totalOffsetY += currentElement.offsetTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = (touch ? touch[0].pageX : event.pageX) - totalOffsetX;
canvasY = (touch ? touch[0].pageY : event.pageY) - totalOffsetY;
canvasX = Math.round(canvasX * (this.width / this.offsetWidth));
canvasY = Math.round(canvasY * (this.height / this.offsetHeight));
return {
x: canvasX,
y: canvasY
}
}
function supports_html5_storage() {
try {
return 'sessionStorage' in window && window.sessionStorage !== null;
} catch (e) {
return false;
}
}
I have made a snake game with processign.js and im trying to make the collision with itself. The problem is that it isnt working as it should.
var screen = 0;
var bg = color(60,150,60);
var snake;
var apple;
var bonus;
var nBonus = 0;
var gameOver = false;
var appleSize = 10;
var applePosX = round(random(10,width-appleSize)/10)*10;
var applePosY = round(random(10,height-appleSize)/10)*10;
var keys = [];
void keyPressed() {
keys[keyCode] = true;
};
void keyReleased() {
keys[keyCode] = false;
};
frameRate(10);
// collision with itself
// -----------------------------------------------------------------------
// ------------------------------- THE SNAKE -----------------------------
var Snake = function(x, y) {
this.x = x;
this.y = y;
this.len = 1;
this.size = 10;
this.snakePosX = 0;
this.snakePosY = 0;
this.points = 0;
this.positions = [];
this.moving = false;
this.apples = 0;
for(var i=0; i<this.len; i++) {
var posX = this.x-i*10;
var posY = this.y;
this.positions[i] = {x:posX, y:posY};
}
};
Snake.prototype.draw = function() {
fill(0);
stroke(255,255,255);
for(var i=0; i<this.positions.length; i++) {
rect(this.positions[i].x, this.positions[i].y, this.size, this.size);
}
};
Snake.prototype.move = function() {
if(gameOver === false) {
if(keys[UP]) {
this.snakePosY = -this.size;
this.snakePosX = 0;
this.moving = true;
}
if(keys[DOWN]) {
this.snakePosY = this.size;
this.snakePosX = 0;
this.moving = true;
}
if(keys[LEFT]) {
this.snakePosX = -this.size;
this.snakePosY = 0;
this.moving = true;
}
if(keys[RIGHT]) {
this.snakePosX = this.size;
this.snakePosY = 0;
this.moving = true;
}
}
if(this.moving == true) {
if(snake.positions.length == 1) {
this.positions[0].x += this.snakePosX;
this.positions[0].y += this.snakePosY;
}
else {
for(var i=1; i<this.positions.length; i++) {
this.positions[i-1].x = this.positions[i].x;
this.positions[i-1].y = this.positions[i].y;
this.positions[i].x += this.snakePosX;
this.positions[i].y += this.snakePosY;
}
}
}
};
Snake.prototype.checkColl = function() {
// collision with itself
if(this.positions.length>1) {
for(var i=0; i<this.positions.length; i++) {
if(this.positions[0].x > 350) {
text('holly crap', 100, 100);
}
}
}
// collision with walls
if(this.positions[0].x > width-this.size || this.positions[0].x < 0 || this.positions[0].y > height-this.size || this.positions[0].y < 0) {
gameOver = true;
gameIsOver();
}
// collision with apples
for(var i=0; i<this.positions.length; i++) {
if(this.positions[i].x >= apple.x && this.positions[i].x+10 <= apple.x+10 && this.positions[i].y >= apple.y && this.positions[i].y+10 <= apple.y+10) {
apple.draw();
this.apples ++;
this.points += 10;
this.positions.unshift({x:apple.x, y:apple.y});
apple.x = round(random(10,width-appleSize)/10)*10;
apple.y = round(random(10,height-appleSize)/10)*10;
if(this.apples > 1 && this.apples % 5 == 0) {
nBonus = 1;
}
}
}
// collision with bonus
if(this.positions[0].x >= bonus.x && this.positions[0].x+10 <= bonus.x+10 && this.positions[0].y >= bonus.y && this.positions[0].y+10 <= bonus.y+10) {
if(this.moving) {
bonus.x = round(random(10,width-appleSize)/10)*10;
bonus.y = round(random(10,height-appleSize)/10)*10;
nBonus = 0;
this.points += 10;
}
}
};
// ------------------------ THE APPLES -----------------------------------
var Apple = function(x, y) {
this.x = x;
this.y = y;
};
Apple.prototype.draw = function() {
fill(255,0,0);
noStroke();
rect(this.x, this.y, appleSize, appleSize);
};
// ------------------------ THE BONUS -----------------------------------
var Bonus = function(x, y) {
this.x = x;
this.y = y;
}
Bonus.prototype.draw = function() {
fill(150,0,0);
stroke(255,255,255)
rect(this.x, this.y, appleSize, appleSize);
};
// -----------------------------------------------------------------------
snake = new Snake(width/2, height/2);
apple = new Apple(applePosX, applePosY);
bonus = new Bonus(width/2, height/2);
// -----------------------------------------------------------------------
void gameIsOver() {
fill(0);
textAlign(CENTER);
text("Game Over\nPress 'S' to play again", width/2, height/2);
textAlign(LEFT);
if(keys[83]) {
screen = 2;
gameOver = false;
}
}
void playGame() {
if(screen === 0) {
if(mouseX >= width/2-50 && mouseY <= width/2+50 && mouseY >= height/2-15 && mouseY <= height/2+15) {
if(mousePressed) {
screen = 1;
}
}
}
}
void makeScreen() {
if(screen === 0) {
textAlign(CENTER);
textSize(30);
text('SNAKE GAME', width/2, 100);
stroke(255,255,255);
noFill();
rectMode(CENTER);
rect(width/2, height/2, 100, 30);
textSize(15);
text('Play', width/2, height/2+5);
textSize(11);
text('By Eskimopest', width/2, height-20);
textAlign(LEFT);
rectMode(LEFT);
playGame();
}
if(screen === 1) {
snake.draw();
snake.move();
snake.checkColl();
apple.draw();
if(nBonus === 1) {
bonus.draw();
}
fill(0);
text('POINTS : '+ snake.points, 10, 20);
text('APPLES : '+ snake.apples, 10, 40);
}
if(screen === 2) {
snake.points = 0;
snake.apples = 0;
snake.x = width/2;
snake.y = height/2;
snake.len = 1;
snake.positions = [{x:snake.x, y:snake.y}];
snake.snakePosX = 0;
snake.snakePosY = 0;
applePosX = round(random(10,width-appleSize)/10)*10;
applePosY = round(random(10,height-appleSize)/10)*10;
screen = 1;
}
}
// -----------------------------------------------------------------------
void draw() {
background(bg);
makeScreen();
for(var i=0; i<snake.positions.length; i++) {
text(i + 'x:'+snake.positions[i].x + ' y:'+snake.positions[i].y, 600, 20+i*10);
}
}
The problem is in snake.prototype.checkColl and I'm trying to make this work but with no results. When I try to make
if(this.positions[0].x = this.positions[i].x)
nothing happens. If anyone could help me I would be very appreciated.
That should be:
if(this.positions[0].x == this.positions[i].x)
Using a single = is doing an assignment. You want to do a comparison, so you need double ==.