Javascript sequential functions that return promises - javascript

I need to run functions sequentially in order to animate in the proper order, but I am running into some issues. The code basically checks some conditions and stores values in arrays (not shown). These values are passed as parameters to certain functions, which need to run sequentially. I am using promises to achieve this
This code stores the needed functions (and their parameters) in an array func_call
var func_call = [];
for (var i = 0; i < s_list.length; i++){
//lower
if (!isUpper) {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
//upper
} else {
canvas = document.getElementById("myCanvasUpr");
context = canvas.getContext("2d");
}
func_call.push(function () {get_path(context, s_list[i], d_list[i], final_arr);});
func_call.push(function () {draw_marker(context, s_list[i], d_list[i], isFirst[i], isLast[i]);});
}
func_call.push(function() {wrap(final_arr)});
func_call.reduce((cur, next) => cur.then(next()), Promise.resolve());
get_path is here
function get_path(context, source, desti, arr){
return new Promise(function(resolve) {
var starting_points = string_to_point[source];
var ending_points = string_to_point[desti];
console.log("get path");
console.log(starting_points);
/*
* Define the variables
* i -> iterator count
* seg_1 -> points of starting exhibit and closest point on the path
* seg_2 -> points of ending exhibit and closest point on path
* main_path -> the points along the main path
* reverse_flag -> denotes whether the direction is forward or reverse
*/
var i;
var seg_1 = [];
var seg_2 = [];
var main_path = [];
var secondary_path = [];
var reverse_flag = false;
var secondary_flag = false;
var primary_flag = false;
var starting_point = starting_points[0];
var ending_point = ending_points[0];
var vertices = [];
var pre = [];
var secondary_vertices = [];
var points = [];
var secondary_points = [];
/*
* Add the first two segments
*/
if(starting_points.length == 3){
for(var j = starting_points[0]; j <= starting_points[1]; j++){
seg_1.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
starting_point = starting_points[2];
}
if(ending_points.length == 3){
for(var j = ending_points[0]; j <= ending_points[1]; j++){
seg_2.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
ending_point = ending_points[2];
}
if(starting_point == 260 && ending_point == 260){
ending_point = -1;
} else if (starting_point == 260){
for(var j = 260; j <= 267; j++){
seg_1.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
starting_point = 72;
} else if (ending_point == 260){
for(var j = 260; j <= 267; j++){
seg_2.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
ending_point = 72;
}
/*
* Handles reverse direction. Switches starting and end points and sets the reverse_flag
*/
if (ending_point != -1 && starting_point > ending_point){
temp = starting_point;
starting_point = ending_point;
ending_point = temp;
reverse_flag = true;
}
/*
* Add points to main_path
*/
for (i = starting_point; i <= ending_point; i++){
var b = point_coord[i];
/*if(i >= 122){
secondary_flag = true;
secondary_path.push({x:b[0],y:b[1]});
} else {
primary_flag = true;
main_path.push({x:b[0],y:b[1]});
}*/
primary_flag = true;
main_path.push({x:b[0] , y:b[1]});
}
/*
* Creates the full path -> combination of seg_1, seg_2, and main_path
*/
if(reverse_flag){
if(primary_flag){
if(secondary_flag){
vertices = seg_1.concat(secondary_path.reverse());
secondary_vertices = main_path.reverse().concat(seg_2);
context.beginPath()
context.arc(pathways[121][0], pathways[121][1], 8, 0, 2 * Math.PI);
context.fillStyle = 'green';
context.fill();
context.beginPath();
context.arc(pathways[122][0], pathways[122][1], 6, 0, 2 * Math.PI);
context.fillStyle = 'red';
context.fill();
} else {
vertices = seg_1.concat(main_path.reverse(),seg_2.reverse());
}
} else {
if(secondary_flag){
vertices = seg_1.concat(secondary_path.reverse(),seg_2);
} else {
}
}
} else {
if(primary_flag){
if(secondary_flag){
vertices = seg_1.concat(main_path);
secondary_vertices = secondary_path.concat(seg_2);
context.beginPath();
context.arc(pathways[122][0], pathways[122][1], 8, 0, 2 * Math.PI);
context.fillStyle = 'green';
context.fill();
context.beginPath()
context.arc(pathways[121][0], pathways[121][1], 6, 0, 2 * Math.PI);
context.fillStyle = 'red';
context.fill();
} else {
vertices = seg_1.concat(main_path,seg_2.reverse());
}
} else {
if(secondary_flag){
vertices = seg_1.concat(secondary_path,seg_2);
} else {
vertices = seg_1.concat(seg_2);
}
}
}
/*
* Calculate the extra points for animation, and draw the animation
*/
if(secondary_vertices.length == 0){
points = calcWaypoints(vertices);
pre.push(points);
} else {
points = calcWaypoints(vertices);
secondary_points = calcWaypoints(secondary_vertices);
pre.push(points, secondary_points);
}
arr.push([context,pre]);
console.log(arr);
resolve();
});
}
draw_marker is here
function draw_marker(context, source, desti, isFirst, isLast) {
return new Promise(function(resolve) {
/*
* Get the point number of the point on the path that the source and destination connect to
*/
var start = string_to_point[source];
var finish = string_to_point[desti];
/*
* Marker
*/
if (isFirst) {
var marker1 = new Image();
marker1.onload = function(){
marker1._x = point_coord[start[0]][0]-1;
marker1._y = point_coord[start[0]][1]-44;
context.drawImage(marker1, marker1._x, marker1._y,marker1.width,marker1.height);
};
marker1.src = "images/map_pin.png";
} else {
context.fillStyle = 'green';
context.beginPath();
context.arc(point_coord[start[0]][0], point_coord[start[0]][1], 8, 0, 2 * Math.PI);
context.strokeStyle = "green";
context.stroke();
context.fill();
}
if (isLast) {
/*var marker2 = new Image();
marker2.onload = function(){
marker2._x = point_coord[finish[0]][0]-15;
marker2._y = point_coord[finish[0]][1]-22;
context.drawImage(marker2, marker2._x, marker2._y,marker2.width,marker2.height);
};
marker2.src = "images/x_marks.png";*/
} else {
context.fillStyle = 'red';
context.beginPath();
context.arc(point_coord[finish[0]][0], point_coord[finish[0]][1], 6, 0, 2 * Math.PI);
context.strokeStyle = "#ff0000";
context.stroke();
context.fill();
}
resolve();
});
}
wrap is here
function wrap(arr){
console.log("in wrap");
var getAnimation = function(context, lines){
console.log("Get animation");
console.log(lines);
return new Promise(function(resolve) {
context.beginPath();
lines.reduce((a, c) => a.then(() => animate(context,c)), Promise.resolve());
resolve();
});
};
arr.reduce((a,c) => a.then(() => getAnimation(c[0],c[1])),Promise.resolve());
}
animate is here
var animate = function(context, p){
return new Promise(function(resolve) {
console.log("in animate");
var t = 1;
context.lineCap = "round";
context.lineWidth = 5;
//context.strokeStyle = "#ff0000";
context.strokeStyle = "#ff3c3c";
//context.strokeStyle = "#f38f1d";
var runAnimation = function(){
if(t<p.length){
console.log("running animation");
context.beginPath();
context.moveTo(p[t-1].x,p[t-1].y);
context.lineTo(p[t].x,p[t].y);
context.stroke();
t++;
requestAnimationFrame(function(){runAnimation()});
} else {
console.log("run animation resolved");
resolve()
}
};
runAnimation();
});
}
The goal is to run however many combos of get_path and draw_markers as needed, then run wrap, which in turn calls animate. Each animate needs to finish before the next one starts as well
How do I accomplish this?
Thank you. If any more details are needed, please let me know

You could use async/await if the target JavaScript environment supports it. If not you could implement a compiler like Babel and use it that way.
If you don't want to go that route there is Bluebird, a Promise library that allows you to call Promise.map with a { concurrency: 1 } option. This will call the promises in sequential order.

Related

Delay between each recursive call to function

I am trying to build a maze generator for a personal project. I have a recursive depth-first search function that recursively goes through each cell in the grid, checks if it has unvisited neighbors, then calls the recursive function again with the next neighbor. It is able to generate the maze just fine but I want to add a delay between each call to the recursive function so I can animate the creation of the maze as it visits each cell. Using the chrome debugger, it seems to do the 1s delay for the first iteration and then it stops waiting and jumps from the await delay back to the beginning of the function over and over without ever moving on. What am I doing wrong?
Here is the recursive function and delay function:
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
});
}
and here is the full javascript code:
"use strict"
// declare globals
const numCols = 10;
const numRows = 10;
const cellSize = 50;
var grid = [];
// create canvas
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
var body = document.getElementsByTagName("body")[0];
body.appendChild(canvas);
var context = canvas.getContext('2d');
function setup() {
createGrid();
const start = grid[0][0]; // start at top left cell
const end = grid[1][1];
recursiveDFS(start);
}
class Cell {
constructor(col, row) {
this.col = col;
this.row = row;
this.neighbors = {};
this.walls = {
top: true,
right: true,
bottom: true,
left: true
};
this.visited = false;
}
setNeighbors() {
//top
if(this.row - 1 >= 0) {
this.neighbors.top = grid[this.col][this.row - 1];
}
//right
if (this.col + 1 < numCols) {
this.neighbors.right = grid[this.col + 1][this.row];
}
//bottom
if (this.row + 1 < numRows) {
this.neighbors.bottom = grid[this.col][this.row + 1];
}
//left
if (this.col - 1 >= 0) {
this.neighbors.left = grid[this.col - 1][this.row];
}
}
}
// create 2d array of Cell objects
// indexing as grid[col][row]
// grid = [[(0,0), (1,0)],
// [(0,1), (1,1)]]
function createGrid() {
for (var col = 0; col < numCols; col++) {
var colArr = []
for (var row = 0; row < numRows; row++) {
var cell = new Cell(col, row);
colArr.push(cell);
drawGridLines(cell);
}
grid.push(colArr);
}
for (var row = 0; row < numRows; row++) {
for (var col = 0; col < numCols; col++) {
grid[col][row].setNeighbors();
}
}
}
// return single neighbor randomized from all possible neighbors
function getNextNeighbor(cell) {
if (cell.neighbors) {
var neighbors = [];
for (var neighbor in cell.neighbors) {
if (cell.neighbors[neighbor].visited === false){
neighbors.push([cell.neighbors[neighbor], neighbor]);
}
}
}
if(neighbors.length > 0) {
return neighbors[Math.floor(Math.random() * neighbors.length)];
} else {
return [undefined, undefined];
}
}
function delay(ms) {
return new Promise(resolve => {
console.log("waiting...");
setTimeout(resolve, ms)
});
}
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function highlightCell(cell) {
context.globalCompositeOperation='destination-over'; // fill rect under existing grid
const topLeft = [(cell.col) * cellSize, (cell.row) * cellSize];
context.fillStyle = '#FF0000';
context.fillRect(topLeft[0], topLeft[1], cellSize, cellSize);
}
function removeWall(cell1, cell2, direction) {
switch (direction) {
case 'top':
cell1.walls.top = false;
cell2.walls.bottom = false;
break;
case 'right':
cell1.walls.right = false;
cell2.walls.left = false;
break;
case 'bottom':
cell1.walls.bottom = false;
cell2.walls.top = false;
break;
case 'left':
cell1.walls.left = false;
cell2.walls.right = false;
break;
}
redrawGrid();
}
function redrawGrid() {
context.clearRect(0, 0, numCols * cellSize, numRows * cellSize); // clear canvas
for (var col = 0; col < numCols; col++) {
for (var row = 0; row < numRows; row++) {
drawGridLines(grid[col][row]);
}
}
}
function drawGridLines(cell) {
const topLeft = [ cell.col * cellSize, cell.row * cellSize];
const topRight = [(cell.col + 1) * cellSize, cell.row * cellSize];
const bottomLeft = [ cell.col * cellSize, (cell.row + 1) * cellSize];
const bottomRight = [(cell.col + 1) * cellSize, (cell.row + 1) * cellSize];
context.lineWidth = 2;
//draw top line
if(cell.walls.top){
context.beginPath();
context.moveTo(topLeft[0], topLeft[1]);
context.lineTo(topRight[0], topRight[1]);
context.stroke();
}
//draw right line
if(cell.walls.right) {
context.beginPath();
context.moveTo(topRight[0], topRight[1]);
context.lineTo(bottomRight[0], bottomRight[1]);
context.stroke();
}
//draw bottom line
if(cell.walls.bottom) {
context.beginPath();
context.moveTo(bottomRight[0], bottomRight[1]);
context.lineTo(bottomLeft[0], bottomLeft[1]);
context.stroke();
}
//draw left line
if(cell.walls.left) {
context.beginPath();
context.moveTo(bottomLeft[0], bottomLeft[1]);
context.lineTo(topLeft[0], topLeft[1]);
context.stroke();
}
}
setup();
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
await recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
Add await when u call recursiveDFS(next); so that it will wait for the function to be done before going to the next step as you have set the function as async.

How to force lines to render behind other things in HTML5 Canvas

I have been trying to make a nodal graph visualizer, but I am having trouble getting the lines to display behind the nodal points. I have already tried to use context.globalCompositeOperation, but to no avail. I have tried to implement rendering it in the order I wanted it to be drawn. Here is the full code for the project:
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
var nodes = [];
var has_clicked = false;
var user_has_picked_up_object = false;
var picked_up_id;
class Vector2 {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
document.onmousemove = function(e){
mouse_pos.x = e.pageX;
mouse_pos.y = e.pageY;
}
document.onmousedown = function(e) {
has_clicked = true;
}
document.addEventListener('mouseup', function(e) {
has_clicked = false;
user_has_picked_up_object = false;
})
var mouse_pos = new Vector2(0, 0);
class Connection {
constructor(node1, node2) {
this.node1 = node1;
this.node2 = node2;
this.isDrawn = false;
}
}
function DistSQ(p1, p2) {
return Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2);
}
class Node {
constructor(pos, id) {
this.pos = pos;
this.radius = 10;
this.radius_squared = 100;
this.connections = [];
this.id = id;
}
AddConnection(conn) {
this.connections.push(conn);
}
RemoveConnection(conn) {
return this.connections.pop(conn);
}
UpdatePosition() {
if(DistSQ(this.pos, mouse_pos) < this.radius_squared && has_clicked) {
if(user_has_picked_up_object && picked_up_id == this.id) {
this.pos = mouse_pos;
}
else {
user_has_picked_up_object = true;
picked_up_id = this.id;
}
}
else {
this.pos = new Vector2(this.pos.x, this.pos.y);
}
}
}
function DrawLines(conns) {
c.beginPath();
c.lineWidth = 1;
c.strokeStyle = 'black';
conns.forEach(element => {
c.moveTo(element.node1.pos.x, element.node1.pos.y);
c.lineTo(element.node2.pos.x, element.node2.pos.y);
});
c.stroke();
}
function DrawCircles(nds) {
c.beginPath();
c.lineWidth = 1;
nds.forEach(element => {
c.strokeStyle = 'black';
c.moveTo(element.pos.x+element.radius, element.pos.y);
c.arc(element.pos.x, element.pos.y, element.radius, 0, 2 * Math.PI, false);
});
c.stroke();
}
function Update() {
requestAnimationFrame(Update);
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
for(var i = 0; i < nodes.length; i++) {
nodes[i].UpdatePosition();
DrawLines(nodes[i].connections);
}
DrawCircles(nodes);
}
function Initialize() {
for(var y = 0, i = 0; y < 5; y++) {
for(var x = 0; x < 5; x++, i++) {
nodes.push(new Node(new Vector2(x*20+20, y*20+20), i));
}
}
for(var i = 1; i < nodes.length; i++) {
nodes[i].AddConnection(new Connection(nodes[i], nodes[i-1]));
}
Update();
}
Initialize();
Thanks to #enxaneta, for giving me the solution to this problem, so I copied his answer here:
The lines are behind the circles- In your function DrawCircles add c.fillStyle = "white";c.fill() – enxaneta

Why is this neural network not rendering (only) full 3-layer pathways?

The function in question is rendering the neural network represented on the right side of this image based on a fairly simple data structure.
Trying to evolve:
After evolving:
Each dot represents a neuron, each line a connection. There's a problem with which neurons and connections are being rendered and which aren't, and I've been wrestling with this problem for 5 hours straight, no coffee breaks. One of you is likely going to point out one tiny stupid mistake causing the issue and I'll likely proceed to pull my hair out.
Put simply: Connections run from the top down. I only want to render complete paths from top to bottom. No dots (neurons) in the middle should be rendered if no connections lead to them from the top. If a top layer neuron connects to a middle layer neuron but that middle layer neuron doesn't connect to a bottom layer neuron, that connection isn't doing anything so it shouldn't be rendered.
As you can see, there are top level neurons that are rendered with no connections at all, and middle level neurons which are rendered with no connection to the top. All connections are top-down, no connections flow upward. The network is feed-forward in other words.
The data structure passed to this function is brain which is the same brain passed to the following Neuron and Connection constructors which are listed in abridged form only showing the properties relevant to the function in question (edited from the original with further attempts to fix the problem:
Neuron
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function(target) {
if (that.active == true) {
new Connection(brain, this, target, function(id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
Connection
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this; //connected references
//incoming connections to a neuron
callback(this.id, this);
}
}
As you can see, brain.globalReferenceNeurons contains the the data needed to render the neural network in the picture.
And here's the rendering function in question (updated again):
function renderBrain(brain, context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDivision = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
function renderLayer(layer, layerCount, layerTotal) {
var length = layer.length;
var widthDivision = width / length;
var neuronCount = 0;
for (var i1 = 0; i1 < layer.length; i1++) {
neuronCount++;
const getActiveProps = obj => Object.keys(obj).filter(k => obj[k].active)
function hasActivePathAhead(obj, count) {
if (!count) {
count = 0;
}
if (obj.active) {
var targets = getActiveProps(obj.connections);
if (obj.layer === 2) {
return true;
} else if (obj.connections[targets[count]]) {
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
function hasActivePathBehind(obj, count) {
if (!count) {
count = 0;
}
if (obj.active) {
var sources = getActiveProps(obj.connected);
if (obj.layer === 0) {
return true;
} else if (obj.connected[sources[count]]) {
for (var i1 = 0; i1 < sources.length; i1++) {
var result =
hasActivePathBehind(obj.connected[sources[count]].source, count + 1);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
if (hasActivePathAhead(layer[i1]) && hasActivePathBehind(layer[i1])) {
context.beginPath();
context.arc((widthDivision * neuronCount)
- (0.5 * widthDivision),
(heightDivision * layerCount)
- (heightDivision * 0.5),
5, 0, 2 * Math.PI, false);
context.fillStyle = '#adf442';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#56cc41';
context.stroke();
var connectionCount = 0;
for (var i2 = 0; i2 < Object.keys(layer[i1].connections).length; i2++) {
var connection =
layer[i1].connections[Object.keys(layer[i1].connections)[i2]];
if (hasActivePathAhead(connection.target)
&& hasActivePathBehind(connection.target)) {
var targetLayer = connection.target.layer;
var index = layerList[targetLayer].findIndex(function(e) {
return e == connection.target
});
if (index > -1) {
var targetLayerLength = Object.keys(layerList[targetLayer]).length;
var targetLayerWidthDivision = width / targetLayerLength;
var p1 = {
x: (widthDivision * neuronCount) - (0.5 * widthDivision),
y: (heightDivision * layerCount) - (heightDivision * 0.5)
};
var p2 = {
x: (index * targetLayerWidthDivision)
+ (0.5 * targetLayerWidthDivision),
y: (targetLayer * heightDivision)
+ (heightDivision * 0.5)
};
connectionCount++;
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.lineWidth = 1;
context.stroke();
}
}
}
}
}
}
var layerCount = 0;
for (i1 = 0; i1 < layerList.length; i1++) {
layerCount++;
renderLayer(layerList[i1], layerCount, layerList.length);
}
}
"Working" test example: https://jsfiddle.net/au2Lt6na/4/
For 5 hours I've tinkered with this function trying to pinpoint the issue and for the life of me I haven't been able to figure it out. Can anyone tell me what's causing the rendering of non-top-to-bottom neural pathways?
Note: I've spent many more hours over the past few days trying to fix this, writing totally new ways of figuring out which paths are complete from top to bottom and it still suffers from the same issues as before. I'm missing something here.
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
return result;
}
This snippet is weird. You may need this instead:
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
if(result){
return true;
}
}
And the usage of count is weird there. I think the count should not be passed as a param, since it's used to index a target or source.
I think the snippet should be like this:
else if (targets.length) {
var target;
for (var i1 = 0; i1 < targets.length; i1++) {
target=targets[i1];
var result = hasActivePathAhead(obj.connections[target].target);
if(result){
return true;
}
}
return false;
}
Live demo here:
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function (target) {
if (that.active == true) {
new Connection(brain, this, target, function (id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this;
callback(this.id, this);
}
}
function renderBrain(brain, context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDivision = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
function renderLayer(layer, layerCount, layerTotal) {
var length = layer.length;
var widthDivision = width / length;
var neuronCount = 0;
for (var i1 = 0; i1 < layer.length; i1++) {
neuronCount++;
const getActiveProps = obj => Object.keys(obj).filter(k => obj[k].active)
function hasActivePathAhead(obj) {
if (obj.active) {
var targets = getActiveProps(obj.connections);
if (obj.layer === 2) {
return true;
} else if (targets.length) {
var target;
for (var i1 = 0; i1 < targets.length; i1++) {
target = targets[i1];
var result = hasActivePathAhead(obj.connections[target].target);
if (result) {
return true;
}
}
return false;
} else {
return false;
}
} else {
return false;
}
}
function hasActivePathBehind(obj) {
if (obj.active) {
var sources = getActiveProps(obj.connected);
if (obj.layer === 0) {
return true;
} else if (sources.length) {
var source;
for (var i1 = 0; i1 < sources.length; i1++) {
source = sources[i1];
var result =
hasActivePathBehind(obj.connected[source].source);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
if (hasActivePathAhead(layer[i1]) && hasActivePathBehind(layer[i1])) {
context.beginPath();
context.arc((widthDivision * neuronCount) -
(0.5 * widthDivision),
(heightDivision * layerCount) -
(heightDivision * 0.5),
5, 0, 2 * Math.PI, false);
context.fillStyle = '#adf442';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#56cc41';
context.stroke();
var connectionCount = 0;
for (var i2 = 0; i2 < Object.keys(layer[i1].connections).length; i2++) {
var connection =
layer[i1].connections[Object.keys(layer[i1].connections)[i2]];
if (hasActivePathAhead(connection.target) &&
hasActivePathBehind(connection.target)) {
var targetLayer = connection.target.layer;
var index = layerList[targetLayer].findIndex(function (e) {
return e == connection.target
});
if (index > -1) {
var targetLayerLength = Object.keys(layerList[targetLayer]).length;
var targetLayerWidthDivision = width / targetLayerLength;
var p1 = {
x: (widthDivision * neuronCount) - (0.5 * widthDivision),
y: (heightDivision * layerCount) - (heightDivision * 0.5)
};
var p2 = {
x: (index * targetLayerWidthDivision) +
(0.5 * targetLayerWidthDivision),
y: (targetLayer * heightDivision) +
(heightDivision * 0.5)
};
connectionCount++;
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.lineWidth = 1;
context.stroke();
}
}
}
}
}
}
var layerCount = 0;
for (i1 = 0; i1 < layerList.length; i1++) {
layerCount++;
renderLayer(layerList[i1], layerCount, layerList.length);
}
}
var brain = {
counter: 0,
layers: 3,
globalReferenceNeurons: {},
globalReferenceConnections: {},
}
var layer0 = [new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),new Neuron(brain, 0), new Neuron(brain, 0)];
var layer1 = [new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1)];
var layer2 = [new Neuron(brain, 2), new Neuron(brain, 2), new Neuron(brain, 2), new Neuron(brain, 2)];
layer0[0].connect(layer1[1]);
layer0[1].connect(layer1[0]);
layer0[3].connect(layer1[0]);
layer1[0].connect(layer2[0]);
layer1[2].connect(layer2[2]);
layer1[1].connect(layer2[3]);
var canvas = document.getElementById('cav');
var ctx = canvas.getContext('2d');
renderBrain(brain, ctx, canvas);
<canvas id="cav" width="600" height="400"></canvas>
Could not fit into the comment and as you do not have a working example we can only guess.
Your recursion does not look right to me. The count variable makes no sense and you have several levels of redundancy checking for active 3 times for each iteration and not vetting when indexing into the key arrays with count.
Without working code and as your variable nomenclature is confusing this is only a guess at how to fix. Same applies to hasActivePathBehind
Ignore the following code
function hasActivePathAhead(obj) {
if (obj.active) {
if (obj.layer === 2) {
return true;
}
var targets = getActiveProps(obj.connections);
for (var i = 0; i < targets.length; i++) {
if(hasActivePathAhead(obj.connections[targets[i]].target)){
return true;
}
}
}
return false;
}
UPDATE and working fix.
Update because accepted answer does not work, it should have used a more rigorous test.
As you have provided a fiddle in the comments I had a look as see that you have removed the count from the functions. Though the functions are still incorrect and not working, the error is elsewhere in the code. The code is overly complex hiding the nature of the bug.
There is too much wrong to go into detail so I have just started from scratch.
Rather than test every node for a backward and forward link I traverse the layers forward from top to bottom. This negates the need to check backward connections. I have a function that checks if a node is connected to the bottom, a function that draws all nodes from a node to the bottom, and a function that draws all active nodes at a layer (active is connected from that layer down)
You can optimise it by adding a flag to nodes indicating that they have already been rendered as the code as is can render some nodes several times. But I did not add that as I did not want to modify the data structure you had. Or you can add a Map that holds node pairs that have been rendered and check that to see if a node pair needs to be rendered.
Using your fiddle as a template here is a working version using the randomised paths as provided in the fiddle.
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function (target) {
if (that.active == true) {
new Connection(brain, this, target, function (id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this;
callback(this.id, this);
}
}
function renderBrain(brain, ctx, canvas) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDiv = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
var coord; // to hold node coordinates defined here to prevent pointless memory allocation dealocation cycle
// Gets the node position based on its ID and layer position
function nodePosition(node,coord = {}){
var pos;
pos = node.id - layerList[node.layer][0].id; // get pos from node id (if this does not hold true you should include the node position in the node data is it is important)
coord.x = (width / layerList[node.layer].length) * (pos + 0.5);
coord.y = heightDiv * (node.layer + 0.5);
return coord;
}
// draws a node
function drawNode(node){
ctx.strokeStyle = '#56cc41';
ctx.fillStyle = '#adf442';
ctx.lineWidth = 2;
coord = nodePosition(node,coord);
ctx.beginPath();
ctx.arc(coord.x,coord.y, 5, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
// draws a link between two nodes
function drawLink(node,node1){
ctx.strokeStyle = '#56cc41';
ctx.lineWidth = 1;
coord = nodePosition(node,coord);
ctx.beginPath();
ctx.moveTo(coord.x,coord.y);
coord = nodePosition(node1,coord);
ctx.lineTo(coord.x,coord.y);
ctx.stroke();
}
// returns true if the path from this node jas a connection that leads to the end
function isPathActive(node){
var paths, i, nextNode;
if(node.active){
if(node.layer === 2){ // is node at end
return true;
}
paths = Object.keys(node.connections).map(key => node.connections[key]);
for(i = 0; i < paths.length; i ++){
nextNode = paths[i].target;
if(nextNode.active){
if(nextNode.layer === 2){
return true;
}
if(isPathActive(nextNode)){
return true;
}
}
}
}
return false;
}
// renders from a node all active pathes to end
function renderPath(node){
var i;
paths = Object.keys(node.connections).map(key => node.connections[key]);
for(i = 0; i < paths.length; i ++){
nextNode = paths[i].target;
if(isPathActive(nextNode)){
drawLink(node,nextNode)
renderPath(nextNode);
}
}
drawNode(node,i+ 1)
}
// renders from top layer all active paths
function renderActivePaths(layer){
var i;
for(i = 0; i < layer.length; i ++){
if(isPathActive(layer[i])){
renderPath(layer[i])
}
}
}
renderActivePaths(layerList[0]);
}
var brain = {
counter: 0,
layers: 3,
globalReferenceNeurons: {},
globalReferenceConnections: {},
}
var layer0 = [new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),
new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),
new Neuron(brain, 0), new Neuron(brain, 0),new Neuron(brain, 0),
new Neuron(brain, 0)]; //10
var layer1 = [new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1)]; //13
var layer2 = [new Neuron(brain, 2), new Neuron(brain, 2)]; //2
layer0[0].connect(layer1[0]);
layer0[1].connect(layer1[1]);
layer0[2].connect(layer1[2]);
layer0[3].connect(layer1[3]);
layer0[4].connect(layer1[4]);
layer0[5].connect(layer1[5]);
layer0[6].connect(layer1[6]);
layer0[7].connect(layer1[7]);
layer0[8].connect(layer1[8]);
layer0[9].connect(layer1[9]);
layer0[0].connect(layer1[3]);
layer0[1].connect(layer1[4]);
layer0[2].connect(layer1[5]);
layer0[3].connect(layer1[6]);
layer0[4].connect(layer1[7]);
layer0[5].connect(layer1[8]);
layer0[6].connect(layer1[9]);
layer0[7].connect(layer1[10]);
layer0[8].connect(layer1[11]);
layer0[9].connect(layer1[12]);
layer1[0].connect(layer2[0]);
layer1[1].connect(layer2[1]);
layer1[2].connect(layer2[0]);
layer1[3].connect(layer2[1]);
layer1[4].connect(layer2[0]);
layer1[5].connect(layer2[1]);
layer1[6].connect(layer2[0]);
layer1[7].connect(layer2[1]);
layer1[8].connect(layer2[0]);
layer1[9].connect(layer2[1]);
layer1[10].connect(layer2[0]);
layer1[11].connect(layer2[1]);
layer1[12].connect(layer2[0]);
//works! until...
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
for (prop in brain.globalReferenceNeurons) {
var rand = getRandomInt(1,6);
var neuron = brain.globalReferenceNeurons[prop];
if (rand == 1 && neuron.layer != 2) neuron.active = false;
}
for (prop in brain.globalReferenceConnections) {
var rand = getRandomInt(1,6);
var connection = brain.globalReferenceConnections[prop];
if (rand == 1) connection.active = false;
}
renderBrain(brain, canvas.getContext("2d"), canvas);
<canvas id="canvas" width= 512 height = 200></canvas>

Emitter follows Particle in canvas

in this HTML5 code, I try to fire particles from the Emitter in objects[0].
This can be run with objects[0].play in the console.
However, the Emitter follows the Particle fired.
My question is, why?
HTML Code
<html>
<head>
<style>
#canv{
border: 1px solid black;
}
</style>
<script src = "setup.js"></script>
<script src = "main.js"></script>
</head>
<body onLoad = "start()">
<canvas width = "500" height = "500" id = "canv"></canvas>
</body>
</html>
setup.js
var objects = [];
var sprites = [];
var c;
var ctx;
var time;
var timeInterval;
function newSprite(url){
var a = sprites.length;
var b = new Image();
b.src = url;
b.onload = function(){
sprites[a] = b;
};
}
function randomN(min, max){
return Math.floor((Math.random()*max) - min);
}
function Vector2(x,y){
this.x = x;
this.y = y;
}
function Particle(pos, life, vel, accel){
this.pos = pos;
this.life = life || Infinity;
this.vel = vel || new Vector2(0,0);
this.accel = accel || new Vector2(0,0);
this.update = function(){
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
this.vel.x += this.accel.x;
this.vel.y += this.accel.y;
this.life--;
if(this.life <= 0){
return false;
}else{
return true;
}
};
}
function Emitter(pos){
this.pos = pos;
this.actualPos = this.pos;
this.particles = [];
this.forces = [];
this.playing = false;
this.newParticle = function(life, vel, accel){
var a = this.particles.length;
this.particles[a] = new Particle(this.pos, life, vel, accel);
};
this.update = function(){
console.log("(" + this.actualPos.x + ", " + this.actualPos.y + ") " + "(" + this.pos.x + ", " + this.pos.y + ")");
for(var a = 0; a < this.particles.length; a++){
for(var b = 0; b < this.forces.length; b++){
this.forces[b](this.particles[a]);
}
var that = this.particles[a];
var particleAlive = that.update();
if(particleAlive == false){
this.particles.splice(a, 1);
a--;
}
}
};
this.play = function(){
this.playing = true;
};
}
function timeStep(){
for(var a = 0; a < objects.length; a++){
if(objects[a].__proto__.constructor.name == "Emitter"){
if(objects[a].playing == true){
objects[a].update();
}
}
}
objects[1].newParticle(1000, new Vector2(1, 0), new Vector2(0.1, 0));
time++;
}
function gravity(p){
//p.vel.y += 0.1;
}
main.js
function start(){
c = document.getElementById("canv");
ctx = c.getContext('2d');
newSprite("spark.png");
objects[0] = new Emitter(new Vector2(50, 60));
objects[0].forces.push(gravity);
objects[0].newParticle(1000, new Vector2(1, 0), new Vector2(0.1, 0));
objects[1] = new Emitter(new Vector2(100, 100));
objects[1].forces.push(gravity);
time = 0;
timeInterval = window.setInterval(timeStep, 10);
reDraw();
}
function reDraw(){
ctx.clearRect(0, 0, c.width, c.height);
for(var a = 0; a < objects.length; a++){
if(objects[a].__proto__.constructor.name == "Emitter"){
ctx.beginPath();
ctx.arc(objects[a].pos.x, objects[a].pos.y, 5, 0, 2*Math.PI);
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
if(objects[a].playing == true){
for(var b = 0; b < objects[a].particles.length; b++){
ctx.beginPath();
ctx.drawImage(sprites[0], objects[a].particles[b].pos.x, objects[a].particles[b].pos.y, 8,8);
//ctx.arc(objects[a].particles[b].pos.x, objects[a].particles[b].pos.y, 5, 0, 2*Math.PI);
ctx.fillStyle = "black";
ctx.fill();
ctx.closePath();
}
}
}
}
requestAnimationFrame(reDraw);
}
Your mistake is a quite classical error of using an object reference rather than using the object's values : when you create a new particle, you set its position to be the very position of the emitter.
So any latter change to the particle's position changes the emitter's position : again, the position (pos) Object is the very same object.
What you want is each particle to have its own position, that has the initial value of the emitter's position.
Done, for instance, with :
function Particle(pos, life, vel, accel){
this.pos = new Vector2(pos.x, pos.y);
//...
Notice that velocity and acceleration are also references, which here is not an issue since you call the function calling the constructor with a new Vector2.
However you might want to protect your code against future possible errors by performing a copy also for those two properties.
I havne't followed the full logic so sorry if this isn't the cause, but there's something odd about this :
function newSprite(url){
var a = sprites.length;
var b = new Image();
b.src = url;
b.onload = function(){
sprites[a] = b;
};
}
At the time when onload runs, a and b are lno longer in scope (I believe): The onload event would be fired as a new function call outside of newSprite.
Wouldn't you need to do something like this ...
b.onload = function(){
sprites[sprites.length] = this;
};
so that the function runs correctly when it's called ?

Simple memory game doesn't update its state. Bad game loop?

I'm trying to make a simple (or so I thought) memory game. Unfortunately it does not update state of cards when user clicks on them. I'm running out of ideas, probably because it's my first javascript game. I suppose there is a problem with game loop. Could anyone at least point me in the right direction and help me understand what needs to be changed/rewritten?
//HTML5 Memory Game implementation
//main variables
var cards = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8];
var exposed = [makeArray("false",16)];
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i<length) {
newArray[i] = value;
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = [makeArray("false",16)];
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i*50+12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i*50, 0, 50, 100);
ctx.strokeRect(i*50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
addEventListener('click', readPos, false);
setInterval(function() {
update();
draw();
}, 16);
I would check your addEventListener method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
I also recommend you look into using jQuery.
After copy and pasting your code I found a couple of things:
You didn't add an event listener to anything, you should add it to something so I added it to document.
You initialize the exposed array with values "false" and later check if they are false. These are not the same, the string "false" isn't the Boolean false.
You initializes the exposed array as a multi dimensional array [[false,false,false ...]] this should be a single dimension array because later you check exposed[1] (1 depending on the mouse x position.
No need to call draw and update every 16 milliseconds, you can call it after someone clicked.
Wrapped the whole thing up in a function so there are no global variables created.
Here is the code after changing these obvious errors. There might be room for optimization but for now I've gotten the problems out.
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="game"></div>
<script type="text/javascript">
(function(){
//HTML5 Memory Game implementation
//main variables
var cards = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
var exposed = makeArray(false, 16);
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i < length) {
newArray.push(value);
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
update();
draw();
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = makeArray(false, 16);
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(150, 150, 150)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i * 50 + 12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i * 50, 0, 50, 100);
ctx.strokeRect(i * 50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
document.body.addEventListener('click', readPos, false);
init();
draw();
})();
</script>
</body>
</html>
Your overall logic was good.
The point that was 'bad' was the way you handle the event :
the event handler should store some valuable information that
the update will later process and clear.
Here you mix your update with event handling, which cannot work
especially since the event will not fire on every update.
So i did a little fiddle to show you, the main change is
the click event handler, which update the var last_clicked_card :
http://jsfiddle.net/wpymH/
//read click position
function readPos(event) {
last_clicked_card = -1;
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
// on canvas ?
if ((mouseY>100)||(mouseX<0)||(mouseX>WIDTH)) return;
// now yes : which card clicked ?
last_clicked_card = Math.floor(mouseX/50);
}
and then update is the processing of this information :
//update cards
function update() {
// return if no new card clicked
if (last_clicked_card == -1) return;
// read and clear last card clicked
var newCard = last_clicked_card;
last_clicked_card=-1;
// flip, store it as first card and return
// if there was no card flipped
if (state==0) { exposed[newCard] = true;
first_card = newCard;
state = 1 ;
return; }
// just unflip card if card was flipped
if ((state = 1) && exposed[newCard]) {
exposed[newCard]=false ;
state=0;
return;
}
// we have a second card now
second_card = newCard;
exposed[second_card] = true;
draw();
// ... i don't know what you want to do ...
if (cards[first_card] == cards[second_card]) {
alert('win'); }
else {
alert('loose'); }
exposed[first_card]=false;
exposed[second_card]=false;
state=0;
}

Categories