Recently, I came across a javascript chess engine on https://codepen.io/zainmer/pen/jOOBjvv and thought to myself "How do I improve it..." With the help of chatgpt... and... Nope, although it plays better than the one in codepen, it still manages to blunder pieces at a depth of 3. I can not increase the depth as the bot will take very long to think. I also do not want to write a lot of code, just want to keep it simpler but stronger. For now, all the code is in a .html file but I may need to separate the styles and scripts from the html later to make it a bit cleaner. Any ideas how to improve the javascript code to load faster? Maybe using webworkers... But I still don't know how to do that. Please help. Here is the index.html code (with the css and js):
.board { Â Â width: 500px; Â Â margin: auto } CSS .noScroll { Â Â overflow: hidden; Â Â position:fixed; } CSS .mobileNavOverlay { Â Â position: fixed; Â Â width: 100%; Â Â height: 100%; Â Â background: rgba(0, 0, 0, .7); Â Â z-index: 2; Â Â top: 0; Â Â left: 0; } .mobileNavOverlay { Â Â position: fixed; Â Â width: 100%; Â Â height: 100%; Â Â background: rgba(0, 0, 0, .7); Â Â z-index: 2; Â Â top: 0; Â Â left: 0; } .noScroll { Â Â overflow: hidden; Â Â position:fixed; }
.info { Â Â width: 400px; Â Â margin: auto; }
.move-history { Â Â max-height: 100px; Â Â overflow-y: scroll; }
/* clearfix */ .clearfix-7da63 { Â clear: both; }
/* board */ .board-b72b1 { Â border: 2px solid #404040; Â -moz-box-sizing: content-box; Â box-sizing: content-box; }
/* square */ .square-55d63 { Â float: left; Â position: relative;
 /* disable any native browser highlighting */  -webkit-touch-callout: none;   -webkit-user-select: none;    -khtml-user-select: none;     -moz-user-select: none;     -ms-user-select: none;       user-select: none; }
/* white square */ .white-1e1d7 { Â background-color: #f0d9b5; Â color: #b58863; }
/* black square */ .black-3c85d { Â background-color: #b58863; Â color: #f0d9b5; }
/* notation */ .notation-322f9 { Â cursor: default; Â font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; Â font-size: 14px; Â position: absolute; } .alpha-d2270 { Â bottom: 1px; Â right: 3px; } .numeric-fc462 { Â top: 2px; Â left: 2px; }
<!-- Used to get images --> <base href="http://chessboardjs.com/" /> <h3 class="board"> JSfish Chess Bot </h3> <div id="board" class="board"></div> <br> <div class="info"> Â Â Search depth: Â Â <select id="search-depth"> Â Â Â Â <option value="1">1</option> Â Â Â Â <option value="2">2</option> Â Â Â Â <option value="3" selected>3</option> Â <option value="4">4</option>
Â
  </select>
  <br>   <span>Positions evaluated: <span id="position-count"></span></span>   <br>   <span>Time: <span id="time"></span></span>   <br>   <span>Positions/s: <span id="positions-per-s"></span> </span>   <br>   <br>   <div id="move-history" class="move-history">   </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.js"></script> <script src="https://rawcdn.githack.com/zm2231/APCSA-Assignemnts/6b4cacbd8039a38ef66ed74921503aa872bb48cc/newChess.js"></script> <script src="https://rawcdn.githack.com/zm2231/APCSA-Assignemnts/643fa5bb62c8d425d18a0dd13b04169e46c8bd6e/chessboard.js"></script> <script></script>
<script> var board, Â Â game = new Chess();
/*The "AI" part starts here */ // Create a new div element for the evaluation scores // Create a new div element for the evaluation scores const evaluationDiv = createElement("div"); evaluationDiv.style.position = "fixed"; evaluationDiv.style.top = "0"; evaluationDiv.style.left = "0"; evaluationDiv.style.width = "320px"; evaluationDiv.style.height = "100vh"; evaluationDiv.style.padding = "10px"; evaluationDiv.style.overflowY = "scroll"; body(evaluationDiv);
const minimaxRoot = function (depth, game, isMaximisingPlayer, callback) { Â const newGameMoves = game.ugly_moves(); Â let bestMove = -Infinity; Â let bestMoveFound; Â const totalIterations = newGameMoves.length; Â let iterationsSoFar = 0;
 for (let i = 0; i < totalIterations; i++) {   const newGameMove = newGameMoves[i];   game.ugly_move(newGameMove);   const value = minimax(depth - 1, game, -Infinity, Infinity, !isMaximisingPlayer, callback);   game.undo();   if (value >= bestMove) {    bestMove = value;    bestMoveFound = newGameMove;   }   iterationsSoFar++;   if (iterationsSoFar === Math.floor(totalIterations / 2)) {    // Append the current evaluation score to the evaluation div after half the iterations    const moveText = createElement("p");    moveText.textContent = "Evaluation: " + bestMove;    evaluationDiv(moveText);   }  }
 return bestMoveFound; }; const minimax = function(depth, game, alpha, beta, isMaximisingPlayer, callback) {  positionCount++;  if (depth === 0) {   return -evaluateBoard(game.board());  }
 const newGameMoves = game.ugly_moves();  let bestMove;
 if (isMaximisingPlayer) {   bestMove = -Infinity;   for (let i = 0; i < newGameMoves.length; i++) {    game.ugly_move(newGameMoves[i]);    bestMove = Math.max(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer, callback));    game.undo();    alpha = Math.max(alpha, bestMove);    if (beta <= alpha) {     break;    }   }  } else {   bestMove = Infinity;   for (let i = 0; i < newGameMoves.length; i++) {    game.ugly_move(newGameMoves[i]);    bestMove = Math.min(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer, callback));    game.undo();    beta = Math.min(beta, bestMove);    if (beta <= alpha) {     break;    }   }  }  return bestMove; };
var evaluateBoard = function (board) { Â Â var totalEvaluation = 0; Â Â for (var i = 0; i < 8; i++) { Â Â Â Â for (var j = 0; j < 8; j++) { Â Â Â Â Â Â totalEvaluation = totalEvaluation + getPieceValue(board[i][j], i ,j); Â Â Â Â } Â Â } Â Â return totalEvaluation; };
var reverseArray = function(array) { Â Â return array.slice().reverse(); };
var pawnEvalWhite = Â Â [ Â [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.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, Â 10.0, Â 3.0, Â 0.0, Â 0.0, Â 0.0], Â Â Â Â Â [0.0, Â 0.0, Â 20.0, 4.0, Â 4.0, Â 0.0, Â 0.0, Â 0.0], Â Â Â Â Â [10.0, Â 5.0, Â 0.0, Â 5.0, Â 5.0, Â 0.0, Â 5.0, Â 10.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, Â 0.0, Â 0.0] Â Â ];
var pawnEvalBlack = reverseArray(pawnEvalWhite);
var knightEval = Â Â [ Â Â Â Â Â [0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 2.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, Â 5.0, Â 0.0, Â 0.0, Â 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, Â 0.0, Â 0.0, Â 0.0, Â 0.0], Â Â Â Â Â [0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 7.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, Â 0.0, Â 0.0, Â 0.0, Â 0.0] Â Â ];
var bishopEvalWhite = [ Â Â Â Â Â [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.0, Â 0.0], Â Â Â Â Â [0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 2.0, Â 0.0, Â 0.0], Â Â Â Â Â [0.0, Â 3.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0], Â Â Â Â Â [4.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 5.0], Â Â Â Â Â [0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0, Â 0.0], Â Â Â Â Â [0.0, Â 0.0, Â 5.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] ];
var bishopEvalBlack = reverseArray(bishopEvalWhite);
var rookEvalWhite = [ Â Â Â Â Â [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.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, Â 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.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, Â 6.0, Â 8.0, Â 8.0, Â 6.0, Â 0.0, Â 0.0] ];
var rookEvalBlack = reverseArray(rookEvalWhite);
var evalQueen = [
     [0.0,  0.0,  0.0,  3.0,  0.0,  0.0,  0.0,  0.0],      [0.0,  0.0,  0.0,  4.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,  -5.0,  -5.0,  -4.0,  0.0,  0.0],      [0.0,  0.0,  0.0,  -5.0,  -5.0,  -4.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,  3.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] ];
var kingEvalWhite = [
     [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.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,  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.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,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0] ];
var kingEvalBlack = reverseArray(kingEvalWhite);
var getPieceValue = function (piece, x, y) { Â Â if (piece === null) { Â Â Â Â return 0; Â Â } Â Â var getAbsoluteValue = function (piece, isWhite, x ,y) { Â Â Â Â if (piece.type === 'p') { Â Â Â Â Â Â return 160 + ( isWhite ? pawnEvalWhite[y][x] : pawnEvalBlack[y][x] ); Â Â Â Â } else if (piece.type === 'r') { Â Â Â Â Â Â return 500 + ( isWhite ? rookEvalWhite[y][x] : rookEvalBlack[y][x] ); Â Â Â Â } else if (piece.type === 'n') { Â Â Â Â Â Â return 290 + knightEval[y][x]; Â Â Â Â } else if (piece.type === 'b') { Â Â Â Â Â Â return 350 + ( isWhite ? bishopEvalWhite[y][x] : bishopEvalBlack[y][x] ); Â Â Â Â } else if (piece.type === 'q') { Â Â Â Â Â Â return 900 + evalQueen[y][x]; Â Â Â Â } else if (piece.type === 'k') { Â Â Â Â Â Â return 0 + ( isWhite ? kingEvalWhite[y][x] : kingEvalBlack[y][x] ); Â Â Â Â } Â Â Â Â throw "Unknown piece type: " + piece.type; Â Â };
  var absoluteValue = getAbsoluteValue(piece, piece.color === 'w', x ,y);   return piece.color === 'w' ? absoluteValue : -absoluteValue; };
/* board visualization and games state handling */
var onDragStart = function (source, piece, position, orientation) { Â Â if (game.in_checkmate() === true || game.in_draw() === true || Â Â Â Â piece.search(/^b/) !== -1) { Â Â Â Â return false; Â Â } };
var makeBestMove = function () { Â Â var bestMove = getBestMove(game); Â Â game.ugly_move(bestMove); Â Â board.position(game.fen()); Â Â renderMoveHistory(game.history()); Â Â if (game.game_over()) { Â Â Â Â alert('Game over'); Â Â } };
var positionCount; var getBestMove = function (game) { Â Â if (game.game_over()) { Â Â Â Â alert('Game over'); Â Â }
  positionCount = 0;   var depth = parseInt($('#search-depth').find('elected').text());
  var d = new Date().getTime();   var bestMove = minimaxRoot(depth, game, true);   var d2 = new Date().getTime();   var moveTime = (d2 - d);   var positionsPerS = ( positionCount * 1000 / moveTime);
  $('#position-count').text(positionCount);   $('#time').text(moveTime/1000 + 's');   $('#positions-per-s').text(positionsPerS);   return bestMove; };
var renderMoveHistory = function (moves) { Â Â var historyElement = $('#move-history').empty(); Â Â historyElement.empty(); Â Â for (var i = 0; i < moves.length; i = i + 2) { Â Â Â Â historyElement.append('<span>' + moves[i] + ' ' + ( moves[i + 1] ? moves[i + 1] : ' ') + '</span><br>') Â Â } Â Â historyElement.scrollTop(historyElement[0].scrollHeight);
};
var onDrop = function (source, target) {
  var move = game.move({     from: source,     to: target,     promotion: 'q'   });
  removeGreySquares();   if (move === null) {     return 'snapback';   }
  renderMoveHistory(game.history());   setTimeout(makeBestMove, 250); };
var onSnapEnd = function () { Â Â board.position(game.fen()); };
var onMouseoverSquare = function(square, piece) {   var moves = game.moves({     square: square,     verbose: true   });
  if (moves.length === 0) return;
  greySquare(square);
  for (var i = 0; i < moves.length; i++) {     greySquare(moves[i].to);   } };
var onMouseoutSquare = function(square, piece) { Â Â removeGreySquares(); };
var removeGreySquares = function() { Â Â $('#board .square-55d63').css('background', ''); };
var greySquare = function(square) { Â Â var squareEl = $('#board .square-' + square);
  var background = '#a9a9a9';   if (squareEl.hasClass('black-3c85d') === true) {     background = '#696969';   }
  squareEl.css('background', background); };
var cfg = { Â Â draggable: true, Â Â position: 'start', Â Â onDragStart: onDragStart, Â Â onDrop: onDrop, Â Â onMouseoutSquare: onMouseoutSquare, Â Â onMouseoverSquare: onMouseoverSquare, Â Â onSnapEnd: onSnapEnd };
Recently, I came across a javascript chess engine on https://codepen.io/zainmer/pen/jOOBjvv and thought to myself "How do I improve it..." With the help of chatgpt... and... Nope, although it plays better than the one in codepen, it still manages to blunder pieces at a depth of 3. I can not increase the depth as the bot will take very long to think. I also do not want to write a lot of code, just want to keep it simpler but stronger. For now, all the code is in a .html file but I may need to separate the styles and scripts from the html later to make it a bit cleaner. Any ideas how to improve the javascript code to load faster? Maybe using webworkers... But I still don't know how to do that. Please help. Here is the index.html code (with the css and js):
live at : https://jsfish.w3spaces.com/
<style>
.board {
  width: 500px;
  margin: auto
}
CSS
.noScroll {
  overflow: hidden;
  position:fixed;
}
CSS
.mobileNavOverlay {
  position: fixed;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, .7);
  z-index: 2;
  top: 0;
  left: 0;
}
.mobileNavOverlay {
  position: fixed;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, .7);
  z-index: 2;
  top: 0;
  left: 0;
}
.noScroll {
  overflow: hidden;
  position:fixed;
}
.info {
  width: 400px;
  margin: auto;
}
.move-history {
  max-height: 100px;
  overflow-y: scroll;
}
/* clearfix */
.clearfix-7da63 {
 clear: both;
}
/* board */
.board-b72b1 {
 border: 2px solid #404040;
 -moz-box-sizing: content-box;
 box-sizing: content-box;
}
/* square */
.square-55d63 {
 float: left;
 position: relative;
 /* disable any native browser highlighting */
 -webkit-touch-callout: none;
  -webkit-user-select: none;
   -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
      user-select: none;
}
/* white square */
.white-1e1d7 {
 background-color: #f0d9b5;
 color: #b58863;
}
/* black square */
.black-3c85d {
 background-color: #b58863;
 color: #f0d9b5;
}
/* highlighted square */
.highlight1-32417, .highlight2-9c5d2 {
 -webkit-box-shadow: inset 0 0 3px 3px yellow;
 -moz-box-shadow: inset 0 0 3px 3px yellow;
 box-shadow: inset 0 0 3px 3px yellow;
}
/* notation */
.notation-322f9 {
 cursor: default;
 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
 font-size: 14px;
 position: absolute;
}
.alpha-d2270 {
 bottom: 1px;
 right: 3px;
}
.numeric-fc462 {
 top: 2px;
 left: 2px;
}
select{
height: 30px;
width: 30px;
border-radius: 100%;
transition: 0.3s ease;
}
select:hover{
transform: scale(1.1);
background-color: black;
color: white;
}
</style>
<!-- Used to get images -->
<base href="http://chessboardjs.com/" />
<h3 class="board">
JSfish Chess Bot
</h3>
<div id="board" class="board"></div>
<br>
<div class="info">
  Search depth:
  <select id="search-depth">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3" selected>3</option>
 <option value="4">4</option>
Â
  </select>
  <br>
  <span>Positions evaluated: <span id="position-count"></span></span>
  <br>
  <span>Time: <span id="time"></span></span>
  <br>
  <span>Positions/s: <span id="positions-per-s"></span> </span>
  <br>
  <br>
  <div id="move-history" class="move-history">
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.js"></script>
<script src="https://rawcdn.githack.com/zm2231/APCSA-Assignemnts/6b4cacbd8039a38ef66ed74921503aa872bb48cc/newChess.js"></script>
<script src="https://rawcdn.githack.com/zm2231/APCSA-Assignemnts/643fa5bb62c8d425d18a0dd13b04169e46c8bd6e/chessboard.js"></script>
<script></script>
<script>
var board,
  game = new Chess();
/*The "AI" part starts here */
// Create a new div element for the evaluation scores
// Create a new div element for the evaluation scores
const evaluationDiv = createElement("div");
evaluationDiv.style.position = "fixed";
evaluationDiv.style.top = "0";
evaluationDiv.style.left = "0";
evaluationDiv.style.width = "320px";
evaluationDiv.style.height = "100vh";
evaluationDiv.style.padding = "10px";
evaluationDiv.style.overflowY = "scroll";
body(evaluationDiv);
const minimaxRoot = function (depth, game, isMaximisingPlayer, callback) {
 const newGameMoves = game.ugly_moves();
 let bestMove = -Infinity;
 let bestMoveFound;
 const totalIterations = newGameMoves.length;
 let iterationsSoFar = 0;
 for (let i = 0; i < totalIterations; i++) {
  const newGameMove = newGameMoves[i];
  game.ugly_move(newGameMove);
  const value = minimax(depth - 1, game, -Infinity, Infinity, !isMaximisingPlayer, callback);
  game.undo();
  if (value >= bestMove) {
   bestMove = value;
   bestMoveFound = newGameMove;
  }
  iterationsSoFar++;
  if (iterationsSoFar === Math.floor(totalIterations / 2)) {
   // Append the current evaluation score to the evaluation div after half the iterations
   const moveText = createElement("p");
   moveText.textContent = "Evaluation: " + bestMove;
   evaluationDiv(moveText);
  }
 }
 return bestMoveFound;
};
const minimax = function(depth, game, alpha, beta, isMaximisingPlayer, callback) {
 positionCount++;
 if (depth === 0) {
  return -evaluateBoard(game.board());
 }
 const newGameMoves = game.ugly_moves();
 let bestMove;
 if (isMaximisingPlayer) {
  bestMove = -Infinity;
  for (let i = 0; i < newGameMoves.length; i++) {
   game.ugly_move(newGameMoves[i]);
   bestMove = Math.max(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer, callback));
   game.undo();
   alpha = Math.max(alpha, bestMove);
   if (beta <= alpha) {
    break;
   }
  }
 } else {
  bestMove = Infinity;
  for (let i = 0; i < newGameMoves.length; i++) {
   game.ugly_move(newGameMoves[i]);
   bestMove = Math.min(bestMove, minimax(depth - 1, game, alpha, beta, !isMaximisingPlayer, callback));
   game.undo();
   beta = Math.min(beta, bestMove);
   if (beta <= alpha) {
    break;
   }
  }
 }
 return bestMove;
};
var evaluateBoard = function (board) {
  var totalEvaluation = 0;
  for (var i = 0; i < 8; i++) {
    for (var j = 0; j < 8; j++) {
      totalEvaluation = totalEvaluation + getPieceValue(board[i][j], i ,j);
    }
  }
  return totalEvaluation;
};
var reverseArray = function(array) {
  return array.slice().reverse();
};
var pawnEvalWhite =
  [
 [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.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,  10.0,  3.0,  0.0,  0.0,  0.0],
     [0.0,  0.0,  20.0, 4.0,  4.0,  0.0,  0.0,  0.0],
     [10.0,  5.0,  0.0,  5.0,  5.0,  0.0,  5.0,  10.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,  0.0,  0.0]
  ];
var pawnEvalBlack = reverseArray(pawnEvalWhite);
var knightEval =
  [
     [0.0,  0.0,  0.0,  0.0,  0.0,  2.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,  5.0,  0.0,  0.0,  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,  0.0,  0.0,  0.0,  0.0],
     [0.0,  0.0,  0.0,  0.0,  0.0,  7.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,  0.0,  0.0,  0.0,  0.0]
  ];
var bishopEvalWhite = [
     [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.0,  0.0],
     [0.0,  0.0,  0.0,  0.0,  0.0,  2.0,  0.0,  0.0],
     [0.0,  3.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
     [4.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  5.0],
     [0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0],
     [0.0,  0.0,  5.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]
];
var bishopEvalBlack = reverseArray(bishopEvalWhite);
var rookEvalWhite = [
     [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.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,  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.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,  6.0,  8.0,  8.0,  6.0,  0.0,  0.0]
];
var rookEvalBlack = reverseArray(rookEvalWhite);
var evalQueen = [
     [0.0,  0.0,  0.0,  3.0,  0.0,  0.0,  0.0,  0.0],
     [0.0,  0.0,  0.0,  4.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,  -5.0,  -5.0,  -4.0,  0.0,  0.0],
     [0.0,  0.0,  0.0,  -5.0,  -5.0,  -4.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,  3.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]
];
var kingEvalWhite = [
     [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.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,  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.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,  0.0,  0.0,  0.0,  0.0,  0.0,  0.0]
];
var kingEvalBlack = reverseArray(kingEvalWhite);
var getPieceValue = function (piece, x, y) {
  if (piece === null) {
    return 0;
  }
  var getAbsoluteValue = function (piece, isWhite, x ,y) {
    if (piece.type === 'p') {
      return 160 + ( isWhite ? pawnEvalWhite[y][x] : pawnEvalBlack[y][x] );
    } else if (piece.type === 'r') {
      return 500 + ( isWhite ? rookEvalWhite[y][x] : rookEvalBlack[y][x] );
    } else if (piece.type === 'n') {
      return 290 + knightEval[y][x];
    } else if (piece.type === 'b') {
      return 350 + ( isWhite ? bishopEvalWhite[y][x] : bishopEvalBlack[y][x] );
    } else if (piece.type === 'q') {
      return 900 + evalQueen[y][x];
    } else if (piece.type === 'k') {
      return 0 + ( isWhite ? kingEvalWhite[y][x] : kingEvalBlack[y][x] );
    }
    throw "Unknown piece type: " + piece.type;
  };
  var absoluteValue = getAbsoluteValue(piece, piece.color === 'w', x ,y);
  return piece.color === 'w' ? absoluteValue : -absoluteValue;
};
/* board visualization and games state handling */
var onDragStart = function (source, piece, position, orientation) {
  if (game.in_checkmate() === true || game.in_draw() === true ||
    piece.search(/^b/) !== -1) {
    return false;
  }
};
var makeBestMove = function () {
  var bestMove = getBestMove(game);
  game.ugly_move(bestMove);
  board.position(game.fen());
  renderMoveHistory(game.history());
  if (game.game_over()) {
    alert('Game over');
  }
};
var positionCount;
var getBestMove = function (game) {
  if (game.game_over()) {
    alert('Game over');
  }
  positionCount = 0;
  var depth = parseInt($('#search-depth').find('
  var d = new Date().getTime();
  var bestMove = minimaxRoot(depth, game, true);
  var d2 = new Date().getTime();
  var moveTime = (d2 - d);
  var positionsPerS = ( positionCount * 1000 / moveTime);
  $('#position-count').text(positionCount);
  $('#time').text(moveTime/1000 + 's');
  $('#positions-per-s').text(positionsPerS);
  return bestMove;
};
var renderMoveHistory = function (moves) {
  var historyElement = $('#move-history').empty();
  historyElement.empty();
  for (var i = 0; i < moves.length; i = i + 2) {
    historyElement.append('<span>' + moves[i] + ' ' + ( moves[i + 1] ? moves[i + 1] : ' ') + '</span><br>')
  }
  historyElement.scrollTop(historyElement[0].scrollHeight);
};
var onDrop = function (source, target) {
  var move = game.move({
    from: source,
    to: target,
    promotion: 'q'
  });
  removeGreySquares();
  if (move === null) {
    return 'snapback';
  }
  renderMoveHistory(game.history());
  setTimeout(makeBestMove, 250);
};
var onSnapEnd = function () {
  board.position(game.fen());
};
var onMouseoverSquare = function(square, piece) {
  var moves = game.moves({
    square: square,
    verbose: true
  });
  if (moves.length === 0) return;
  greySquare(square);
  for (var i = 0; i < moves.length; i++) {
    greySquare(moves[i].to);
  }
};
var onMouseoutSquare = function(square, piece) {
  removeGreySquares();
};
var removeGreySquares = function() {
  $('#board .square-55d63').css('background', '');
};
var greySquare = function(square) {
  var squareEl = $('#board .square-' + square);
  var background = '#a9a9a9';
  if (squareEl.hasClass('black-3c85d') === true) {
    background = '#696969';
  }
  squareEl.css('background', background);
};
var cfg = {
  draggable: true,
  position: 'start',
  onDragStart: onDragStart,
  onDrop: onDrop,
  onMouseoutSquare: onMouseoutSquare,
  onMouseoverSquare: onMouseoverSquare,
  onSnapEnd: onSnapEnd
};
board = ChessBoard('board', cfg);
</script>