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
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 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.
I have been working on this blackjack problem on learnstreet:
http://www.learnstreet.com/cg/simple/project/blackjack#get-hint
I am stuck on the last method getStrategy() - here's a description of how the task is to be completed:
"This method simulates the dealer's strategy, so that he knows when to hit and when to stand -- i.e., when to accept another draw from the deck and risk "going bust" and breaking 21, or stopping with the current hand and hoping his opponent will not beat his number.
This is a special function in that it returns an object that is a function itself. (How's that for crazy?) It accepts 'n', an integer score of the dealer's hand at which point the dealer's strategy is to hit or to stand. (Blackjack dealers usually take hits when his cards total less than 17 points, so n would be 17 in that case.)
What you need to return in this method should be in the form of "return function(currenthand){};" where you fill in what's inside the curly braces ({}). The current hand will be supplied to the function call, and you will need to write some logic where the dealer compares the current hand's points with 'n'."
LearnStreet implemented getSrategt() in this way:
function getstrategy(n) {
return function(currenthand) {
return (countpoints(currenthand) < n);
}
}
The getStrategy() method is called in the applyStrategy method like this:
/*
This function applies the strategy you define in getstrategy(n): DON'T CHANGE
*/
function applystrategy(hand, n) {
var strat = getstrategy(n);
return strat(hand);
}
Can anybody please explain to me why we are returning (countpoints(currenthand) < n)?
countpoints(currenthand) will return the number of points in the hand. n is 17, the number where, if the current points is less than, the dealer will take another hit. Basically, if current points is less than 17, keep playing, if it is equal to or greater, stop.
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.