Variable set to HTML object changes with the object - javascript

I am making a simple chess game in Vanilla JS, and am trying to implement an undo move function where, when you press a button, you can take back your move. There are currently two buttons, one to undo your move, and the other to submit your move and pass the game over to your opponent. Simple functionality of most chess games.
Right now the way this works is by assigning a variable to the dynamically generated 8 by 8 board HTML Object before the player makes a move (so let previousState = board where board is const board = document.getElementById("board")). So, before the player even touches the game, the current state of the board is saved.
Here's the JS:
function drawBoard() {
// Two loops one for even rows one for odd inside of another loop so all rows are drawn
let isWhite = false
let idSwitch = 0
for (let p = 1; p <= 4; p++) {
let oddRow = document.createElement("div")
let evenRow = document.createElement("div")
for (let i = 1; i <= 8; i++) {
idSwitch++
let square = document.createElement("div")
square.classList.add("square")
square.setAttribute("id", `square${idSwitch}`)
if (isWhite) {
isWhite = false
} else {
square.classList.add("black")
isWhite = true
}
oddRow.appendChild(square)
}
board.appendChild(oddRow)
for (let q = 1; q <= 8; q++) {
idSwitch++
let square = document.createElement("div")
square.classList.add("square")
square.setAttribute("id", `square${idSwitch}`)
if (isWhite) {
square.classList.add("black")
isWhite = false
} else {
isWhite = true
}
evenRow.appendChild(square)
}
board.appendChild(evenRow)
}
}
And the HTML:
<section id="board-container">
<div id="board"></div>
</section>
for reference.
Then, when the player makes a move, the variable is supposed to stay exactly the same, so that, if they want to revert to the original, the variable is still there for them.
So, after they move, they have two options. Either pass the move to their opponent, at which point the previousState variable gets reassigned to the new state of the board (i.e. How the board is after the aforementioned move), or, and herein lies the rub, they can press undo, and the board will revert to how it was before they made their turn (i.e. What the board var is set to).
Simple right? You assign a var before they go and then revert the board back to it if they want to take the move back. Otherwise, reset the var to how the board looks after they go. Rinse and repeat.
The only problem is that, for some reason, the variable changes somewhere between when it is initially defined and after the player moves. What happens is that the player goes, and the variable gets logged to the console. It has the parent element board and then all the rows and squares, and then all the pieces in their correct posistions in the squares. Then the player moves, and the variable is logged out again, but now that the board element, now that the actual HTML Object, is different, and, for some reason, the variable, which was never reassigned, mirrors how the board now looks. I do not understand why, as it should just stay the same, and not update in tandem with the board. Why does it?
Here's a codepen to check it out yourself:
https://codepen.io/jacklouden/pen/qBaLPdo.
Thank you!

Are you familiar with the concepts of variables being by reference or by value? I'm guessing that's your issue.
There is a good SO post on it already.
JavaScript by reference vs. by value
I'll take a direct quote from that post to answer this here.
"
Javascript is always pass by value, but when a variable refers to an object (including arrays), the "value" is a reference to the object.
Changing the value of a variable never changes the underlying primitive or object, it just points the variable to a new primitive or object.
However, changing a property of an object referenced by a variable does change the underlying object."
Try making a copy of your html object and hiding it. I didn't deep dive into what you're doing, but you are doing DOM manipulation, you need to create a new DOM element containing the state. This should give you the reset capability you are looking for.

Related

Recursive Reduce method in ES6 / Immutable

Hope this isn't too difficult a question without context, but here goes nothing. So, I inherited this code from someone, and I can't seem to get it to work!
We're making a Go game. We want to scan a set of pieces on the board and see if they're empty or not. An empty square is called a 'liberty'. Now, at the bottom of the function there we're creating a new 2D array 'visitedBoard' that keeps track of where we've scanned so far.
PROBLEM, the current implementation allows for liberties to be scanned twice! It only seems to be marking something as 'visited' in the board when it is either empty or another color (0), not when it's 1.
BTW, at the bottom - we're iterating through neighbors, which is a 4 item array of objects {row: 2, col: 3} and then recursively running it through this function.
Any assistance is helpful. I'm new to this functional / immutable business.
const getLiberties = function (board, point, color) {
if (board.get(point.row).get(point.col) === C.VISITED) {
return 0; // we already counted this point
} else if (board.get(point.row).get(point.col) === C.EMPTY) {
return 1; // point is a liberty
} else if (board.get(point.row).get(point.col) !== color) {
return 0; // point has an opposing stone in it
}
const neighbours = getNeighbours(board, point)
const visitedBoard = board.setIn([point.row, point.col], C.VISITED)
return neighbours.reduce(
(liberties, neighbour) => liberties + getLiberties(visitedBoard,
neighbour, color), 0)}
instead of .get(point.row).get(point.col) you can use .getIn([point.row, point.col])
inside reduce you always use same visitedBoard for all calls. You have to re-assign new value to variable after reduce callback call

Weird twice execution in the double for loop in JavaScript

I got this weird bug, which is not 100% reproducible, for like half a week and still cannot figure it out. Hopefully someone can give me some instruction.
So I'm building this Tetris game. In this well grid, which is actually a 2d array, whenever the tetromino drops at the very bottom(it hits another tetromino or the boundary of the well), I transfer the grid of that, which contains four square blocks, into the well grid. Normally it behaves well, but sometimes after the game, the shape doesn't look correct anymore.
And here is the function with comment:
function transferTetroGridIntoWell({ grid, tetroGrid, tetroPosition, color }) {
let newGrid = [...grid]
let relativeX, relativeY
// the tetroGrid could be a 2d array like:
// [1,1,0]
// [1,1,0], in which 1 indicates a block, and 0 is none
for (let row = 0; row < tetroGrid.length; row++) {
for (let col = 0; col < tetroGrid[0].length; col++) {
if (!tetroGrid[row][col]) continue
// the index of the array relative to the well grid
relativeX = tetroPosition.x + col
relativeY = tetroPosition.y + row
// assign the color to the according square block in the well grid
newGrid[relativeY][relativeX] = color
}
}
return newGrid
}
Here is the problem:
Since each of the tetromino contains only 4 square blocks, the newGrid[relativeY][relativeX] = color should only be executed for four times, which is true from what it looks like in the debugger. But sometimes it sometimes look like this assignment gets executed twice before the it is called again.
Here is the debug screenshot:
before execution:
1st time after execution:(and this is where the weird thing happens, there are two #f6d42b inserted into the well, not only grid8, but also grid7)
2nd time after execution:(still double execution)
3rd time after execution:
4th time after execution:
Four times of execution inserted 6 square blocks. How could that happen??!!
Full source code: https://github.com/thomasyimgit/Tetris
REALLY APPRECIATED for whoever finished reading this long post!!
Turns out it's all about mutating the data.
Using the spread operator to copy an array is only a shallow copy. Thus, it's possible that two rows are referencing to the same row of the grid. And when you do the assignment, the two rows update simultaneously, which seems like double execution.
I modify the first line in the transferTetroGridIntoWell function to this and it fixes the problem:
let newGrid = grid.map(r => r.map(c => c))
MUTATING is EVIL.

How to modify object from updates and remove old keys?

I am creating a multiplayer game and I have an object in javascript, with a number of keys and values.
This object is called players, for holding information about each player that is connected to the game server.
name is the key of the object, and then the value of the object is a Player object which holds information such as x, y, level, etc.
Constantly I am sending a request to the server to get updated information about the players.
Because this is happening very often, I don't want the players object to be reset every time (players = {}), so instead, I am updating the object with any new information.
At the moment I am checking if name in players, and if so, I update the object like this:
players[name].x = x;
players[name].y = y;
// etc.
Otherwise, I simply create a new Player object, with the information and add it to the players object. (If a new player connected for instance)
The problem is, if a player that is already in players is no longer in the updated information from the server (i.e the player disconnected), how do I go about removing them from the object.
Is it necessary to loop trough players, and if the player is no longer in the updated information, remove it from the object, or is there any simpler way of doing this?
If there is no other way, is it a better approach to just reset the dictionary and add the data? It feels like that isn't the best way to do something simple like this.
Here is my code so far:
var newplayers = new info from server;
for(var i=0; i<newplayers.length; i++)
{
var pl = newplayers[i];
var name = pl.name;
var x = pl.x;
var y = pl.y;
// etc.
if(name in players)
{
players[name].x = x;
players[name].y = y;
// etc.
} else
newplayer = new Player();
newplayer.x = x;
newplayer.y = y;
// etc.
players[name] = newplayer;
}
}
// What if the player is no longer in the updated info, but still in players?
All help appreciated! Thanks in advance!
So you have a choice between removing outdated data from your players dictionary or rebuild it from scratch every time?
I think the answer depends a lot on how much data you have. If you have at most 20 players, it probably doesn't matter too much. If you have 1 million players it's different.
If you want to be sure, the best thing to do would be to measure it. Try both solutions with the biggest number of players you want to be able to handle and see what the impact on performance is.
Or just go with the simplest implementation and see if it's good enough for your purpose. No point in optimising before you need it.
Personally I'd just loop through players to remove the outdated data. If the performance is not good enough, then I'd optimise.

How to make a Simon Js game based on arrays?

Im writing a js simple simon game and im clueless on how to do it.
I know that :
I need to create two arrays, and a level(score) variable
A randomly generated number from 1 to 4 (inclusive) needs to be added
to the first array, When one of four buttons is pressed, the value
of it is added to the second array, if the second array is not the
same size or bigger than the first array. Each time a value is added
to the second array, check that the value is equal to the value in
the same position in the first array, if not, clear both arrays, and
set levelvar to 1, and alert "gameover" This means if you get one
wrong, you cannot continue. If the length of the second array
matches the level variable, add a random number to array one, clear
array two, increment levelvar.
But, I am clueless in aspect to the code.
My Jsfiddle :http://jsfiddle.net/jbWcG/2/
JS:
var x = []
var y = []
var levelvar = 1
document.getElementById("test").onclick= function() {
document.getElementById("test").innerHTML=x
};
document.getElementById("button1").onclick= function() {
x.push("Red")
};
document.getElementById("button2").onclick= function() {
x.push("Green")
};
document.getElementById("button3").onclick= function() {
x.push("Yellow")
};
document.getElementById("button4").onclick= function() {
x.push("Blue")
};
HTML:
<button id="button1">Red</button><br />
<button id="button2">Green</button><br />
<button id="button3">Yellow</button><br />
<button id="button4">Blue</button><br />
<p id="test">Click To see What you have clicked</p>
How would I make a two arrays see if a certain value is the same?
Lets say, that the generated array is : [1,2,3,4,1,2,3]
and i am at position 5 and i press 2, how would i check that the two numbers match?
Thanks in advance
The easiest way to check one at a time that position i of your array is x is
if (gen_arr[i] == x) {
// matches
} else {
// doesn't match
}
So if you conceptualize the flow of your game, you're going to want to, at each button press:
somehow keep track of which index they are on (maybe have a counter that increments with each button press)
checks if gen_arr[i] == x (and displays game over if it doesn't).
Alternatively, instead of keeping track of which index, you can call gen_array.shift() to get the first item in gen_array AND delete it from the array, in a flow kind of like this:
var gen_array = [1,2,3,4,1];
function press_button(button_pressed) {
var supposed_to_be = gen_array.shift();
// at this point, on the first call,
// supposed_to_be = 1, and gen_array = [2,3,4,1]
if (supposed_to_be != button_pressed) {
// game over!
} else {
// you survive for now!
if (gen_array.length() == 0) {
// gen_array is empty, they made it through the entire array
// game is won!
}
}
}
While that represents the general "what to check" at every step, using this verbatim is not recommended as it quickly leads to an unstructured game.
I recommend looking into things called "game state" diagrams, which are basically flow charts which have every "state" of the game -- which in your case, includes at least
"displaying" the pattern
waiting for button press
checking if button press is correct
game over
game won
And from each state, draw arrows on "how" to transition from one state to the next. You can do a google search to see examples.
Once you have a good game state diagram/flow chart, it's easier to break down your program into specific chunks and organize it better ... and you can usually then see exactly what you need to code and what is missing/what is not missing.

Minesweeper game - Maximum call stack side exceeded

So I'm making a minesweeper game in JS.
I have this function:
function doSquare(x, y) { //takes x,y coordinates of a square. checks and acts accordingly to what's around it
var revealed = [];
var size = board.length;
var mines = countAround(x,y);
table.rows[x].cells[y].innerHTML = mines;
if (mines === 0) {
for (var i=Math.max(0,x-1), l = Math.min(x+1, size-1); i<=l; i++) {
for (var j=Math.max(0, y-1), k = Math.min(y+1, size-1); j<=k; j++) {
if (x == i && y==j) {continue;}
if (revealed.indexOf(i+"."+j) === -1) {
doSquare(i, j);
revealed.push(i+"."+j);
}
}
}
}
}
The board's rows and cols are equal. countAround(x,y) returns the amount of mines around (x,y); revealed is an array which stores which squares have already been dealt with, to prevent dealing with them again.
This function is supposed to, when a square is clicked, reveal the number of mines near it and write it into the cell. Then, it checks every square around it, and if that square hasn't already been dealt with (if it isn't in the revealed array), the function doSquare() runs on it again. The function will not 'spread' from a square if the square has any mines next to it.
I get an error: maximum call stack size exceeded. But the function stops its 'spreading' upon reaching a square with mines, and also doesn't run on a square which already has been taken care of. So I would love an explanation as to why this happens.
I think the problem is that 'revealed' is defined inside your function. This means that each time the function is called, a new 'revealed' is created locally for the function. Therefore, a square with no mines around it will call doSquare for an adjacent square, which may in turn call doSquare on the original square. However, doSquare won't remember that it has already checked this square as a new local version of 'revealed' is created for this call.
Solution:
Either pass 'revealed' as an argument to doSquare so all calls use the same variable (i.e. function doSquare(x, y, revealed){..., making the initial call as doSquare(x, y, []);, or declare 'revealed' outside of doSquare, and empty it each time you wish to check for mines.

Categories