I have implemented minimax algorithm for an unbeatable tic toc toe game. The minimax algorithm is recursive, performing big iterations, which are not big for the computer, only big for humans. The very first step takes about 3 seconds to perform on codepen, but performs instantly on my computer locally. what's wrong? is my code not efficient? or it's a problem on codepen? How do it make it efficient on codepen? Here's my JS code:
var player; var opponent; var myMove = false; var board = [ [null, null, null], [null, null, null], [null, null, null] ]; //displays modal $('.play').click(function() { $('.modal').css("display", "block"); }); $('.O').click(function() { $('.modal').css("display", "none"); player = 'O'; opponent = 'X'; resetGame(); updateSymbol(); main(); }); $('.X').click(function() { $('.modal').css("display", "none"); player = 'X'; opponent = 'O'; resetGame(); updateSymbol(); main(); }); function main() { $(".col-xs-4").click(function() { if ($(this).is(':empty')) { var cell = $(this).attr("id"); var row = parseInt(cell[1]); var col = parseInt(cell[2]); if (!myMove) { board[row][col] = false; myMove = true; updateMove(); makeMove(); } } }); } function checkWin(board) { vals = [true, false]; var allNotNull = true; for (var k = 0; k < vals.length; k++) { var value = vals[k]; // Check rows, columns, and diagonals for win var diagonalComplete1 = true; var diagonalComplete2 = true; for (var i = 0; i < 3; i++) { if (board[i][i] != value) { diagonalComplete1 = false; } if (board[2 - i][i] != value) { diagonalComplete2 = false; } var rowComplete = true; var colComplete = true; for (var j = 0; j < 3; j++) { if (board[i][j] != value) { rowComplete = false; } if (board[j][i] != value) { colComplete = false; } if (board[i][j] === null) { allNotNull = false; } } if (rowComplete || colComplete) { return value ? 1 : 0; } } if (diagonalComplete1 || diagonalComplete2) { return value ? 1 : 0; } } if (allNotNull) { return -1; } return null; } function resetGame() { board = [ [null, null, null], [null, null, null], [null, null, null] ]; myMove = false; for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { $("#" + "b" + i + "" + j).text("").css("background", "#222"); } } $('h3').css("display", "none").text(""); } function updateMove() { updateSymbol(); var winner = checkWin(board); if (winner === 1) { $('h3').css("display", "block").append(opponent + " won!!").addClass("animated infinite pulse"); markWin(); setTimeout(resetGame, 2000); } else if (winner === 0) { $('h3').css("display", "block").append(player + ' won!!').addClass("animated infinite pulse"); markWin(); setTimeout(resetGame, 2000); } else if (winner === -1) { $('h3').css("display", "block").append("It's a Draw!!").addClass("animated infinite pulse"); markWin(); setTimeout(resetGame, 2000); } } function updateSymbol() { for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { if (board[i][j] === false && $("#" + "b" + i + "" + j).is(':empty')) { $("#" + "b" + i + "" + j).text(player); } else if (board[i][j] === true && $("#" + "b" + i + "" + j).is(':empty')) { $("#" + "b" + i + "" + j).text(opponent); } } } } function markWin() { var arr = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7] ]; for (var i = 0; i < arr.length; i++) { if ($('.' + 'b' + arr[i][0] + ':contains("O")').length > 0 && $('.' + 'b' + arr[i][1] + ':contains("O")').length > 0 && $('.' + 'b' + arr[i][2] + ':contains("O")').length > 0) { $('.' + 'b' + arr[i][0]).css("background", "green"); $('.' + 'b' + arr[i][1]).css("background", "green"); $('.' + 'b' + arr[i][2]).css("background", "green"); i = arr.length; } } //checks O win for (var j = 0; j < arr.length; j++) { if ($('.' + 'b' + arr[j][0] + ':contains("X")').length > 0 && $('.' + 'b' + arr[j][1] + ':contains("X")').length > 0 && $('.' + 'b' + arr[j][2] + ':contains("X")').length > 0) { $('.' + 'b' + arr[j][0]).css("background", "green"); $('.' + 'b' + arr[j][1]).css("background", "green"); $('.' + 'b' + arr[j][2]).css("background", "green"); j = arr.length; } } //checks X win return false; } //[0,0] [0,1] [0,2] //[1,0] [1,1] [1,2] //[2,0] [2,1] [2,2] function minimax(board, player) { nodes++; var winner = checkWin(board); if (winner !== null) { switch (winner) { case 1: // AI wins return [1, board]; case 0: // opponent wins return [-1, board]; case -1: // Tie return [0, board]; } } else { // Next states var nextVal = null; var nextBoard = null; for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { if (board[i][j] === null) { board[i][j] = player; var value = minimax(board, !player)[0]; if ((player && (nextVal === null || value > nextVal)) || (!player && (nextVal === null || value < nextVal))) { nextBoard = board.map(function(arr) { return arr.slice(); }); nextVal = value; } board[i][j] = null; } } } return [nextVal, nextBoard]; } } function minimaxMove(board) { nodes = 0; return minimax(board, true)[1]; } function makeMove() { board = minimaxMove(board); console.log(nodes); myMove = false; if (nodes !== 1) { updateMove(); } }
Here's the codepen link of my code: http://codepen.io/makkBit/pen/JXgdEo/