Minesweeper game - Maximum call stack side exceeded - javascript

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.

Related

How do I implement minimax with the chess.js node module

I'm currently working on creating a chess engine using chess.js, chessboard.js, and the minimax algorithm. I eventually want to implement alpha-beta, but for right now, I just want to get minimax to work. It seems like the computer is thinking, but it usually just does Nc6. If I move the pawn to d4, it usually takes with the knight, but sometimes it just moves the rook back and forth in the spot that was opened up by the knight. If there is nothing for the knight to take, the computer moves the Rook or some other pointless move. My best guess is that all of the moves are returning the same valuation, and so it just makes the first move in the array of possible moves, hence the top left rook being a prime target. I should note that part of my confusion is around the way a recursive function works, and most of the stuff I've found online about recursive functions leaves me more confused than when I started.
I'm using Express.js with the chessboard.js config in public/javascripts as a boardInit.js that's included in the index.ejs folder, and when the user makes a move, a Post request is sent to /moveVsComp. It sends it to the server, where the app.post function for /moveVsComp tells chess.js to make the move that the player made.
After the player move is recorded, the computer calls the computerMoveBlack function.
Function call in the post request:
let compMove = computerMoveBlack(3);
game.load(currentFen)
game.move(compMove)
res.status(200).send({snapback: false, fen: game.fen()})
computerMoveBlack Function:
function computerMoveBlack(depth) {
let bestMove = ['', 105];
for (let move of game.moves()) {
game.move(move)
let value = minimax(move, depth-1, false)
if (value < bestMove[1]) {
bestMove = [move, value]
}
game.undo()
}
console.log(bestMove[0])
return bestMove[0]
}
This function loops through all of the moves, and I was using this because it seemed like this was the best way to keep the best move instead of just returning a valuation of the current position.
Minimax Function:
function minimax(node, depth, maximizingPlayer) {
let value = maximizingPlayer ? -105 : 105
if (depth === 0 || game.game_over()) return getValuation()
if (maximizingPlayer) {
for (let move of game.moves()) {
game.move(move)
value = Math.max(value, minimax(move, depth-1, false))
game.undo()
}
return value
} else {
for (let move of game.moves()) {
game.move(move)
value = Math.min(value, minimax(move, depth-1, true))
game.undo()
}
return value
}
}
getValuation Function:
function getValuation() {
let evalString = game.fen().split(' ')[0];
let score = 0;
score += (evalString.split('r').length -1) * -5 || 0;
score += (evalString.split('b').length -1) * -3 || 0;
score += (evalString.split('n').length -1) * -3 || 0;
score += (evalString.split('q').length -1) * -9 || 0;
score += (evalString.split('p').length -1) * -1 || 0;
score += (evalString.split('R').length -1) * 5 || 0;
score += (evalString.split('N').length -1) * 3 || 0;
score += (evalString.split('B').length -1) * 3 || 0;
score += (evalString.split('Q').length -1) * 9 || 0;
score += (evalString.split('P').length -1) || 0;
return score;
}
I should note that I understand using a FEN in the valuation is very slow for this use case, but I'm not really sure what a better alternative would be.
Just as kind of a recap of the questions, I'm trying to figure out why it just makes the first move in the array every time, what is wrong with the format of my functions, and what a better way to get the valuation of a position is as opposed to string manipulation of the FEN.
I will point out a few suggestions below to help you on the way if you are just getting started. First I just want to say that you are probably right that all moves get the same score and therefore it picks the first possible move. Try to add some Piece Square Tables (PST) to your Evaluation function and see if it puts pieces on appropriate squares.
I would implement a Negamax function instead of Minimax. It is way easier to debug and you won't have to duplicate a lot of code when you later make more optimizations. Negamax is one of the standard chess algorithms.
It seems like you don't do the legal move generation yourself, do you know how the board is represented in the library that you use? Instead of using the FEN for evaluation you want to use the board (or bitboards) to be able to do more advanced evaluation (more on it further down).
The min/max value of -105/105 is not a good way to go. Use -inf and inf instead to not get into troubles later on.
Regarding the evaluation you normally use the board representation to figure out how pieces are placed and how they are working together. Chessprogramming.org is a great resource to read up on different evaluation concepts.
For your simple starting evaluation you could just start with counting up all the material score at the beginning of the game. Then you subtract corresponding piece value when a piece is captured since that is the only case where the score is changing. Now you are recalculating lots of things over and over which will be very slow.
If you want to add PST to the evaluation then you also want to add the piece value change for the moving piece depending on the old and new square. To try and sum up the evaluation:
Sum up all piece values at start-up of a game (with PST scores if you use them) and save it as e.g. whiteScore and blackScore
In your evaluation you subtract the piece value from the opponent if you capture a piece. Otherwise you keep score as is and return it as usual.
If using PST you change the own score based on the new location for the moved piece.
I hope it makes sense, let me know if you need any further help.

Variable set to HTML object changes with the object

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.

variable changes when it shouldn't

Here is what I am trying to do with this code below. I am receiving the position of an enemy every six frames from the server.
The client-side enemies are stored in an array (called enemies)as objects and these enemy objects have position vectors as a property (called pos), that has an x and y. There is also another position vector (called previousPos) that stores the second most recent position the enemy was at. I am doing this so I can lerp between these two positions so the game can consume less bandwidth while still being smooth.
data[i].x contains the x position that the server sends out to the client every 6 frames, and I am attempting to set the position of the enemy (with pos) to what the server sent out while keeping the enemy's previous position (with previousPos) so I can lerp between them.
This is the function I run whenever the server sends a message regarding enemy stats:
for (var i = 0; i <= enemies.length - 1; i++) { //iterates through all enemies
testerino++; //used to ensure what iteration this is on
if (data[i].x != undefined || data[i].y != undefined) {
console.log(enemies[i].pos.x,enemies[i].previousPos.x,data[i].x, testerino);
if (data[i].x != enemies[i].pos.x) enemies[i].previousPos.x = enemies[i].pos.x; //proven they are different
if (data[i].y != enemies[i].pos.y) enemies[i].previousPos.y = enemies[i].pos.y;
console.log(enemies[i].pos.x,enemies[i].previousPos.x,data[i].x, testerino);
}
if (data[i].x != undefined) enemies[i].pos.x = data[i].x;
if (data[i].x != undefined) console.log(enemies[i].pos.x,enemies[i].previousPos.x,data[i].x, testerino);
if (data[i].y != undefined) enemies[i].pos.y = data[i].y;
}
In the console it returns:
(first number is pos.x, second: previousPos.x, third: what loop we are on)
sketch.js:213 587.8102829077167 587.8102829077167 567.8102829082868 225
sketch.js:216 587.8102829077167 587.8102829077167 567.8102829082868 225
sketch.js:219 567.8102829082868 567.8102829082868 567.8102829082868 225
Notice that between line 216 and 219 only enemies[i].pos.x is assigned to, and yet somehow enemies[i].previousPos.x was also changed as well! Even though I never assigned anything to it!
Any ideas to what may have went wrong?
Earlier in the code I did indeed do:
this.previousPos = this.pos;
within the constructor of the enemy function, not knowing that javascript has something called referencing. This means that previousPos was used as a nickname for position, so all changes to pos happenned to previousPos too, as they were the same variable. The library I used had a vector copy method (now i know why its there...) and I think I replaced that code with:
this.previousPos = this.pos.copy()
as to ensure that previousPos was made into a clone, and not a nickname to the same variable as assignment through the equals sign would've done.

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.

Javascript: Simple Particle Motion, Particle Elastically Bouncing Off Other Particle

I've created this rather simple javascript; balls or 'molecules' moving around the screen. I was hoping to add to the functionality that when one ball comes into contact with another, they swap velocities. We don't need to worry about any angles, just when they come into contact with each other, the velocities swap. (Instead of changing the velocities though, in the code linked I've just coded a colour change)
I've been trying to call the function 'someplace' to recognise when the molecules touch, but I've had no luck with that. I don't really understand why.
Link to code:
http://jsbin.com/arokuz/5/
There seems to be three main problems:
The molecules seem to be randomly changing, rather than when two molecules touch.
When one sets the array to have say, 3 molecules, only two appear, the first is actually there, but unresponsive to .fillstyle changes, so invisible against the canvas
With the function method I would only be able to recognise when molecules in series (1 and 2 or 4 and 5) in the array touch...how could I check all the molecules?
You are only comparing a molecule with 2 other ones, which in fact might be anywhere.
Collision detection is a topic quite hard to solve, but if you want to have your idea
working quickly you might go for a n^2 algorithm with 2 nested for loops.
the code is quite expected :
// collision
for(var t = 0; t < molecules.length-1; t++)
for(var tt = t+1; tt < molecules.length; tt++) {
var p1 = molecules[t];
var p2 = molecules[tt];
if (sq(p1.x-p2.x) +sq(p1.y-p2.y) < sq(p1.radius+p2.radius) )
{
p1.collided = 8; // will diplay for next 8 frames
p2.collided = 8; // .
}
}
the fiddle is here :
http://jsbin.com/arokuz/10
The reason only two appear when three are made isn't because the first one doesn't render it is rather the last one doesn't, this is because of how you draw them by comparing its distance with the next one in the list - as it is the last there is no next and thus throws a null error and continues (check the console).
The reason why they seem to "randomly" detect collisions or not is because they are not checking against all other molecules - only the next in the list, unfortunately the only simply way to do it would be to go through all other balls for every ball and checking.
To get the molecules to detect distance you could use the pythagorean theorem, I typically use it such as:
var distx = Math.abs(molecule1.x - molecule2.x);
var disty = Math.abs(molecule1.x - molecule2.y);
var mindist = molecule1.radius + molecule2.radius;
return Math.sqrt(distx*distx+disty*disty) < mindist;

Categories