Js chess engine (help)

Sort:
Avatar of BPGHchess

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('nervous.pngelected').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
};


board = ChessBoard('board', cfg);
</script>

Avatar of Scemer

IDK Code.

Avatar of BPGHchess

sad.png sad

Avatar of The-1andOnly_alpha1

Ik code

Avatar of The-1andOnly_alpha1

I'm pretty good at in and in fact, i'm making a chess site like chess.com

Avatar of Scemer

Buddy, this was one year ago.

Avatar of BasixWhiteBoy
Scemer wrote:

Buddy, this was one year ago.

I guess you got a nice notification for this.

Avatar of Scemer

Indeed, Basixy.

Avatar of DevinSuckAtChess

#6 it's been a long year

Avatar of The-1andOnly_alpha1

Ik

Avatar of The-1andOnly_alpha1

Im surprised someone even responded.

Avatar of EnCroissantmaterylan

I'm surprised you even responded

Avatar of EnCroissantmaterylan
BPGHchess wrote:

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 scoresconst 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};

board = ChessBoard('board', cfg);</script>

First you need more html just script will not work

Avatar of EnCroissantmaterylan

Or just use your python skills

Avatar of EnCroissantmaterylan

Or just use a Python script I wrote

import chess

import random

from time import perf_counter

class TranspositionTable:

"""

A transposition table for storing positions that have already been evaluated.

This prevents redundant calculations during search.

"""

def __init__(self, size=2**20): # 1 million entries

self.table = {}

self.size = size

def store(self, key, depth, flag, score, best_move):

"""Stores a position's data in the transposition table."""

if len(self.table) >= self.size:

# Simple eviction policy: overwrite oldest entries

# For a real engine, more advanced policies (e.g., LRU) are better.

self.table.pop(next(iter(self.table)))

self.table[key] = (depth, flag, score, best_move)

def retrieve(self, key):

"""Retrieves data for a given position if it exists."""

return self.table.get(key)

class AdvancedChessEngine:

def __init__(self):

self.tt = TranspositionTable()

self.opening_book = {}

self.nodes = 0

self.start_time = 0

self.time_limit = 5.0 # seconds per move

self.killer_moves = {} # Heuristic for move ordering

self.history_moves = {} # Heuristic for move ordering

# Load an opening book from a text file (e.g., a list of FENs or moves)

self.load_opening_book("opening_book.txt")

def load_opening_book(self, filename):

"""Loads a list of moves from a file for an opening book."""

try:

with open(filename, 'r') as f:

for line in f:

fen, move_str = line.strip().split()

if fen not in self.opening_book:

self.opening_book[fen] = []

self.opening_book[fen].append(chess.Move.from_uci(move_str))

except FileNotFoundError:

print("Opening book not found. Starting from scratch.")

def search_move(self, board: chess.Board):

"""

Performs an iterative deepening search to find the best move.

This approach incrementally increases the search depth.

"""

self.nodes = 0

self.start_time = perf_counter()

best_move = None

# Check opening book

fen = board.fen()

if fen in self.opening_book:

return random.choice(self.opening_book[fen])

for depth in range(1, 10): # Iterative deepening loop

if perf_counter() - self.start_time > self.time_limit:

break

score, move = self.alphabeta(board, depth, -float('inf'), float('inf'))

if move:

best_move = move

return best_move

def alphabeta(self, board: chess.Board, depth, alpha, beta):

"""

Alpha-Beta pruning with several advanced enhancements.

"""

self.nodes += 1

# Transposition table lookup

tt_key = chess.polyglot.zobrist_hash(board)

tt_entry = self.tt.retrieve(tt_key)

if tt_entry and tt_entry[0] >= depth:

flag, score = tt_entry[1], tt_entry[2]

if flag == "exact":

return score, tt_entry[3]

elif flag == "lowerbound" and score > alpha:

alpha = score

elif flag == "upperbound" and score < beta:

beta = score

if alpha >= beta:

return score, tt_entry[3]

# Base case for recursion

if depth == 0 or board.is_game_over():

return self.evaluate_board(board), None

# Quiescence search (prevents horizon effect)

if depth <= 2:

return self.quiescence_search(board, alpha, beta), None

# Move ordering heuristics

moves = sorted(list(board.legal_moves), key=lambda m: self.score_move(board, m), reverse=True)

best_move = None

for move in moves:

board.push(move)

score, _ = self.alphabeta(board, depth - 1, -beta, -alpha)

board.pop()

score = -score

if score > alpha:

alpha = score

best_move = move

self.tt.store(tt_key, depth, "exact", score, best_move)

if alpha >= beta:

self.tt.store(tt_key, depth, "lowerbound", score, best_move)

return alpha, best_move

self.tt.store(tt_key, depth, "upperbound", alpha, best_move)

return alpha, best_move

def quiescence_search(self, board, alpha, beta):

"""

Searches only "noisy" moves (captures and promotions) at the end of the search.

"""

self.nodes += 1

stand_pat = self.evaluate_board(board)

if stand_pat >= beta:

return beta

alpha = max(alpha, stand_pat)

for move in self.get_noisy_moves(board):

board.push(move)

score = -self.quiescence_search(board, -beta, -alpha)

board.pop()

if score >= beta:

return beta

alpha = max(alpha, score)

return alpha

def score_move(self, board, move):

"""

Move ordering heuristic. Scores moves based on desirability.

This is a simplified example; a real engine would use more complex scores.

"""

# Prioritize captures using Most Valuable Victim - Least Valuable Aggressor

if board.is_capture(move):

victim = board.piece_at(move.to_square).piece_type

aggressor = board.piece_at(move.from_square).piece_type

# Material value mapping

piece_values = {chess.PAWN: 1, chess.KNIGHT: 3, chess.BISHOP: 3, chess.ROOK: 5, chess.QUEEN: 9, chess.KING: 10}

return 10 * piece_values.get(victim, 0) - piece_values.get(aggressor, 0)

# Consider killer moves

if move in self.killer_moves.get(board.ply(), []):

return 900

return 0

def evaluate_board(self, board: chess.Board):

"""

Advanced material and position evaluation.

Scores are in centipawns.

"""

if board.is_checkmate():

return -float('inf') if board.turn == chess.WHITE else float('inf')

if board.is_stalemate() or board.is_insufficient_material() or board.is_fivefold_repetition():

return 0

score = 0

piece_values = {

chess.PAWN: 100, chess.KNIGHT: 320, chess.BISHOP: 330,

chess.ROOK: 500, chess.QUEEN: 900

}

# Material evaluation

for piece_type in piece_values:

score += len(board.pieces(piece_type, chess.WHITE)) * piece_values[piece_type]

score -= len(board.pieces(piece_type, chess.BLACK)) * piece_values[piece_type]

# Piece-Square Tables (advanced evaluation)

# These are simple examples. Real tables are much more detailed.

pawn_table_white = [

0, 0, 0, 0, 0, 0, 0, 0,

5, 10, 10,-20,-20, 10, 10, 5,

5, -5,-10, 0, 0,-10, -5, 5,

0, 0, 0, 20, 20, 0, 0, 0,

5, 5, 10, 25, 25, 10, 5, 5,

10, 10, 20, 30, 30, 20, 10, 10,

50, 50, 50, 50, 50, 50, 50, 50,

0, 0, 0, 0, 0, 0, 0, 0

]

pawn_table_black = pawn_table_white[::-1]

for i in range(64):

if board.piece_at(i) and board.piece_at(i).piece_type == chess.PAWN:

if board.piece_at(i).color == chess.WHITE:

score += pawn_table_white[i]

else:

score -= pawn_table_black[i]

return score if board.turn == chess.WHITE else -score

def get_noisy_moves(self, board):

"""Returns only capture and promotion moves."""

moves = []

for move in board.legal_moves:

if board.is_capture(move) or move.promotion:

moves.append(move)

return moves

# Example usage for interacting with a UCI-like interface

if __name__ == "__main__":

engine = AdvancedChessEngine()

board = chess.Board()

print("UCI-like command line interface. Type 'uci' to begin.")

while True:

try:

command = input().strip()

if command == "uci":

print("id name Advanced Python Engine")

print("id author [Your Name]")

print("uciok")

elif command.startswith("isready"):

print("readyok")

elif command.startswith("ucinewgame"):

engine = AdvancedChessEngine()

elif command.startswith("position"):

parts = command.split()

if "fen" in parts:

fen_index = parts.index("fen") + 1

fen = " ".join(parts[fen_index:])

board = chess.Board(fen)

else:

board = chess.Board()

if "moves" in parts:

moves_index = parts.index("moves") + 1

for move_str in parts[moves_index:]:

move = chess.Move.from_uci(move_str)

board.push(move)

elif command.startswith("go"):

best_move = engine.search_move(board)

if best_move:

print(f"bestmove {best_move.uci()}")

elif command.startswith("quit"):

break

except Exception as e:

print(f"Error: {e}")