I want to access my mobile ip camera instead of using webcam. I tried to set the ip address in the ref attribute but it doesn't show anything I also try to set the localhost address to ref but it still doesn't show anything. This code is for gun detection it works only in web cam mode I want to use the mobile IP Camera instead of webcam. I am using my mobile camera as an IP camera and want to access that specific camera but everytime it shows me the webcam of my laptop. I tried to set the value of this.video.current.srcObject to the ip of my camera and also try to assign it the localhost address but it still doesn't work.
import React from 'react'
import * as tf from '#tensorflow/tfjs'
import './styles.css'
const LABELS = '/model/labels.json'
const MODEL = '/model/tensorflowjs_model.pb'
const WEIGHTS_URL = '/model/weights_manifest.json'
var obj;
const TFWrapper = model => {
const calculateMaxScores = (scores, numBoxes, numClasses) => {
const maxes = []
const classes = []
for (let i = 0; i < numBoxes; i++) {
let max = Number.MIN_VALUE
let index = -1
for (let j = 0; j < numClasses; j++) {
if (scores[i * numClasses + j] > max) {
max = scores[i * numClasses + j]
index = j
}
}
maxes[i] = max
classes[i] = index
}
return [maxes, classes]
}
const buildDetectedObjects = (width,height,boxes,scores,indexes,classes) => {
const count = indexes.length
const objects = []
for (let i = 0; i < count; i++) {
const bbox = []
for (let j = 0; j < 4; j++) {
bbox[j] = boxes[indexes[i] * 4 + j]
}
const minY = bbox[0] * height
const minX = bbox[1] * width
const maxY = bbox[2] * height
const maxX = bbox[3] * width
bbox[0] = minX
bbox[1] = minY
bbox[2] = maxX - minX
bbox[3] = maxY - minY
objects.push({
bbox: bbox,
class: classes[indexes[i]],
score: scores[indexes[i]]
})
}
return objects
}
const detect = input => {
const batched = tf.tidy(() => {
const img = tf.fromPixels(input)
// Reshape to a single-element batch so we can pass it to executeAsync
return img.expandDims(0)
})
const height = batched.shape[1]
const width = batched.shape[2]
return model.executeAsync(batched).then(result => {
const scores = result[0].dataSync()
const boxes = result[1].dataSync()
// clean the webgl tensors
batched.dispose()
tf.dispose(result)
const [maxScores, classes] = calculateMaxScores(
scores,
result[0].shape[1],
result[0].shape[2]
)
const indexTensor = tf.tidy(() => {
const boxes2 = tf.tensor2d(boxes, [
result[1].shape[1],
result[1].shape[3]
])
return tf.image.nonMaxSuppression(
boxes2,
maxScores,
20, // maxNumBoxes
0.1,
0.5
)
})
const indexes = indexTensor.dataSync()
indexTensor.dispose()
return buildDetectedObjects(width,height,boxes,maxScores,indexes,classes)
})
}
return {
detect: detect
}
}
class Detection extends React.Component {
videoRef = React.createRef()
canvasRef = React.createRef()
gunDetected = false
componentDidMount() {
if (navigator.mediaDevices) {
const webCamPromise = navigator.mediaDevices
.getUserMedia({
audio: false,
video: {
facingMode: 'user'
}
})
.then(stream => {
window.stream = stream
this.videoRef.current.srcObject = stream
return new Promise((resolve, _) => {
this.videoRef.current.onloadedmetadata = () => {
resolve()
}
})
})
const modelPromise = tf.loadFrozenModel(MODEL, WEIGHTS_URL)
const labelsPromise = fetch(LABELS).then(data => data.json())
Promise.all([modelPromise, labelsPromise, webCamPromise])
.then(values => {
const [model, labels] = values
this.detectFrame(this.videoRef.current, model, labels)
})
.catch(error => {
console.error(error)
})
}
}
setRef = webcam => {
this.webcam = webcam;
};
detectFrame = (video, model, labels) => {
TFWrapper(model)
.detect(video)
.then(predictions => {
this.renderPredictions(predictions, labels)
requestAnimationFrame(() => {
this.detectFrame(video, model, labels)
})
})
}
renderPredictions = (predictions, labels) => {
const ctx = this.canvasRef.current.getContext('2d')
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
// Font options.
const font = '20px sans-serif'
ctx.font = font
ctx.textBaseline = 'top'
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const width = prediction.bbox[2]
const height = prediction.bbox[3]
const label = labels[parseInt(prediction.class)]
obj=label;
console.log(obj) //returning GUN
//Bounding Box Styling s
ctx.strokeStyle = '#FF0000'
ctx.lineWidth = 5
ctx.strokeRect(x, y, width, height)
//Label Background Styling
ctx.fillStyle = '#FF0000'
const textWidth = ctx.measureText(label).width
const textHeight = parseInt(font, 0)
ctx.fillRect(x, y, textWidth, textHeight)
})
predictions.forEach(prediction => {
const x = prediction.bbox[0]
const y = prediction.bbox[1]
const label = labels[parseInt(prediction.class)]
// Draw the text last to ensure it's on top.
ctx.fillStyle = '#000000'
ctx.fillText(label, x, y)
})
}
render() {
return (
<div>
<video
className="size"
autoPlay
playsInline
muted
ref={this.videoRef}
width="800"
height="500"
/>
<canvas
className="size"
ref={this.canvasRef}
width="800"
height="500"
/>
</div>
)
}
}
export default Detection;
Related
I am learning web worker and right now I am going through the problem of using await inside the onmessage. How can i do this?
import { Copc, Key } from "copc";
var nodePages, pages, receivedData;
async function load() {
let filename = "https://s3.amazonaws.com/data.entwine.io/millsite.copc.laz";
const copc = await Copc.create(filename);
let scale = copc.header.scale[0];
let [x_min, y_min, z_min, x_max, y_max, z_max] = copc.info.cube;
let width = Math.abs(x_max - x_min);
let center_x = (x_min + x_max) / 2;
let center_y = (y_min + y_max) / 2;
let center_z = (z_min + z_max) / 2;
receivedData = await Copc.loadHierarchyPage(
filename,
copc.info.rootHierarchyPage
);
nodePages = receivedData.nodes;
pages = receivedData.pages;
postMessage(200);
}
onmessage = function (message) {
let index = message.data;
let myRoot = nodePages[keyCountMap[m]];
const view = await Copc.loadPointDataView(filename, copc, myRoot);
};
and again there is another issue, the loadPointDataView function is asynchronous, how can i implement this in my webworker?
Any help please
This is my original code that i want to parallelize:
let filename = "https://s3.amazonaws.com/data.entwine.io/millsite.copc.laz";
const copc = await Copc.create(filename);
scale = copc.header.scale[0];
const { nodes: nodePages, pages: pages } = await Copc.loadHierarchyPage(
filename,
copc.info.rootHierarchyPage
);
for (let m = 0; m < keyCountMap.length; m += 2) {
let myRoot = nodePages[keyCountMap[m]];
const view = await Copc.loadPointDataView(filename, copc, myRoot);
let getters = ["X", "Y", "Z", "Intensity"].map(view.getter);
let chunkCount = 20;
let totalCalled = 0;
let innerPromises = [];
for (let j = 0; j < keyCountMap[m + 1]; j += chunkCount) {
readPoints(index + j, getters)
}
}
const readPoints = async (id, getters) => {
return new Promise((resolve, reject) => {
let returnPoint = getXyzi(id, getters);
positions.push(
returnPoint[0] - x_min - 0.5 * width,
returnPoint[1] - y_min - 0.5 * width,
returnPoint[2] - z_min - 0.5 * width
);
const vx = (returnPoint[3] / 65535) * 255;
color.setRGB(vx, vx, vx);
colors.push(color.r, color.g, color.b);
});
};
function getXyzi(index, getters) {
return getters.map((get) => get(index));
}
Here i'm calculation two values color and number after calculation i'm updating the values but this is not working
gameSchema.methods.computeBets = async function(){
const game = this;
const greenMultiplier = 2;
const voiletMultiplier = 4.5;
const redMultiplier = 2;
try {
const { gameType } = game;
let model = Schema;
gameType === 'Fast Parity' ? model = FastParity : model = SlowParity;
const gameDetails = await model.findOne({ gameId: game._id });
const { allPlayerBettedBetId } = gameDetails;
let color = {};
let number = {};
await Promise.all(allPlayerBettedBetId.map(async(bet) => {
const betdetails = await UserBet.findById(bet._id);
const { colorBettedOn, numberBettedOn,amountBetted,betType} = betdetails;
// console.log(colorBettedOn, numberBettedOn, amountBetted);
if(betType==='color')
color[colorBettedOn] = color[colorBettedOn] ? color[colorBettedOn] + amountBetted : amountBetted;
if(betType==='number')
number[numberBettedOn] = number[numberBettedOn] ? number[numberBettedOn] + amountBetted : amountBetted;
}));
console.log(color, number);
color.forEach(item => {
if (item.green)
item.green *= greenMultiplier;
if (item.red)
item.red *= redMultiplier;
if (item.voilet)
item.voilet *= voiletMultiplier;
});
let colorCombination = {
"green": color.green,
"green/voilet": color.green+(color.voilet/2),
"red/voilet": color.red+(color.voilet/2),
"red": color.red
};
number.forEach(item => {
item.value *= 2;
})
console.log(number,colorCombination)
return { number, colorCombination };
after await Promise.all console.log(number,color) is working but when i'm updating the values they are not working.I'm not good at async function as i don't have much exprience in that.what mistake i'm doing in the forEach functions
I'm trying to use soundtouch.js to preserve the pitch of audio in the browser, but am having some trouble with an instance property not being recognized by the audio context during an event.
Here's my basic React UI:
App.js
These are the functions in my React component handling the input ranges and getting a Playback instance:
const playbackEngine = new PlaybackEngine({
emitter: emitter,
pitch: pitch,
tempo: tempo,
});
const handlePitchChange = (event) => {
pitch = event.target.value;
document.getElementById('pitch-value').innerHTML = `Pitch (${pitch}x)`;
emitter.emit('pitch');
playbackEngine.pitch = event.target.value;
}
const handleTempoChange = (event) => {
tempo = event.target.value;
document.getElementById('tempo-value').innerHTML = `Tempo (${tempo}x)`;
emitter.emit('tempo');
playbackEngine.tempo = event.target.value;
}
const handleSeek = (event) => {
percent = parseFloat(event.target.value);
document.getElementById('seek-value').value = percent;
playbackEngine.seekPercent(percent);
playbackEngine.play();
}
PlaybackEngine.js
const {SimpleFilter, SoundTouch} = require('./soundtouch');
const BUFFER_SIZE = 4096;
export default class PlaybackEngine {
constructor({emitter, pitch, tempo}) {
this.emitter = emitter;
this.context = new AudioContext();
this.scriptProcessor = this.context.createScriptProcessor(BUFFER_SIZE, 2, 2);
this.scriptProcessor.onaudioprocess = e => {
const l = e.outputBuffer.getChannelData(0);
const r = e.outputBuffer.getChannelData(1);
const framesExtracted = this.simpleFilter.extract(this.samples, BUFFER_SIZE);
if (framesExtracted === 0) {
this.emitter.emit('stop');
}
for (let i = 0; i < framesExtracted; i++) {
l[i] = this.samples[i * 2];
r[i] = this.samples[i * 2 + 1];
}
};
this.soundTouch = new SoundTouch();
this.soundTouch.pitch = pitch;
this.soundTouch.tempo = tempo;
this.duration = undefined;
}
get pitch() {
return this.soundTouch.pitch;
}
set pitch(pitch) {
this.soundTouch.pitch = pitch;
}
get tempo() {
return this.soundTouch.tempo;
}
set tempo(tempo) {
this.soundTouch.tempo = tempo;
}
decodeAudioData(data) {
return this.context.decodeAudioData(data);
}
setBuffer(buffer) {
const bufferSource = this.context.createBufferSource();
bufferSource.buffer = buffer;
this.samples = new Float32Array(BUFFER_SIZE * 2);
this.source = {
extract: (target, numFrames, position) => {
this.emitter.emit('time', (position / this.context.sampleRate));
const l = buffer.getChannelData(0);
const r = buffer.getChannelData(1);
for (let i = 0; i < numFrames; i++) {
target[i * 2] = l[i + position];
target[i * 2 + 1] = r[i + position];
}
return Math.min(numFrames, l.length - position);
},
};
this.simpleFilter = new SimpleFilter(this.source, this.soundTouch);
this.emitter.emit('buffered', this.simpleFilter);
this.duration = buffer.duration;
this.emitter.emit('duration', buffer.duration);
}
play() {
this.scriptProcessor.connect(this.context.destination);
}
pause() {
this.scriptProcessor.disconnect(this.context.destination);
}
seekPercent(percent) {
if (this.simpleFilter !== undefined) {
this.simpleFilter.sourcePosition = Math.round(
percent / 100 * this.duration * this.context.sampleRate
);
}
}
}
App.js calls playbackEngine.setBuffer() once the audio file is ready, which adds this.simpleFilter as an instance property. The audio plays correctly, but when I call seekPercent() in my handleSeek function, it is undefined. Consequently, the onaudioprocess crashes because of this, with the following error:
Uncaught TypeError: Cannot read property 'extract' of undefined
at ScriptProcessorNode.PitchEngine.scriptProcessor.onaudioprocess
Thanks in advance for the help.
Solved it.
The problem was that the audio's arrayBuffer was being placed in setBuffer() during the component's useEffect with an empty dependency array (componentDidMount-style). So when events occurred on the instantiated playback engine, they were as if only the instance properties before the useEffect was called existed. Fixed it by defining event listeners within the useEffect on my input controls.
I'm doing a coding challenge from the coding train, and I'm trying to improve on his code. The idea is that the cars are driving around a race track. When I went back to check something, I noticed that I misspelled "activation: sigmoid", as in activation function. When I fixed it, the cars seemed to be driving in circles.
I'm a very new coder (as I am 12 years old), so many things in my code are broken, hard to understand, or just not finished. I'm also pretty new to stack overflow, so I might be breaking a lot of rules.
The link to download my project is here: https://1drv.ms/u/s!ApmY_SAko19ChzCKe5uNT7I9EZAX?e=YUg2ff
The misspelled words are at lines 29 and 34 in the nn.js file.
car.js
function pldistance(p1, p2, x, y) {
const num = abs((p2.y - p1.y) * x - (p2.x - p1.x) * y + p2.x * p1.y - p2.y * p1.x);
const den = p5.Vector.dist(p1, p2);
return num / den;
}
class Car {
constructor(brain, color = [random(255), random(255), random(255)]) {
this.colorGene = color;
this.dead = false;
this.finished = false;
this.fitness = 0;
this.rays = [];
this.wallRays = [];
this.degreeOfSight = degreeOfSight;
this.degreeOfRays = degreeOfSight / (numOfRays - 1);
if (this.degreeOfSight == 360) {
this.degreeOfRays = degreeOfSight / numOfRays;
}
this.pos = createVector(start.x, start.y);
this.vel = createVector();
this.acc = createVector();
this.sight = sight;
this.maxspeed = maxspeed;
this.maxforce = maxTurningSpeed;
this.currentGoal = 0;
this.timeTillDeadC = timeTillDead;
this.timeTillDead = this.timeTillDeadC;
this.goal;
this.rate = mutationRate;
if (degreeOfSight != 360) {
for (let a = -(this.degreeOfSight / 2); a <= this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
} else {
for (let a = -(this.degreeOfSight / 2); a < this.degreeOfSight / 2; a += this.degreeOfRays) {
this.rays.push(new Ray(this.pos, radians(a)));
}
}
for (let a = 0; a < 360; a += 45) {
this.wallRays.push(new Ray(this.pos, radians(a)));
}
if (brain) {
this.brain = brain.copy();
} else {
this.brain = new NeuralNetwork(this.rays.length + 2, 16, 2);
}
}
applyForce(force) {
this.acc.add(force);
}
update(x, y) {
this.timeTillDead--;
if (this.timeTillDead <= 0) {
this.dead = true;
}
if (!this.dead || this.finished) {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.vel.limit(this.maxspeed);
this.acc.set(0, 0);
}
for (let i = 0; i < this.rays.length; i++) {
this.rays[i].rotate(this.vel.heading());
}
for (let i = 0; i < this.wallRays.length; i++) {
this.wallRays[i].rotate(this.vel.heading());
}
}
show(walls) {
push();
translate(this.pos.x, this.pos.y);
if (visualization) {
fill(this.colorGene[0], this.colorGene[1], this.colorGene[1]);
} else {
fill(0);
}
stroke(255);
const heading = this.vel.heading();
rotate(heading);
rectMode(CENTER);
rect(0, 0, 10, 5);
pop();
if (!this.dead) {
checkpoints[this.currentGoal].show();
}
for (let i = 0; i < this.rays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
if (closest) {
if (showLines) {
ellipse(closest.x, closest.y, 4)
stroke(255, 100)
line(this.pos.x, this.pos.y, closest.x, closest.y);
}
}
}
}
check(checkpoints, walls) {
if (!this.dead) {
this.goal = checkpoints[this.currentGoal];
const d = pldistance(this.goal.a, this.goal.b, this.pos.x, this.pos.y);
if (d < 5) {
this.fitness++;
this.currentGoal++;
this.timeTillDead = this.timeTillDeadC;
if (this.currentGoal == checkpoints.length) {
this.finished = true;
this.fitness = this.fitness * 1.5;
if (endBarrier) {
this.dead = true;
} else {
this.currentGoal = 0;
}
}
}
}
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.wallRays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record) {
record = d;
closest = pt;
}
}
}
if (record < 4) {
this.dead = true;
}
}
}
look(walls) {
const inputs = [];
for (let i = 0; i < this.wallRays.length; i++) {
let closest = null;
let record = this.sight;
for (let wall of walls) {
const pt = this.rays[i].cast(wall);
if (pt) {
const d = p5.Vector.dist(this.pos, pt);
if (d < record && d < this.sight) {
record = d;
closest = pt;
}
}
}
inputs[i] = map(record, 0, 50, 1, 0);
}
inputs.push(end.x);
inputs.push(end.y);
const output = this.brain.predict(inputs);
let angle = map(output[0], 0, 1, -PI, PI);
let speed = map(output[1], 0, 1, -this.maxspeed, this.maxspeed);
angle += this.vel.heading();
const steering = p5.Vector.fromAngle(angle);
steering.setMag(speed);
steering.limit(this.maxforce);
this.applyForce(steering);
}
mutateDemBabies() {
if (this.finished) {
this.rate = finishingMutationRate;
}
this.brain.mutate(this.rate);
let changeColor = this.brain.mutated();
if (changeColor) {
for (let color of this.colorGene) {
let r = map(random(20), 0, 20, -25, 25);
color += r;
}
}
this.rate = mutationRate;
}
dispose() {
this.brain.dispose();
}
}
nn.js
//<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#1.1.0/dist/tf.min.js"></script>
class NeuralNetwork {
//this how many inputs, hidden, and output nodes there are. modelC is the brain that we want to copy to give to the new bird
constructor(inputNumber, hiddenNumber, outputNumber, modelC) {
if (modelC instanceof tf.Sequential) {
//this is the making a copy of the neural network
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = modelC;
} else {
//this is the creating a random brain
this.input_nodes = inputNumber;
this.hidden_nodes = hiddenNumber;
this.output_nodes = outputNumber;
this.model = this.createBrain();
}
this.changeColor = false;
}
createBrain() {
//the model is the neural network
const model = tf.sequential();
//configuring the hidden layer
const hiddenLayer = tf.layers.dense({
units: this.hidden_nodes,
inputShape: [this.input_nodes],
activaation: "sigmoid"
});
//configuring the output layer
const outputLayer = tf.layers.dense({
units: this.output_nodes,
activaation: "sigmoid"
});
//adding the hidden layer to the model
model.add(hiddenLayer);
//adding the output layer to the model
model.add(outputLayer);
//returning the model
return model;
}
predict(inputs) {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a tensor with the inputs
const xs = tf.tensor2d([inputs]);
//running the inputs through the neural network
const ys = this.model.predict(xs);
//getting the raw numbers from the tensor object
const outputs = ys.dataSync();
//returning the outputs
return outputs;
});
}
copy() {
//clearing the tensors after using them
//then returning the output
return tf.tidy(() => {
//creating a new neural network
const modelCopy = this.createBrain();
//getting the weights from the old neural network
const weights = this.model.getWeights();
//setting the new weights
modelCopy.setWeights(weights);
//making a new network but this time with all the weights then returning it
return new NeuralNetwork(
this.input_nodes,
this.hidden_nodes,
this.output_nodes,
modelCopy
);
});
}
mutate(rate, colorGene) {
//clearing the tensors after using them
tf.tidy(() => {
this.changeColor = false;
//getting the weights so that we can change them later
const weights = this.model.getWeights();
//the variable that will be holding the mutated weights
const mutatedWeights = [];
for (let i = 0; i < weights.length; i++) {
//getting the shape of the current weights
let shape = weights[i].shape;
//making a copy of the raw numbers from the object tensor
//dataSync gets the numbers, but doesn't make a copy, so slice will make the copy
let values = weights[i].dataSync().slice();
for (let j = 0; j < values.length; j++) {
//if the random number is less than mutation rate the it runs the code
if (random(1) < rate) {
this.changeColor = true;
//mutating the value
//randomGaussianis returns a float from a series of numbers with a mean of 0
values[j] = values[j] + randomGaussian();
}
}
//holding the new value of each weight
mutatedWeights[i] = tf.tensor(values, shape);
}
//setting the mutated weights as the new weights
this.model.setWeights(mutatedWeights);
});
}
mutated() {
if (this.changeColor) {
this.changeColor = false;
return true;
} else {
this.changeColor = false;
return false;
}
}
dispose() {
//disposing the brain so that memory doesn't leak
this.model.dispose();
}
}
I have a code like this. It is here only to show how It works. Problem appears when I want to compare cards. When I click on the first and second card and they are not matched their grandfather div should remove a class which flips a tile. Second thing, I click on the same card twice it will return "win". How to fix this and make this code look clean?
{
let guesses = [];
let tries = 0;
const doubleArrVal = arr => arr.concat(arr);
const addFlipEffect = (e) => {
let target = e.currentTarget;
if (!target.classList.contains("tile--static")) {
target.classList.add("tile--active");
}
return target;
};
const addManyListeners = (collection, e, fn) => {
for (let i = 0; i < collection.length; i++) {
collection[i].addEventListener(e, fn, false);
}
};
const randomize = (arr) => {
for (let i = 0; i < arr.length; i++) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
};
const prepareArray = (ammount) => {
let imgNames = ["angular", "bootstrap", "css", "foundation", "github", "grunt", "html", "ruby", "jquery", "less", "nodejs", "sass"];
imgNames = imgNames.slice(0, ammount);
const doubled = doubleArrVal(imgNames);
return randomize(doubled);
};
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img" data-name="${id}">
</div>
</div>`;
return markUp;
};
const createCards = (ammount) => {
const container = document.getElementById("gameContainer");
const preparedCards = prepareArray(ammount);
preparedCards.map(card => {
const cardElement = createMarkUp(card);
container.innerHTML += cardElement;
});
return container;
};
// Problem is here
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.push(image);
tries++;
if (tries === 2) {
if (guesses[0].dataset.name === guesses[1].dataset.name) {
console.log("win");
} else {
setTimeout(() => {
guesses[0].parentNode.parentNode.classList.remove("tile--active");
guesses[1].parentNode.parentNode.classList.remove("tile--active");
}, 500);
}
guesses = [];
tries = 0;
}
}
const startGame = (level) => {
const gameCards = createCards(4);
addManyListeners(gameCards.children, "click", addFlipEffect);
addManyListeners(gameCards.children, "click", compare);
};
startGame();
}
<div id ="gameContainer"></div>
I would use a Set for guesses, to facilitate the unique selection:
let guesses = new Set;
//...
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.add(image);
if (guesses.size === 2) { // guaranteed to be 2 different images
if (new Set(Array.from(guesses, guess => guess.dataset.name)).size == 1) {
console.log("win");
guesses = new Set;
} else {
setTimeout(() => {
for (let guess of guesses) {
guess.parentNode.parentNode.classList.remove("tile--active");
}
guesses = new Set; // only clear here
}, 500);
}
}
}
If your template would put the data-name="${id}" on the grandfather/root div, it would all become a bit simpler: then you only have to work with the div, not the img:
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game" data-name="${id}">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img">
</div>
</div>`;
return markUp;
};
//...
const compare = (e) => {
guesses.add(e.currentTarget);
if (guesses.size !== 2) return;
if (new Set(Array.from(guesses, guess => guess.dataset.name)).size == 1) {
console.log("win");
guesses = new Set;
return;
}
setTimeout(() => {
for (let guess of guesses) {
guess.classList.remove("tile--active");
}
guesses = new Set;
}, 500);
}
This is an error of scope. Your timeout uses the variable guesses but it is executed in the global scope, where the variable is undefined. So I have used bind, to bind it to the function.
To make sure you have 2 different elements in guesses, simply test them before testing their value.
{
let guesses = [];
let tries = 0;
const doubleArrVal = arr => arr.concat(arr);
const addFlipEffect = (e) => {
let target = e.currentTarget;
if (!target.classList.contains("tile--static")) {
target.classList.add("tile--active");
}
return target;
};
const addManyListeners = (collection, e, fn) => {
for (let i = 0; i < collection.length; i++) {
collection[i].addEventListener(e, fn, false);
}
};
const randomize = (arr) => {
for (let i = 0; i < arr.length; i++) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
};
const prepareArray = (ammount) => {
let imgNames = ["angular", "bootstrap", "css", "foundation", "github", "grunt", "html", "ruby", "jquery", "less", "nodejs", "sass"];
imgNames = imgNames.slice(0, ammount);
const doubled = doubleArrVal(imgNames);
return randomize(doubled);
};
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img" data-name="${id}">
</div>
</div>`;
return markUp;
};
const createCards = (ammount) => {
const container = document.getElementById("gameContainer");
const preparedCards = prepareArray(ammount);
preparedCards.map(card => {
const cardElement = createMarkUp(card);
container.innerHTML += cardElement;
});
return container;
};
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.push(image);
tries++;
if (tries === 2) {
if (guesses[0] !== guesses[1] && guesses[0].dataset.name === guesses[1].dataset.name) {
console.log("win");
} else {
setTimeout(((guesses) => {
guesses[0].parentNode.parentNode.classList.remove("tile--active");
guesses[1].parentNode.parentNode.classList.remove("tile--active");
}).bind(null, guesses), 500);
}
guesses = [];
tries = 0;
}
}
const startGame = (level) => {
const gameCards = createCards(4);
addManyListeners(gameCards.children, "click", addFlipEffect);
addManyListeners(gameCards.children, "click", compare);
};
startGame();
}
<div id="gameContainer"></div>