I can't make setTimeout() work in my script - javascript

I'm making a memory game. It already sort of works, but when I choose two cards that aren't the same picture, they flip back instantly, without showing the user what is on the second card. I think I should use setTimeout(), but for now it only made my flipBack() function not work at all. Maybe, I am using it incorrectly or in the wrong place.
I tried putting both the flipBack() and setTimeout(flipBack, 5000) inside "else if", outside of it, outside of my revealCard() function.
I'm only getting "Uncaught TypeError: Cannot read properties of undefined (reading 'style')
at flipBack (memory game.js:61:35)" in the console. It seems that setTimeout cannot execute flipBack() or something like that.
let board = document.getElementById("board");
let score = document.getElementById("score");
let fails = document.getElementById("fails");
let cards = document.querySelectorAll(".card");
// add eventListener to every card
for (let i = 0; i < 6; i++) {
cards[i].addEventListener("click", revealCard);
}
let revealCounter = 2;
let imgAltArray = [];
let latestTwoRevealedCards = [];
let points = 0;
let wrongGuesses = 0;
function revealCard(event) {
if (revealCounter > 0) {
let cardImg = event.target.firstChild;
// make card "flip", so you can see the picture
cardImg.style.visibility = "visible";
imgAltArray.push(cardImg.alt);
latestTwoRevealedCards.push(cardImg);
console.log(imgAltArray);
revealCounter--;
// check if both cards have the same picture on them by comparing alt parameters
if (revealCounter == 0 && imgAltArray[0] === imgAltArray[1]) {
imgAltArray = [];
latestTwoRevealedCards = [];
points++
score.textContent = `Score: ${points}`;
revealCounter = 2;
}
else if (revealCounter == 0 && imgAltArray[0] !== imgAltArray[1]) {
wrongGuesses++;
fails.textContent = `Failed attempts: ${wrongGuesses}`
imgAltArray = [];
// make cards flip back
setTimeout(flipBack, 5000);
latestTwoRevealedCards = [];
revealCounter = 2;
}
}
}
// TIMEOUT DOESN"T WORK
function flipBack() {
for (let i = 1; i >= 0; i--) {
latestTwoRevealedCards[i].style.visibility = "hidden";
}
}

latestTwoRevealedCards[i] This element may be undefined

Related

Cannot access 'isColliding' before initialization/ divs colliding

I'm trying to make an egg catching game and found some code snippets online and wanted them to work together so the points go up by one when two divs are colliding. I am getting this error Cannot access 'isColliding' before initialization. Also I don't know if the div collision works since I can't test it because of the error, thanks in advance for any help!
Code:
let points = $('#points');
let countpoints = 0;
let overlap = isColliding("#basket", "#egg6");
if (overlap) {
function EggHitsBasket() {
countpoints++;
points.text("points:" + countpoints);
}
}
let isColliding = function (div1, div2) {
let d1Offset = div1.offset();
let d1Height = div1.outerHeight(true);
let d1Width = div1.outerWidth(true);
let d1Top = d1Offset.top + d1Height;
let d1Left = d1Offset.left + d1Width;
let d2Offset = div2.offset();
let d2Height = div2.outerHeight(true);
let d2Width = div2.outerWidth(true);
let d2Top = d2Offset.top + d2Height;
let d2Left = d2Offset.left + d2Width;
return !(d1Top < d2Offset.top || d1Offset.top > d2Top || d1Left < d2Offset.left || d1Offset.left > d2Left);
};
you're calling isColliding before it's been defined, it's the same that would happen in the following code snippet:
let x = y * 2
let y = 42
you need to either declare isColliding before you call it. Or use of a function declaration
function isColiiding(div1, div2) { ... }
rather than a function expression
let isColliding = function (div1, div2) { ... }

Why aren’t my two values comparing correctly?

I have objects that I am trying to compare their values and make a function happen if one value is greater than the other. But My code seems to be wrong because even though the value is greater than the other, it still console logs "false" instead of true. I even console logged the value to check if it was greater and it was. Here's my code.
var clicks1 = 0;
function onClick() {
clicks1 += 1;
document.getElementById("clicks1").innerHTML = clicks1;
document.getElementById("clicks1").value = clicks1;
var clicks2 = 0;
function onClick() {
clicks2 += 1;
document.getElementById("clicks2").innerHTML = clicks2;
document.getElementById("clicks2").value = clicks2;
playbutton.onclick = function () {
const click1var = document.getElementById("clicks1").value
const click2var = document.getElementById("clicks2").value
console.log(click1var)    //Console shows 3
console.log(click2var)    //Console shows 1
if (click1var < click2var) {
console.log('true')
} else {
console.log('false')  //Console shows false.  
}
}
click1var is 3
click2var is 1
if (click1var < click2var){ // are you asking if 3 is lower than 1 (FALSE)
then it will go to the else.

Array not being updated after switching indexs

I'm working on a visualizer for sorting algorithms. Everything is working as intended until I got to the Selection Sort. I understand that the Selection sort will make a pass and search for the MINIMUM value and then swap that the index that it started at in the array. However, each time it makes a pass, the i value doesn't change. I tested it by changing the color of the block the i index represents in my loop and it never changes, so the MINIMUM value just keeps switching to where ever the i is.
You can view my project here on GitHub Pages, just use the left Navbar to choose Selection Sort and you can see the problem I'm having. The bottom snippet is my swap function, it didn't do this with any of the other sort methods, only the selection sort.
Github Pages -- https://kevin6767.github.io/sorting-algorithm-visualization/
Selection function
async function selectionSort() {
let blocks = document.querySelectorAll('.block');
for (let i = 0; i < blocks.length; i++) {
// Assume a minimum value
let min = i;
for (let j = i + 1; j < blocks.length; j++) {
blocks[j].style.backgroundColor = '#FF4949';
blocks[min].style.backgroundColor = '#13CE66';
blocks[i].style.backgroundColor = 'orange';
await new Promise((resolve) =>
setTimeout(() => {
resolve();
}, frame_speed)
);
const value1 = Number(blocks[j].childNodes[0].innerHTML);
const value2 = Number(blocks[min].childNodes[0].innerHTML);
if (value1 < value2) {
blocks[min].style.backgroundColor = '#58B7FF';
min = j;
}
blocks[j].style.backgroundColor = '#58B7FF';
}
if (min !== i) {
let tmp = blocks[i];
blocks[i] = blocks[min];
blocks[min] = tmp;
await swap(blocks[i], blocks[min]);
blocks = document.querySelectorAll('.block');
}
// Swap if new minimun value found
blocks[i].style.backgroundColor = '#58B7FF';
}
}
Swap function
function swap(el1, el2) {
return new Promise((resolve) => {
const style1 = window.getComputedStyle(el1);
const style2 = window.getComputedStyle(el2);
const transform1 = style1.getPropertyValue('transform');
const transform2 = style2.getPropertyValue('transform');
el1.style.transform = transform2;
el2.style.transform = transform1;
// Wait for the transition to end!
window.requestAnimationFrame(function () {
setTimeout(() => {
container.insertBefore(el2, el1);
resolve();
}, 300);
});
});
}
I ended up fixing it. It seems that I had to take the Nodelist array I was getting from just .querySelectorAll and convert that into an array using .Arrayfrom() which was pretty simple after some googling. From then on I needed to figure out how to update the array each pass, which once again was as simple as just moving one index from another.
The interesting part of the answer was how I was going to update the Nodelist itself that way all my css code would still work (This is a sorting visualizer, so it would show you what element it was on and highlight it with a color). The answer however was right in front of me. Even though I turned the Nodelist array into a regular array, I was still able to apply styles to it. This meant I didn't have to mutate the Nodelist array at all and was just able to keep a seperate array within the function to work with.
PS. The algorithm did have a lot of trouble in the above snippet because I was comparing 2 strings in the if statement (value1 and value2) this is what caused a lot of the actual algorithm erroring and was simply fixed by adding a Number() function around my innerhtml code.
Selection
async function selectionSort() {
let blocks = document.querySelectorAll('.block');
let convertedBlocks = Array.from(blocks);
let len = convertedBlocks.length;
for (let i = 0; i < len; i++) {
let min = i;
for (let j = i + 1; j < len; j++) {
convertedBlocks[j].style.backgroundColor = 'red';
convertedBlocks[min].style.backgroundColor = 'green';
convertedBlocks[i].style.backgroundColor = 'orange';
await new Promise((resolve) =>
setTimeout(() => {
resolve();
}, frame_speed)
);
if (
Number(convertedBlocks[min].childNodes[0].innerHTML) >
Number(convertedBlocks[j].childNodes[0].innerHTML)
) {
convertedBlocks[min].style.backgroundColor = '#58B7FF';
min = j;
}
convertedBlocks[j].style.backgroundColor = '#58B7FF';
}
if (min !== i) {
let tmp = convertedBlocks[i];
convertedBlocks[i] = convertedBlocks[min];
convertedBlocks[min] = tmp;
await swap(convertedBlocks[i], convertedBlocks[min]);
}
convertedBlocks[min].style.backgroundColor = '#58B7FF';
convertedBlocks[i].style.backgroundColor = '#58B7FF';
}
}

JS Class property "is undefined" even tho it appears to be defined

I'm quite new to JS and I'm trying to do a TicTacToe game with a custom size board in an attempt to learn a bit more. I first coded just a 3x3 version and started building up from there.
Right as I got past the point where I have a custom grid size entered right after loading the page and the grid rendering, I started getting the same problem when trying to click any cell to try and play a turn.
"this.game_state[clicked_cell_i] is undefined".
I have tried opening up F12 and checking if the game_state array (which is a 2d array of strings that tracks which cell is played and which isn't) but when I do everything seems normal and the array gets printed out without problem. (picture showcases printing out the game_state array in a 4x4 grid) https://i.stack.imgur.com/gV0pY.png
I would really appreciate it if somebody could explain to me what's happening or even better - help me fix it. Thanks :)
Code: https://jsfiddle.net/z8649pxL
class game {
status_display;
is_game_active;
curr_player;
game_state;
constructor() {
this.status_display = document.querySelector('.status');
this.is_game_active = true;
this.curr_player = "X";
this.game_state = matrix(rows, rows, "");
}
cell_played(clicked_cell, clicked_cell_i, clicked_cell_j){
this.game_state[clicked_cell_i][clicked_cell_j] = this.curr_player;
clicked_cell.innerHTML = this.curr_player;
if(this.curr_player === "X"){
document.getElementById((i*rows)+j).style.backgroundColor = "#ff6600";
} else if(this.curr_player === "O"){
document.getElementById((i*rows)+j).style.backgroundColor = "#33ccff";
}
}
cell_click(clicked_cellEvent){
debugger
const clicked_cell = clicked_cellEvent.target;
let clicked_cell_i = parseInt(clicked_cell.getAttribute('i'));
let clicked_cell_j = parseInt(clicked_cell.getAttribute('j'));
if(this.game_state[clicked_cell_i][clicked_cell_j] !== "" || !this.is_game_active) {
return;
}
this.cell_played(clicked_cell, clicked_cell_i, clicked_cell_j);
this.res_validation();
}
};
let ex_game = new game();
function create_grid() {
document.getElementById('hidestart').style.display = "none";
var Container = document.getElementsByClassName("grid");
Container.innerHTML = '';
rows = prompt("n?");
let i = 0, j = 0;
document.documentElement.style.setProperty("--columns-row", rows);
for (i = 0; i < rows ; i++) {
for(j = 0; j < rows; j++){
var div = document.createElement("div");
div.className = "cell";
div.id = (i*rows)+j;
div.setAttribute("cell-index", (i*rows)+j);
div.setAttribute("i", i);
div.setAttribute("j", j);
let wrapper = document.getElementsByClassName("grid");
wrapper[0].appendChild(div);
}
}
document.querySelectorAll('.cell').forEach(cell => cell.addEventListener('click', (e) => {
ex_game.cell_click(e);
e.stopPropagation();
}));
document.getElementById('hidestart').style.display = "block";
}
function matrix(rows, cols, defaultValue){
var arr = [];
// Creates all lines:
for(var i=0; i < rows; i++){
// Creates an empty line
arr.push([]);
// Adds cols to the empty line:
arr[i].push(new Array(cols));
for(var j=0; j < cols; j++){
// Initializes:
arr[i][j] = defaultValue;
}
}
return arr;
}```
When you call
new game()
the constructor is been called of that class.
And in that constructor you are calling matrix which requires the value of rows.
But initially the class do not have any value.
So the state is not getting initialized at that point of time.
That why when you use the array, it is getting undefined.
So to resolve this issue, you could just try to get the object of class game after getting the rows from user, and pass that rows in the arguments when calling the constructor of the class.
let ex_game = new game(rows);
And then using this argument call the function matrix.
Edit:
I have looked into your code.
The error is in the method called cell_played. you are using i and j which are not known to it. Please replace your code with following line. This would resolve your error.
cell_played(clicked_cell, clicked_cell_i, clicked_cell_j){
this.game_state[clicked_cell_i][clicked_cell_j] = this.curr_player;
clicked_cell.innerHTML = this.curr_player;
if(this.curr_player === "X"){
document.getElementById((clicked_cell_i*rows)+clicked_cell_j).style.backgroundColor = "#ff6600";
} else if(this.curr_player === "O"){
document.getElementById((clicked_cell_i*rows)+clicked_cell_j).style.backgroundColor = "#33ccff";
}
}
And also remove the parameter rows from constructor and just call the constructor after taking the number of rows from user.
let ex_game
function create_grid() {
/* document.getElementById('hidestart').style.display = "none" */
var Container = document.getElementsByClassName("grid");
Container.innerHTML = '';
rows = prompt("n?");
let i = 0, j = 0;
ex_game= new game().........

JavaScript Variable in method modifying object's attribute?

I'm trying to create a poker game in JavaScript. I thought the function that tested flushes worked perfectly until I displayed the hand AFTER the method ran. If there is a flush, this function is supposed to show the user's full hand, and then only the flush underneath it. Instead, it shows only the flush, and then the flush again underneath it:
var $ = function (id) { return document.getElementById(id); };
var test = function() {
var deck = new POKER.Deck(); //creates deck
var hand = new POKER.Hand(); //creates hand
//----------------TEST HAND UNTIL FLUSH-----------------
while (hand.getValue() != POKER.HAND_TYPE.FLUSH) {
deck.refreshDeck(); //refresh deck with new cards
for (var i = 0; i < 7; ++i) { //populate hand
hand.addCard(deck.dealCard());
}
console.log(hand.size() + " before"); //only for debugging. Prints "7 before"
hand.testFlush();
console.log(hand.size() + " after"); //only for debugging. Result unexpected
if (hand.getValue() == POKER.HAND_TYPE.FLUSH) { //if hand has a flush
for (var j = 0; j < hand.size(); j++) { //display full hand
var img = document.createElement("img");
var card = hand.getCardAtIndex(j);
img.src = card.getImage();
$("images").appendChild(img);
}
for (var k = 0; k < 5; k++) { //display flush hand
var img2 = document.createElement("img");
var card2 = hand.getValueCardAtIndex(k);
img2.src = card2.getImage();
$("handImg").appendChild(img2);
}
break;
} else {
hand.empty();
}
}
};
window.onload = function() {
test();
};
The second console.log statement prints out "4 after" until the testFlush method detects a flush, and the final result is "5 after".
testFlush method:
POKER.Hand.prototype.testFlush = function() {
//first, sort cards by rank so that the highest flush is
//taken if there are more than five cards of the same suit
this.sortByRank();
this.sortBySuit();
var tempHand = this.cards; //modifiable version of this.cards
var NUM_OF_TESTS = 3; //only 3 loops required to test for all possible flushes
var LAST_CARD_INDEX = 4; //represents the fifth card, or index 4
var MAX_CARDS = 5; //maximum cards possible in a hand (valueCards)
for (var i = 1; i <= NUM_OF_TESTS; i++){
//check if 1st and 5th cards are the same suit
if(tempHand[0].getSuit() == tempHand[LAST_CARD_INDEX].getSuit()){
this.value = POKER.HAND_TYPE.FLUSH;
while(tempHand.length != MAX_CARDS){ //remove last card in tempHand until there are only five cards
tempHand.pop();
}
this.valueCards = tempHand;
}else{
tempHand.splice(0,1); //removes first card from the temporary hand
}
}
};
All "hand.size()" does in the test function is "return this.cards.length". So what I don't understand is how the testFlush method could be altering the object attribute "this.cards" when it only alters the temporary variable tempHand.
Hand object:
POKER.Hand = function(){
this.cards = [];
this.value; //integer that corresponds to the POKER.HAND_TYPE
this.valueCards = []; //array of the five cards that corresponds only to this.value
};
Hand.size method:
POKER.Hand.prototype.size = function() {
return this.cards.length;
};
The problem is this line:
var tempHand = this.cards; //modifiable version of this.cards
Assigning an array or object to a variable does not make a copy of it. The variable is a reference to the same array, so tempHand.pop() modifies this.cards as well. You can make a copy of an array with .slice():
var tempHand = this.cards.slice();

Categories

Resources