I have a fiddle with the following code. ColorWalk clone
Here is the js code:
function floodFill(x, y, selectedColor, grayColor) {
if (x < 0 || x >= 600) return;
if (y < 0 || y >= 400) return;
let square = $('.blockattribute').filter(function(ind, el) {
return $(el).css('left') == x + 'px' && $(el).css('top') == y + 'px'
});
let squareColor = square.css('background-color');
if (squareColor === grayColor || squareColor === selectedColor) {
square.removeClass().addClass('blockattribute gray');
floodFill(x + 20, y, selectedColor, grayColor);
floodFill(x, y + 20, selectedColor, grayColor);
floodFill(x - 20, y, selectedColor, grayColor);
floodFill(x, y - 20, selectedColor, grayColor);
}
else {
return;
}
}
I've been working on learning javascript/jquery and algorithms and I've pretty much have this clone working except for the fact that when I get deeper and deeper into the grid, the slower and slower the code is. I've been reading about memoization and trying to use it on the grid but I'm getting stuck as to how to approach. All I'm really looking for is a little push regarding how to do this. Maybe memoization isn't the way to go and maybe I can optimize my code some other way. My current thinking is that I need to grab the last gray square and then proceed from there. Am I on the right track?
----Edit------
I realized that I can combine the if/else operator to check for matching gray color or selected color
Reading from and writing to the DOM are very expensive in Javascript. You also should never use the DOM as the source of your data.
To speed up your algorithm, store the pixel data offline as regular Javascript data, manipulate the data only, then update the visual code only once. This way you minimize the number of DOM operations.
Additionally, Javascript is not "tail call optimized," meaning you can't recurse forever, and every level of recursion will slow down the program to some degree. If you can use a non-recursive flood fill algorithm in this case, it will likely be faster.
Related
I'm a new programmer currently coding a javascript alpha-beta pruning minimax algorithm for my chess engine, using Chess.js and Chessboard.js. I've implemented a basic algorithm with move ordering. Currently, it's evaluating around 14000 nodes for 8 seconds, which is way too slow. Is there something wrong with my algorithm or are there optimizations that I haven't implemented? My algorithm can't process anything deeper than depth 4 within reasonable time constraints. Thank you.
P.S. the "tracking Eval" function just evaluates each specific move as a way to avoid doing a full evaluation of boards at leaf nodes, this optimization sped up my program by around 50%, but it's still slow right now.
function minimax(game, depth, distanceFromRoot, alpha, beta, gameEval) {//returns gameEval
if (depth === 0) {
nodeNum++;
if(game.turn() === 'b'){
return (-gameEval / 8);
}else{
return (gameEval / 8);
}
}
// run eval
var prevEval = gameEval;
var moves = game.moves();
moveOrdering(moves);
var bestMove = null;
var bestEval = null;
for (let i = 0; i < moves.length; i++) {
var gameCopy = new Chess()//dummy board to pass down
gameCopy.load(game.fen())
const moveInfo = gameCopy.move(moves[i])
var curGameCopy = new Chess()//static board to eval, before the move so we know which piece was taken if a capture occurs
curGameCopy.load(game.fen())
var curEval = trackingEval(curGameCopy, prevEval, moveInfo, moves[i]); //returns the OBJECTIVE eval for the current move for current move sequence
var evaluated = -minimax(gameCopy, depth - 1, distanceFromRoot + 1, -beta, -alpha, curEval);//pass down the current eval for that move
if (evaluated >= beta) {
return beta;
}
if (evaluated > alpha){
alpha = evaluated
bestMove = moves[i]
bestEval = evaluated;
if (distanceFromRoot === 0) {
bestEval = evaluated;
}
}
}
if(distanceFromRoot === 0){
setEval(-bestEval)
return bestMove;
}
return alpha;
}
It's hard to say what optimizations you have made and what is reasonable since we only see a small part of your code. Your evaluation can be slow, your move ordering can be slow/incorrect, and to copy the board is also slower than to make and then unmake the move.
You can find lots of advice on how to speed up your algorithm here: https://www.chessprogramming.org/Search. Chessprogramming.org is a very good resource for developing your engine in general too.
I see two quick optimizations, before going further into other classical optimization.
Do not compute the evaluation of the board except when depth = 0. I assume that you compute the whole evaluation at every step, it's very time consuming and totally unnecessary.
Do not copy the board each time. It's also time consuming. Work with one board for the whole search, in which you make and unmake moves when you are doing the search. The pseudo-code for this is:
for move in moves:
board.do(move) #the original (not a copy) board has made the move
#Alpha-beta stuff like you did
board.undo(move) #restore the board
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.
I'm working on an HTML5-canvas game, where the map is randomly generated 10px by 10px tiles which the player can then dig and build upon. The tiles are stored in an array of objects and a small map contains about 23000 tiles. My collision detection function checks the players position against all non-air tiles every run through (using requestAnimationFrame()), and it works perfectly but I feel like it's CPU intensive. Collision function is as follows (code came from an online tutorial):
function colCheck(shapeA, shapeB) {
var vX = (shapeA.x + (shapeA.width / 2)) - (shapeB.x + (shapeB.width / 2)),
vY = (shapeA.y + (shapeA.height / 2)) - (shapeB.y + (shapeB.height / 2)),
hWidths = (shapeA.width / 2) + (shapeB.width / 2),
hHeights = (shapeA.height / 2) + (shapeB.height / 2),
colDir = null;
// if the x and y vector are less than the half width or half height, they we must be inside the object, causing a collision
if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) {
// figures out on which side we are colliding (top, bottom, left, or right)
var oX = hWidths - Math.abs(vX),
oY = hHeights - Math.abs(vY);
if (oX >= oY) {
if (vY > 0) {
colDir = "t";
shapeA.y += oY;
} else {
colDir = "b";
shapeA.y -= oY;
}
} else {
if (vX > 0) {
colDir = "l";
shapeA.x += oX;
} else {
colDir = "r";
shapeA.x -= oX;
}
}
}
return colDir;
};
Then within my update function I run this function with the player and tiles as arguments:
for (var i = 0; i < tiles.length; i++) {
//the tiles tag attribute determines rendering colour and how the player can interact with it ie. dirt, rock, etc.
//anything except "none" is solid and therefore needs collision
if (tiles[i].tag !== "none") {
dir = colCheck(player, tiles[i]);
if (dir === "l"){
player.velX = 0;
player.jumping = false;
} else if (dir === "r") {
player.velX = 0;
player.jumping = false;
} else if (dir === "b") {
player.grounded = true;
player.jumping = false;
} else if (dir === "t") {
player.velY *= -0.3;
}
}
};
So what I'm wondering is if I only check tiles within a certain distance from the player using a condition like Math.abs(tiles[i].x - player.x) < 100 and the same for y, should that make the code more efficient because it will be checking collision against fewer tiles or or is it less efficient to be checking extra parameters?
And if that's difficult to saying without testing, how do I go about finding how well my code is running?
but I feel like it's CPU intensive
CPU's are intended to do a lot of stuff very fast. There is math to determine the efficiency of your algorithm, and it appears that your current implementation is O(n). If you reduce the number of tiles to a constant number, you would achieve O(1), which is better, but may not be noticeable for your application. To achieve O(1), you would have to keep an index of the X closest tiles and incrementally update the index when closest tiles change. I.e. if the player moves to the right, you would modify the index so that the left most column of tiles are removed and you get a new column of tiles on the right. When checking for collisions, you would simply iterate through the fixed number of tiles in the index instead of the entire set of tiles.
...should that make the code more efficient because it will be checking collision against fewer tiles or or is it less efficient to be checking extra parameters?
The best way to answer that is with a profiler, but I expect it would improve performance especially on larger maps. This would be an O(n) solution because you still iterate over the entire tile set, and you can imagine as your tile set approaches infinity, performance would start to degrade again. Your proposed solution may be a good compromise between the O(1) solution I suggested above.
The thing you don't want to do is prematurely optimize code. It's best to optimize when you're actually experiencing performance problems, and you should do so systematically so that you get the most bang for your buck. In other words even if you did have performance problems, the collision detection may not be the source.
how do I go about finding how well my code is running?
The best way to go about optimizing code is to attach a profiler and measure which parts of your code are the most CPU intensive. When you figure out what part of your code is too slow, either figure out a solution yourself, or head over to https://codereview.stackexchange.com/ and ask a very specific about how to improve the section of code that isn't performing well and include your profiler information and the associated section of code.
In response to Samuel's answer suggesting I use a profiler:
With a map made up of ~23 000 tiles in an array:
The original collision code was running 48% time. By changing if (tiles[i].tag !== "none") to the following the amount of time spent checking for collisions dropped to 5%.
if (tiles[i].tag !== "none"
&& Math.abs(tiles[i].x - player.x) < 200
&& Math.abs(tiles[i].y - player.y) < 200)
With a map made up of ~180 000 tiles:
The original collision code was running 60-65% of the time and performance of the game is so low it can't be played. With the updated code the collision function is only running 0.5% of the time but performance is still low, so I would assume that even though less tiles are being checked for collision there are so many tiles that checking their position relative to the player is causing the game to run slowly.
I needed to show some simple code example for friend, for example moving a button along the edge of the screen clockwise. Sure this is something very simple, no, this is simplest. But I found myself spending about almost 30 minutes on that. I was embarased, because I am professional programmer for 20+ years, and I even needed to start the program many times before the button was flying the right ways. And I was thinking, why? So I speculated that this kind of code is difficult to get right immediately, because it is very old style, each case is typed separately, each check must be entered manually, you need to go through each iteration and make carefully sure that all numbers and checks are exactly precisely correct, and this is hard to do because of the nature of code style, which is, eehh, spaghetti, messy?
So I was like, is there a way to convert it to "modern" way, like use loop instead of cases, use templates or other meta-programming, use functional approach or at least use arrays. And it seems I cannot find any good way for this.
var mx = screen.width - b.w, my = screen.height - b.h
setInterval(function() {
var step = 3
if (state == 1) {
b.x += step
if (b.x >= mx) b.x = mx, state++
} else if (state == 2) {
b.y += step
if (b.y >= my) b.y = my, state++
} else if (state == 3) {
b.x -= step
if (b.x <= 0) b.x = 0, state++
} else if (state == 4) {
b.y -= step
if (b.y <= 0) b.y = 0, state = 1
}
b.apply()
}, 1)
This is JavaScript, in C it would be even more difficult to get right fast, because you need to think about types.
Here is what I come up myself... This maybe demonstrating what I am trying to achieve. I am not talking about choosing different algorithm. but rather abotu exploring language(s) features, and programming techniques.
var D = 3, name = ['x','y'], delta = [D,D,-D,-D],
limit = [screen.width - b.w, screen.height - b.h,0,0]
setInterval(function() {
b[name[0]] += delta[0]
if (delta[0] > 0 && b[name[0]] > limit[0] || b[name[0]] <= 0)
b[name[0]] = limit[0],
[name,delta,limit].some(function(x){ x.push(x.shift()) })
b.apply()
},1)
This one at least separates data from code, making it simpler to get it right. It worked for me from the first attempt. But I am still not satisfied completely)
The average American newspaper article is written at something like a 6th grade reading level. This isn't because the person writing it never went beyond 6th grade; rather, they've learned that it's better to write in a manner that everyone can understand. I bring this up because you're calling your code childish or unsophisticated, yet it's the most clear and concise code there is for the task you had, and therefore the best.
If you want to go around a square, you're really not going to have any choice besides what you did - have four different directions to go, and keep track of where you're at. You code shows off the basics of a state machine, because that's what you need to go around in four different straight lines.
If you're willing to fake it with a slightly different movement, you can remove all states and just go around in an ellipse using some trigonometry. (It actually should be a circle, but since the screen is rectangular, you'll get an ellipse, with different speeds on the long and short sides of the screen.)
Here's the basics. It may need some tweaking to make sure you have the edges hitting the right spot. Truthfully, I think by the time you work out the special cases on this version, you'll find your solution was more elegant.
// find the center x and y
var centerX = screen.width / 2;
var centerY = screen.height / 2;
// first, find the radius. If you want to cover everything, you need half the diagonal
var radius = Math.sqrt(centerX * centerX + centerY * centerY);
var increment = 0.01; // higher values, of course, will move you faster
var theta = 0;
setInterval(function() {
b.x = Math.min(Math.max(centerX + radius * Math.cos(x), screen.width), 0);
b.y = Math.min(Math.max(centerY + radius * Math.sin(y), screen.height), 0);
theta -= increment;
b.apply();
}, 1);
As I mentioned, this will almost certainly need some tweaking to come anywhere as close to looking good as the code you made. My code may be less childish, but it's less easy to understand - and it actually does less well at accomplishing your task.
Don't worry about how fancy your code was. It works well, and is clear to understand, and that's really what matters.
Edit
I realized later on that I forgot to base everything from the center, and the code I had posted would do a circle from the top left. I've added in the center stuff above. See? Adding complexity doesn't at all imply the code will be better... :-)
Also, I did figure out one change I would recommend to your original algorithm: name your states! Make them strings, and have state be "top", "right", "bottom" and "left", and actively set them rather than using state++. That will help make your code even more readable.
I am currently trying to create a sudoku grid in javascript, to do this I need to set up a loop so one line re-appears 10 times with a gap of 20 pixels between each one. So far I have:
var canvas;
canvas = openGraphics();
var x;
var y;
var gap;
x = 20;
y = 20;
gap = 25;
canvas.drawLine(20, 20, 20, 245);
canvas.paint();
How would you recommend to do this?
As you already stated, you have to use a looping construct.
The Mozilla Developer Network has good documentation on these.
But honestly, I think you should rather read their JavaScript Guide before trying to write a Game, otherwise you'll end up bumping into a ton of dead ends and you will soon loose the interest in making the game at all.
Also, please stay on MDN when searching looking JavaScript help, since there are a lot of sites on the Internet that have bad, old, broken code example and help.
Especially stay away from w3schools.
There are various looping constructs available to programmers, the 2 most common ones are the for loop and the while loop.
The for loop is good for "looping" a number of times and the while loop is for looping while some value is "true".
In this case, you know the number of lines that you need to draw so the for loop is best matched
This is example code for the for loop. What happens is that the code between the { and } is run multiple time. Each time the loop runs, variable i gets larger by 1, starting from 0. This continues until the condition i<numberTimesToLoop becomes false.
for(var i=0;i<numberTimesToLoop;i++)
{
document.write("i = " + i);
document.write("<br />");
}
Not that I want to do your assignment for you, but in a lot of cases its easier to learn from seeing actual code. (I demonstrate and I find plenty of students who on seeing an answer can immediately recognise it in the future. Of course I give them the theory first and if it doesn't click I try this method...)
This modification to your current code will draw your vertical lines.
var canvas;
canvas = openGraphics();
var x;
var y;
var gap;
x = 20;
y = 20;
gap = 25;
var currentX = 20;
for(var i=0;i<10;i++)
{
canvas.drawLine(currentX, 20, currentX, 245);
currentX = currentX + gap;
}
canvas.paint();
(Not really sure what the problem with the w3schools website is)