Skip to main content

To challenge myself, I set out to create a tic-tac-toe game, complete with a Minimax-driven AI. I had already attempted this challenge once when I was following the FreeCodeCamp curriculum.

For this review I would kindly ask everyone who is nice enough to give it a moment of their time to please advise me if my approach is the best way of interacting with the DOM? I am currently using objectobjects with DOM elements as properties. This avoids having to use IDs, classes or data attributes. I am still very much a beginner so I am trying to use Vanilla JS as much as possible, figuring it would give me a good basis for any frameworks I might decide to learn.

I also have a question about performance. I know it is not the best, but my understanding of JS/CS in general is not deep enough to know how to better it. As it stands, the initial call to MinimaxMinimax() takes a whopping 1080 ms on average.

AFor a working Pen:example, see https://codepen.io/martialis39/pen/dzLOKd;this code Pen.

TheHere is the JS for easy reference:

To challenge myself I set out to create a tic-tac-toe game, complete with a Minimax-driven AI. I had already attempted this challenge once when I was following the FreeCodeCamp curriculum.

For this review I would kindly ask everyone who is nice enough to give it a moment of their time to please advise me if my approach is the best way of interacting with the DOM? I am currently using object with DOM elements as properties. This avoids having to use IDs, classes or data attributes. I am still very much a beginner so I am trying to use Vanilla JS as much as possible, figuring it would give me a good basis for any frameworks I might decide to learn.

I also have a question about performance. I know it is not the best, but my understanding of JS/CS in general is not deep enough to know how to better it. As it stands, the initial call to Minimax takes a whopping 1080 ms on average.

A working Pen: https://codepen.io/martialis39/pen/dzLOKd;

The JS for easy reference:

To challenge myself, I set out to create a tic-tac-toe game, complete with a Minimax-driven AI. I had already attempted this challenge once when I was following the FreeCodeCamp curriculum.

For this review I would kindly ask everyone who is nice enough to give it a moment of their time to please advise me if my approach is the best way of interacting with the DOM? I am currently using objects with DOM elements as properties. This avoids having to use IDs, classes or data attributes. I am still very much a beginner so I am trying to use Vanilla JS as much as possible, figuring it would give me a good basis for any frameworks I might decide to learn.

I also have a question about performance. I know it is not the best, but my understanding of JS/CS in general is not deep enough to know how to better it. As it stands, the initial call to Minimax() takes a whopping 1080 ms on average.

For a working example, see this code Pen.

Here is the JS for easy reference:

deleted 38 characters in body; edited tags; edited title
Source Link
200_success
  • 145.7k
  • 22
  • 191
  • 481

JS Tic Tac Toe using Minimax algorithm; beginneralgorithm

Thank you for any and all input.

JS Tic Tac Toe using Minimax algorithm; beginner

Thank you for any and all input.

JS Tic Tac Toe using Minimax algorithm

Tweeted twitter.com/StackCodeReview/status/906870363849740289
Source Link

JS Tic Tac Toe using Minimax algorithm; beginner

To challenge myself I set out to create a tic-tac-toe game, complete with a Minimax-driven AI. I had already attempted this challenge once when I was following the FreeCodeCamp curriculum.

For this review I would kindly ask everyone who is nice enough to give it a moment of their time to please advise me if my approach is the best way of interacting with the DOM? I am currently using object with DOM elements as properties. This avoids having to use IDs, classes or data attributes. I am still very much a beginner so I am trying to use Vanilla JS as much as possible, figuring it would give me a good basis for any frameworks I might decide to learn.

I also have a question about performance. I know it is not the best, but my understanding of JS/CS in general is not deep enough to know how to better it. As it stands, the initial call to Minimax takes a whopping 1080 ms on average.

Thank you for any and all input.

A working Pen: https://codepen.io/martialis39/pen/dzLOKd;

The JS for easy reference:

const BOARD = document.querySelector('.board');
const BUTTONS = document.querySelector('.buttons')
let GRID = [];
function Player () {

  this.state = 0;
  this.symbol;
  this.moveCount = 0;
}
function PlayerAI () {
  this.state = 1;
  this.symbol;
  this.moveCount;
  this.moveCountClone;
}

function Game (state) {
  this.running = state;
}
let game = new Game(true);
let player = new Player;
let playerAI = new PlayerAI;

function init(){
  game.running = true;
  BUTTONS.addEventListener('click', function(e){

    player.symbol = e.target.textContent;

    player.symbol == 'O' ? playerAI.symbol = 'X' : playerAI.symbol = 'O';
    BUTTONS.classList.add('hidden');
    GRID.forEach(function(el){
      el.activate()
    })
    startGame(player, playerAI);

  })

}

// CELL FILE
let count = 1;
let fakeBoard = [2,2,2,0,0,0,2,2,2];

function Cell (index) {
  this.index = index;
  this.state = 2;// default state;
  this.parent = BOARD;
  this.DOMElement = document.createElement("div");
}

Cell.prototype.render = function () {

  this.parent.appendChild(this.DOMElement);
  this.DOMElement.classList.add("cell");
}

Cell.prototype.activate = function() {
  this.DOMElement.classList.add("active");
}

// draw grid
for(var i = 0; i < 9; i++){
  GRID.push(new Cell(i));
  GRID[i].render();
}

// END CELL FILE


function startGame (){

    GRID.forEach(function(cell){
    cell.DOMElement.addEventListener('click', function(){
      if(cell.DOMElement.textContent == ''){
        cell.DOMElement.classList.remove('active');
        cell.DOMElement.textContent = player.symbol;
        cell.state = player.state;
        handleWin(checkForTheWin(GRID, winStateMatrix, player))
        if(game.running === true){
            AIMove();
        }
      }
    });
  })
}


function AIMove () {
    let myMoves = possibleMoves(GRID);

    if(myMoves.length > 0){
    let myMove = myMoves[0].index;


    let next = miniMax(GRID, 0);

    makeMove(GRID, next.index, playerAI)
    displayMoveOnBoard(next.index, playerAI)
    handleWin(checkForTheWin(GRID, winStateMatrix, playerAI));

  }
}
// ai 1, human 0


function score(int, depth){
  if(int  == 1){
    return 100 - depth;
  }
  if(int == 0){
    return -100 + depth;
  }
  if(int == 2){
    return 0; // prefer draw, should be 0;
  }
}


function miniMax(gameState, depth){

    let moves = possibleMoves(gameState);
    bestMove = moves[0];
    bestScore = -1000;
    moves.forEach(function(move){
      let clone = JSON.parse(JSON.stringify(gameState));
      makeMove(clone, move.index, playerAI);
      let score = min(clone, depth+1);
      if(score > bestScore){
        bestMove = move;
        bestScore = score;
      }
    })
    return bestMove;
}
function max(gameState, depth){
  let check = checkForTheWin(gameState, winStateMatrix, player);
  if(check >= 0){
    return score(check, depth);
  }
  let moves = possibleMoves(gameState);
  let bestScore = -1000;
  let bestMove;
  moves.forEach(function(move){
      let clone = JSON.parse(JSON.stringify(gameState));
      makeMove(clone, move.index, playerAI);
      let score_value = min(clone, depth+1);
      if(score_value > bestScore){
        bestScore = score_value;
        bestMove = move;
      }
  })
  return bestScore;
}


function min(gameState, depth){
  let check = checkForTheWin(gameState, winStateMatrix, playerAI);
  if(check){
    return score(check, depth);
  }
  let moves = possibleMoves(gameState);
  let bestScore = 1000;
  let bestMove;
  moves.forEach(function(move){
      let clone = JSON.parse(JSON.stringify(gameState));
      makeMove(clone, move.index, player);
      let score_value = max(clone, depth+1);
      if(score_value < bestScore){
        bestScore = score_value;
        bestMove = move;
      }
  })
  return bestScore;
}

let winStateMatrix = [[0,1,2],
                      [3,4,5],
                      [6,7,8],
                      [0,3,6],
                      [1,4,7],
                      [2,5,8],
                      [0,4,8],
                      [2,4,6]]


function checkForTheWin(gamestate, winMatrix, whichPlayer){

  let stateOne = 1;
  let stateZero = 0;
  for(let i = 0; i < winMatrix.length; i++){

   if(gamestate[winMatrix[i][0]].state == whichPlayer.state && gamestate[winMatrix[i][1]].state == whichPlayer.state && gamestate[winMatrix[i][2]].state == whichPlayer.state){
     if(whichPlayer.state == stateOne){

       return 1;
     }
     if(whichPlayer.state == stateZero){
       //
       return 0;
     }
   }
  }

  let moves = possibleMoves(gamestate);

  if(!moves){
    return 2;
  }
}

function handleWin(potentialWin){
  //takes in checkforthewin function
  if(potentialWin == 2){
    game.running = false;
    alert('DRAW')
    window.location.reload(true);
  }
  if(potentialWin == 1){
    game.running = false;
    alert('AI WIN, RELOAD THE PAGE TO PLAY AGAIN')
    window.location.reload(true);
  }
  if(potentialWin == 0){
    game.running = false;
    alert('PLAYER WIN, RELOAD THE PAGE TO PLAY AGAIN')
    window.location.reload(true);
  }

}

function possibleMoves (gamestate){

    let arr = gamestate.filter(function(el){
      return el.state == 2;
    })
    if(arr.length < 1){
      return false;
    } else {
      return arr;
    }
}

function displayMoveOnBoard(moveAsIndex, whichPlayer){
  GRID[moveAsIndex].DOMElement.classList.remove('active');
  GRID[moveAsIndex].DOMElement.textContent = whichPlayer.symbol;
}

function makeMove(board, moveAsIndex, whichPlayer){
  board[moveAsIndex].state = whichPlayer.state;
}

init();