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

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.

Related

changing values in array

I am trying to build a battleship game and using functions.
I wish to create and randomise 1 & 0 in my array every time I run the function as seen in the array below
Since it is a battlefield game, is there any way to make the 1s be in a row /column of 4/3/2/1? , to mimic the different sizes of the battleships
let battelfield = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,1,0,0,0],
[1,0,0,0,0,0,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0],
[1,0,0,1,0,0,0,0,0,0],
[1,0,0,1,0,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,0]
]`
For a battleship, the way I would do it would be (assuming your grid is already filled with 0s):
For each ship
randomly select a starting position
randomly select a direction (up, down, left, right)
add your ship (by changing however many 1s you need to, based on the size of the ship).
The checks you need to add would be:
At step 1, make sure there isn't a boat there already, in which case pick again.
At step 2, make sure you're not going to hit the side of your game board, or another ship, in which case try another direction. If all 4 directions have been tried and there isn't enough space for a ship, back to step 1.
I usually don't give full answers when OP doesn't really show they tried but I liked the challenge.
The idea is to:
Set your empty board.
Choose a random point in the board where the ship will start
Choose direction (H or V)
With the random point and direction, make sure there is room for the ship according to the limits of the board
Create a list of positions the ship will take
Test all positions to make sure they are free
Set the positions on the board as filled.
At any given time, if a check is not fulfilled I've put continue; this will stop the current iteration and back to the beginning of the while. That way, the code runs until it finds a spot, and return to leave the loop.
Also, I've made a 1d array instead of 2d because it felt easier for mathing it out and manipulations. Feel free to convert to 2D afterward, or not.
let battlefield = new Array(10*10).fill(0);
placeShip(3);
placeShip(4);
placeShip(4);
placeShip(5);
console.log(battlefield);
function placeShip(length){
while(true){
const start = Math.round(Math.random()*99);
if(battlefield[start]==='1') continue;
const orientation = Math.random() <=.5?'H':'V';
// Fill the positions where the ship will be placed.
const positions = new Array();
if(orientation === 'H'){
// First make sure we have room to place it
if(10-((start+1) % 10) < length)continue;
for(let p=start;p<start+length;p++){
// Set for each length the position the ship will take.
positions.push(p);
}
}else if(orientation === 'V'){
// Same as for H but we divide by 10 because we want rows instead of cells.
if(10-((start/10) % 10) < length)continue;
for(let p=start;p<start+length*10;p+=10){
// Set for each length the position the ship will take.
positions.push(p);
}
}
// Now let's check to make sure there is not a ship already in one of the positions
for(let i=0,L=positions.length;i<L;i++){
if(battlefield[positions[i]]!=="0")continue;
}
// Now let's put the ship in place
for(let i=0,L=positions.length;i<L;i++){
battlefield[positions[i]] = 1;
}
return;
}
}

Possible optimizations for my alpha-beta pruning algorithm?

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

Java Script + Node.js Calculator issue

I am a "new" developer into the foray of Web Development and I have come across an issue I was hoping that you fine people on Stack Overflow would be able to help me with. I have asked several Cadre and Instructors in my class and we are all stumped by it.
To start with I have decided to put all of my code on a Gitlab repo so if you want to look at the whole thing (or if you want to add to it let me know): Link to Github Repo. I fiqured you guys don't want the whole thing posted as a wall of text and rather some snip-its of what in the file I specifically. But it is relitively small file
I am useing simple JavaScript as well as Node.Js to be able to build a working calculator in the back end that I can use as a template for any other project I will need to work on in the future. For now I am trying to just get it working by imputing things via the console.
I have made a way for what is imputed in Node and to an imputArray var I have set up and the Array goes something like this:
[(command), (num1), (num2), (num3), ...]
I set up a switch function that runs a block of code based on what command was given (add, subtract, divide, etc..). As well as separating the command from the number and putting them inside another array.
The part I need some help with is with getting the block of code to work for what I want it to do. I have got it set up to run rather easily on two numbers but I want it to handle as many numbers as I want to throw at it. I tried various forms of for loops as well as forEach loops and I cant seem to get it working.
case 'divide':
for (i = 1; i < numArray.length; i++) { // If any number besides the first is 0 spit out this
if (numArray[i] === 0) {
consol.log("You canot divide by zero!");
}
else {
var previousTotal = numArray[0]; //Inital number in array
for (i = 1; i < numArray.length; i++) {
previousTotal = previousTotal / numArray[i]; // for each number in array divide to the previous number
}
}
result = previousTotal // Pushes end total to result
}
break;
I have gone through several different versions of the above code (such as using for loops instead) but this is pretty much what I ended up with. I'm sure there is an easier way and more sane way to do what I am trying to do, but if I knew how I wouldn't be here.
Essentially this is the ideal thing I want to do but I cant find a way to do it: I want to run a small block of code the index of the number array, minus one. In this case it is dividing the previous number by the next number in the array.
So it only runs if there are more then one in the array and it does the function to the previous number, or total from the last one in the array.
This is pretty much the only thing holding me back from finishing this so if someone can take the time to look at my crapy code and help it do what I want it to do that would be awesome.
Your code is reseting result each time the outer loop iterates so it will just equal what ever the last prev Total is. Basically every loop but the last is irrelevant. Do you want to add them to result? If so you want:
result += previousTotal
Or if you want an array of the answers you want:
result.push(reviousTotal)
Sorry not 100% what you want. Hope this helps!
You just need one loop, and you probably want to stop iterating if a 0 occurs:
result = numArray[0]; //no need for another variable
for (var i = 1; i < numArray.length; i++) { // declare variables!
if (numArray[i] === 0) {
console.log("You canot divide by zero!"); // typo...
break; // exit early
}
result = result / numArray[i];
}
For sure that can be also written a bit more elegantly:
const result = numArray.reduce((a, b) => a / b);
if(isNaN(result)) {
console.log("Can't divide by zero!");
} else {
console.log(`Result is ${result}`);
}
I assume you want the divide command to do ((num1/num2)/num3)/...
There are couple of issues in the code you posted, I will post a version that does the above. You can inspect and compare it your version to find your mistakes.
// divide, 10, 2, 5
case 'divide':
if (numArray.length < 2) {
console.log("no numbers in array")
break;
}
// previousTotal starts with 10
var previousTotal = numArray[1];
// start from the second number which is 2
for (i = 2; i < numArray.length; i++) {
if (numArray[i] === 0) {
console.log("You canot divide by zero!");
}
else {
previousTotal = previousTotal / numArray[i]; // for each number in array divide to the previous number
}
}
result = previousTotal;
// result will be (10/2)/5 = 1
break;

Unbalanced binary tree not functioning properly. Node.Js

So I was solving a problem for class involving binary search and the algorithm I implemented to solve it worked fine but my hunch is that a slight gamble would be more effective given the parameters of the problem
The fictional town of HollyBroke, Fl is made up of a 30 x 30 block grid. The streets are named after the presidents of the United States and the avenues are numbered numerically. The infamous two-word arsonist is holding the town hostage. He selects a house every Saturday for destruction by fire and taunts the police department by challenging them to guess the location for each week’s crime. He will answer up 10 guesses with either a “yes”or a “no” answer during his very brief phone call right before he strikes the match. (He won’t stay on the line so the call can’t be trace.)
The city wants you to develop a program to provide a quick response when this notorious criminal calls.
The answer to that was easy enough to create an algorithm for but I thought a median-1/median+1 gamble would be more effective. My hunch is that more often than not I will arrive at the conclusion with one extra question to go allowing me to either ask a binary search question about the arsonist or if the game allowed it I would show up with police before the end of the call. If I don't outright solve it beforehand I would have a very small space to search after it was completed, like three or four blocks right next to each other,
This is my code for the "gambling" binary search.
`var array = [{"a":30,"b":30,"c":0}]
function findLower(input) {
var half = Math.floor(input/2);
if(0 == input%2)
return (half-1);
else
return (half);
};
function findUpper(input) {
var half = Math.floor(input/2);
if(input%2 == 0)
return (half+1);
else
return (half+1);
}
for (var i = 0; i <= 9; i++){
for (var z = array.length - 1; z >= 0; z--) {
if (array[z].c = i){
if (array[z].a>array[z].b)
array.push({"a":findLower(array[z].a),"b":array[z].b,"c":array[z].c + 1},{"a":findUpper(array[z].a),"b":array[z].b,"c":array[z].c + 1})
else
array.push({"a":array[z].a,"b":findLower(array[z].b),"c":array[z].c + 1},{"a":array[z].a,"b":findUpper(array[z].b),"c":array[z].c + 1})
}
};
}
console.log(array.length);`
Its coming up with an absurd array length given that it should be 2^10 +2^9 + 2^8 ..... = 2047
The program is coming up with an array length of 19683
And some of the arrays should most certainly not be 30*14 at node level 10 I'm sure the algorithm was set up properly. I've walked it through two levels by pen and paper and it seems like it should work properly.
Found it.
if (array[z].c = i){
should be
if (array[z].c == i){
its a conditional statement not declaring them equal
Also I was wrong. You only have about a 40% chance of successfully locating the house in 10 guesses.

Detecting a Fist with Leap Motion SDK V2

I want to check if a Hand in a Leap Motion Frame is currently a Fist.
The usually suggested method is to look for hand.grabStrength with a value of 1. The problem is that the value jumps to 1 even with a "Claw-Like" Hand, or anything else with very slightly curled fingers.
Another approach would be to check on each finger if it is extended. But this has a similiar issue, Fingers only count as extended if they are completely straight. So even if i check for all fingers to be not extended, the same issue as above occurs (claw-like hands get recognized as grabbed).
Combining these two methods also does not solve the issue, which is not surprising given that they both suffer from the same problems.
Now, we do have all the bones of each finger available, with positions and everything. But I have no idea where to start with the math to detect if a finger is curled.
Basically I have this setup for now:
var controller = Leap.loop(function(frame){
if(frame.hands.length>0){
//we only look at the first available hand
var hand = frame.hands[0];
//we get the index finger only, but later on we should look at all 5 fingers.
var index = hands.fingers[1];
//after that we get the positions of the joints between the bones in a hand
//the position of the metacarpal bone (i.e. the base of your hand)
var carp = index.carpPosition;
//the position of the joint on the knuckle of your hand
var mcp = index.mcpPosition;
//the position of the following joint, between the proximal and the intermediate bones
var pip = index.pipPosition;
//the position of the distal bone (the very tip of your finger)
var dip = index.dipPosition;
//and now we need the angle between each of those positions, which is where i'm stuck
}
});
So, how do I get the angle between two of those positions (carp to mcp, mcp to pip, pip to dip)? Any ideas?
Alright, I think I found a sort of working approach to detect an actual fist, and not a claw.
First off, instead of the positions of the joints, we need the distance Vectors for each Bone.
Then we calculate the Dot product between the Metacarpal and the Proximal bone, as well as the dot Product between the Proximal and the Intermediate Bone. We can ignore the Distal bone, it doesn't change the result too much.
We sum all the calculated dot products (10 in total) and calculate the average out (we divide by 10). This will give us a value between 0 and 1. A Fist is beneath 0.5 and everything above that is basically not a fist.
Additionally you might also want to check for the amount of extended fingers on a Hand and check if it is 0. This will ensure that a "Thumbs-up" and similiar 1-digit poses do not get recognized as a Fist.
Here is my implementation:
const minValue = 0.5;
var controller = Leap.loop(function(frame){
if(frame.hands.length>0)
{
var hand = frame.hands[0];
var isFist = checkFist(hand);
}
});
function getExtendedFingers(hand){
var f = 0;
for(var i=0;i<hand.fingers.length;i++){
if(hand.fingers[i].extended){
f++;
}
}
return f;
}
function checkFist(hand){
var sum = 0;
for(var i=0;i<hand.fingers.length;i++){
var finger = hand.fingers[i];
var meta = finger.bones[0].direction();
var proxi = finger.bones[1].direction();
var inter = finger.bones[2].direction();
var dMetaProxi = Leap.vec3.dot(meta,proxi);
var dProxiInter = Leap.vec3.dot(proxi,inter);
sum += dMetaProxi;
sum += dProxiInter
}
sum = sum/10;
if(sum<=minValue && getExtendedFingers(hand)==0){
return true;
}else{
return false;
}
}
While this works like it should, I doubt that this is the correct and best approach to detect a Fist. So please, if you know of a better way, post it.
Solution works perfect, any chance you could explain why you divide by 10 and why the minValue is 0.5? Thanks!
Well, it doesn't work that good, to be honest. I'll soon start to work on a little project that has the goal to improve the detection of fists with Leap Motion.
Regarding your questions, We divide the sum by 10 because we have 2 Bone Joints per finger, with 5 fingers. We want the average value from the sum of all those calculations, because not all fingers will be angled in the same way. So we want some value that encompasses all of these values into a single one: the average value. Given that we have 10 calculations in total (2 per each finger, 5 fingers), we divide the sum of those calculations and there we go. We will get a value between 0 and 1.
Regarding the minValue: Trial&Error. In a project of mine, I used a value of 0.6 instead.
This is another problem of this approach: ideally a flat hand should be a value of nearly 0, while a fist should be 1.
I know it is an old topic but if you guys still around the answer could be simpler just by using sphereRadius() ;
I found "grabStrength" is good

Categories