How to improve performance of Minimax for Chess.js?

1.4k Views Asked by At


I am trying to code a chess engine using Chess.js and Chessboard.js using the Minimax algorithm with alpha-beta pruning. The problem is that the algorithm takes a very long time to perform all the evaluations and decide the move to make, even with depth=3. How can I speed up the performance?

The following is my minimax implementation:

var minimax = function (depth, game, alpha, beta, isAIplaying) {

  // end branch: simply evaluate and return the current score of the board
  if (depth === 0) {
    return evaluateBoard(game);
  }

  // list of all the possible moves in current status
  var newGameMoves = game.moves();
  if (isAIplaying) {
      var bestMove = -9999;
      for (var i = 0; i < newGameMoves.length; i++) {
          game.move(newGameMoves[i]);
          bestMove = Math.max(bestMove, minimax(depth - 1, game, !isAIplaying));
          game.undo();
          alpha = Math.max(alpha, bestMove);
          if (beta <= alpha) {
            return bestMove;
          }
      }
      return bestMove;
  } else {
      var bestMove = 9999;
      for (var i = 0; i < newGameMoves.length; i++) {
          game.move(newGameMoves[i]);
          bestMove = Math.min(bestMove, minimax(depth - 1, game, !isAIplaying));
          game.undo();
          beta = Math.min(beta, bestMove);
          if (beta <= alpha) {
           return bestMove;
          }
      }
      return bestMove;
  }
};

The function is called in the following peace of code where Black has to decide which move has to take:

var possibleMoves = game.moves(); 

// game over
if (possibleMoves.length === 0) 
  return;

var best_score = -9999;
var bestMoveFound;

for(var i=0; i<possibleMoves.length; ++i){
  game.move(possibleMoves[i]); // make one possible move

   // minimax: take the current status of the game
  // it is not Black's turn
  var score = minimax(3, game, -10000, 10000, false);

  // if I get a better score then I store it
  if(score >= best_score){
    best_score = score;
    bestMoveFound = i;
  }

  // undo move
  game.undo();
}

// make the best move (update both the game and the board on the screen)
game.move(possibleMoves[bestMoveFound]);
board.position(game.fen());

Where the following is the evaluation function:

var evaluateBoard = function(current_game) {
    var status = current_game.fen();

    var score = 0;

    // calculate score for the current situation
    var fen_idx = 0;
    var piece = status[fen_idx];
    while(piece != ' '){
      switch(piece){
        case 'p': score += pawn; break;
        case 'n': score += knight; break;
        case 'b': score += bishop; break;
        case 'r': score += tower; break;
        case 'q': score += queen; break;
        case 'k': score += king; break;
        case 'P': score -= pawn; break;
        case 'N': score -= knight; break;
        case 'B': score -= bishop; break;
        case 'R': score -= tower; break;
        case 'Q': score -= queen; break;
        case 'K': score -= king; break;
        default: break;
      }

      ++fen_idx;
      piece = status[fen_idx];
    }

    return score;
};

pawn, bishop and the others are simple constants and p indicates a black pawn while P indicates a white one. Do you have any idea for speeding the performance up?

1

There are 1 best solutions below

0
On

The idea to evaluate the FEN-String looks fast, but your program has to calculate a FEN-String for every leave of the game tree. This is really slow, because it has to scan the board about every figure and also about every empty square of the board in a loop over the 64 squares. You should not do this, but keep the figures on the board in a list for calculating analog to your source code.

It would be even better to have a sum of all peaces and only add or subtract a value, if a piece is taken or replace on the board. So you have no calculation in your evaluation function and your engine will be much faster. In my experience you should reach depth 7 in <2 seconds if your evaluation function is only a comparison of the piece values of the two sides.