2
\$\begingroup\$

Currently, I am using an algorithm that finds the best move based on the existing states of the board. Is there a better way to do it? Is there a data structure that I can use?

I have also considered the MinMax algorithm, but did not want to use it as it checks for every possibility on every turn.

var playerSym;
var computerSym;
var turnCount=0;
var playerWin=0;
var compWin=0;
var whoseTurn;
var grid=[];
var winFlag=false;

function initialiseGrid(){

  var k=0;
  for(var i=0;i<9;i++){


  grid[i]=document.getElementById(k);
  k++;

}

 }

function enterPlayerChoice(eventSrc){

  if(!(document.getElementById("x").checked||document.getElementById("o").checked)){

    alert("Choose Your Symbol");
    return;

  }

  var target=eventSrc.target;
  if(target.innerText!=="")
    return;

  turnCount++;
  var txtNode=document.createTextNode(playerSym);
  target.appendChild(txtNode);
  //playerMoves.push(Number(target.id));

  whoseTurn="Player";

  if(turnCount>3)
    checkWin();

  if(turnCount>8&&winFlag==false){
    if(confirm("Its A Draw!!"))
      gameReset();
  }
  else
    computersTurn();

}

function computersTurn(){

  whoseTurn="Computer";
  if(document.getElementById("easy").checked)
    playEasy();

  else
    playHard();

}

function getRandomNum(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function playEasy(){

    turnCount++;
    var boxNum=getRandomNum(0,8);

    while(document.getElementById(boxNum).innerText!=="")
      boxNum=getRandomNum(0,8);

    insertCompChoice(boxNum);

    if(turnCount>3)
      checkWin();

    if(turnCount>8&&winFlag==false)
      if(confirm("Its A Draw!!"))
        gameReset();

}

function insertCompChoice(boxNum){

    var txtNode=document.createTextNode(computerSym);
    document.getElementById(boxNum).appendChild(txtNode);

}

function checkForWin(){

  //diagonal left to right
  if((grid[0].innerText==computerSym&&grid[4].innerText==computerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}
  else if((grid[0].innerText==computerSym&&grid[8].innerText==computerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==computerSym&&grid[8].innerText==computerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}

  //diagonal right to left
  else if((grid[2].innerText==computerSym&&grid[4].innerText==computerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[6].innerText==computerSym&&grid[2].innerText==computerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==computerSym&&grid[6].innerText==computerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}

  //first row
  else if((grid[0].innerText==computerSym&&grid[1].innerText==computerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}  
  else if((grid[1].innerText==computerSym&&grid[2].innerText==computerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}
  else if((grid[0].innerText==computerSym&&grid[2].innerText==computerSym)&&(document.getElementById(grid[1].id).innerText==="")){
      return Number(grid[1].id);}

  //second row
  else if((grid[3].innerText==computerSym&&grid[5].innerText==computerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}  
  else if((grid[3].innerText==computerSym&&grid[4].innerText==computerSym)&&(document.getElementById(grid[5].id).innerText==="")){
      return Number(grid[5].id);}
  else if((grid[4].innerText==computerSym&&grid[5].innerText==computerSym)&&(document.getElementById(grid[3].id).innerText==="")){
      return Number(grid[3].id);}

  //third Row
  else if((grid[7].innerText==computerSym&&grid[8].innerText==computerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[6].innerText==computerSym&&grid[7].innerText==computerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}
  else if((grid[8].innerText==computerSym&&grid[6].innerText==computerSym)&&(document.getElementById(grid[7].id).innerText==="")){
      return Number(grid[7].id);}

  //first coloumn
  else if((grid[0].innerText==computerSym&&grid[3].innerText==computerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[0].innerText==computerSym&&grid[6].innerText==computerSym)&&(document.getElementById(grid[3].id).innerText==="")){
      return Number(grid[3].id);}
  else if((grid[3].innerText==computerSym&&grid[6].innerText==computerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}

  //second column
  else if((grid[1].innerText==computerSym&&grid[4].innerText==computerSym)&&(document.getElementById(grid[7].id).innerText==="")){
      return Number(grid[7].id);}  
  else if((grid[1].innerText==computerSym&&grid[7].innerText==computerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==computerSym&&grid[7].innerText==computerSym)&&(document.getElementById(grid[1].id).innerText==="")){
      return Number(grid[1].id);}

  //third column
  else if((grid[2].innerText==computerSym&&grid[5].innerText==computerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}  
  else if((grid[8].innerText==computerSym&&grid[2].innerText==computerSym)&&(document.getElementById(grid[5].id).innerText==="")){
      return Number(grid[5].id);}
  else if((grid[5].innerText==computerSym&&grid[8].innerText==computerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}

  return -1;


}

function blockOppWin(){

  //diagonal left to right
  if((grid[0].innerText==playerSym&&grid[4].innerText==playerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}
  else if((grid[0].innerText==playerSym&&grid[8].innerText==playerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==playerSym&&grid[8].innerText==playerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}

  //diagonal right to left
  else if((grid[2].innerText==playerSym&&grid[4].innerText==playerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[6].innerText==playerSym&&grid[2].innerText==playerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==playerSym&&grid[6].innerText==playerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}

  //first row
  else if((grid[0].innerText==playerSym&&grid[1].innerText==playerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}  
  else if((grid[1].innerText==playerSym&&grid[2].innerText==playerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}
  else if((grid[0].innerText==playerSym&&grid[2].innerText==playerSym)&&(document.getElementById(grid[1].id).innerText==="")){
      return Number(grid[1].id);}

  //second row
  else if((grid[3].innerText==playerSym&&grid[5].innerText==playerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}  
  else if((grid[3].innerText==playerSym&&grid[4].innerText==playerSym)&&(document.getElementById(grid[5].id).innerText==="")){
      return Number(grid[5].id);}
  else if((grid[4].innerText==playerSym&&grid[5].innerText==playerSym)&&(document.getElementById(grid[3].id).innerText==="")){
      return Number(grid[3].id);}

  //third Row
  else if((grid[7].innerText==playerSym&&grid[8].innerText==playerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[6].innerText==playerSym&&grid[7].innerText==playerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}
  else if((grid[8].innerText==playerSym&&grid[6].innerText==playerSym)&&(document.getElementById(grid[7].id).innerText==="")){
      return Number(grid[7].id);}

  //first coloumn
  else if((grid[0].innerText==playerSym&&grid[3].innerText==playerSym)&&(document.getElementById(grid[6].id).innerText==="")){
      return Number(grid[6].id);}  
  else if((grid[0].innerText==playerSym&&grid[6].innerText==playerSym)&&(document.getElementById(grid[3].id).innerText==="")){
      return Number(grid[3].id);}
  else if((grid[3].innerText==playerSym&&grid[6].innerText==playerSym)&&(document.getElementById(grid[0].id).innerText==="")){
      return Number(grid[0].id);}

  //second column
  else if((grid[1].innerText==playerSym&&grid[4].innerText==playerSym)&&(document.getElementById(grid[7].id).innerText==="")){
      return Number(grid[7].id);}  
  else if((grid[1].innerText==playerSym&&grid[7].innerText==playerSym)&&(document.getElementById(grid[4].id).innerText==="")){
      return Number(grid[4].id);}
  else if((grid[4].innerText==playerSym&&grid[7].innerText==playerSym)&&(document.getElementById(grid[1].id).innerText==="")){
      return Number(grid[1].id);}

  //third column
  else if((grid[2].innerText==playerSym&&grid[5].innerText==playerSym)&&(document.getElementById(grid[8].id).innerText==="")){
      return Number(grid[8].id);}  
  else if((grid[8].innerText==playerSym&&grid[2].innerText==playerSym)&&(document.getElementById(grid[5].id).innerText==="")){
      return Number(grid[5].id);}
  else if((grid[5].innerText==playerSym&&grid[8].innerText==playerSym)&&(document.getElementById(grid[2].id).innerText==="")){
      return Number(grid[2].id);}


  return -1;

}

function blockOppFork(){

  if(grid[0].innerText==playerSym&&grid[4].innerText==playerSym&&grid[8].innerText==computerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);
    else if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);
  }

  if(grid[4].innerText==playerSym&&grid[8].innerText==playerSym&&grid[0].innerText==computerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);
    else if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);
  }

  if(grid[0].innerText==playerSym&&grid[8].innerText==playerSym&&grid[4].innerText==computerSym){
    if(document.getElementById(grid[1].id).innerText==="")
      return Number(grid[1].id);
    else if(document.getElementById(grid[3].id).innerText==="")
      return Number(grid[3].id);
    else if(document.getElementById(grid[5].id).innerText==="")
      return Number(grid[5].id);
    else if(document.getElementById(grid[7].id).innerText==="")
      return Number(grid[7].id);
  }

  if(grid[4].innerText==computerSym&&grid[3].innerText==playerSym&&grid[7].innerText==playerSym){
    if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);

  }

  if(grid[4].innerText==computerSym&&grid[3].innerText==playerSym&&grid[1].innerText==playerSym){
    if(document.getElementById(grid[0].id).innerText==="")
      return Number(grid[0].id);

  }

  if(grid[4].innerText==computerSym&&grid[1].innerText==playerSym&&grid[5].innerText==playerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);

  }

  if(grid[4].innerText==computerSym&&grid[5].innerText==playerSym&&grid[7].innerText==playerSym){
    if(document.getElementById(grid[8].id).innerText==="")
      return Number(grid[8].id);

  }

  return -1;

}

function createFork(){

  if(grid[0].innerText==computerSym&&grid[4].innerText==computerSym&&grid[8].innerText==playerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);
    else if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);
  }

  if(grid[4].innerText==computerSym&&grid[8].innerText==computerSym&&grid[0].innerText==playerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);
    else if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);
  }

  if(grid[0].innerText==computerSym&&grid[8].innerText==computerSym&&grid[4].innerText==playerSym){
    if(document.getElementById(grid[1].id).innerText==="")
      return Number(grid[1].id);
    else if(document.getElementById(grid[3].id).innerText==="")
      return Number(grid[3].id);
    else if(document.getElementById(grid[5].id).innerText==="")
      return Number(grid[5].id);
    else if(document.getElementById(grid[7].id).innerText==="")
      return Number(grid[7].id);
  }

  if(grid[4].innerText==playerSym&&grid[3].innerText==computerSym&&grid[7].innerText==computerSym){
    if(document.getElementById(grid[6].id).innerText==="")
      return Number(grid[6].id);

  }

  if(grid[4].innerText==playerSym&&grid[3].innerText==computerSym&&grid[1].innerText==computerSym){
    if(document.getElementById(grid[0].id).innerText==="")
      return Number(grid[0].id);

  }

  if(grid[4].innerText==playerSym&&grid[1].innerText==computerSym&&grid[5].innerText==computerSym){
    if(document.getElementById(grid[2].id).innerText==="")
      return Number(grid[2].id);

  }

  if(grid[4].innerText==playerSym&&grid[5].innerText==computerSym&&grid[7].innerText==computerSym){
    if(document.getElementById(grid[8].id).innerText==="")
      return Number(grid[8].id);

  }

  return -1;

}

function playHard(){


  /*
  //////////////////////
  The first two turns decide the game.

  First Turn:
  Take corner or center.

  Second Turn:
  If Center is empty take it.
  If not, take a corner.

  From third turn onwards,
    1) play for win
    2) block opp win
    3) block fork
    4) create fork to win
    5) play center if open
    6) play corner 
    7) If AI reaches this stage, all possible win combinations are gone. Play a random box(mostly line centers) and draw the game.
  //////////////////////
  */
  turnCount++;
  var boxNum;

  if((turnCount==1)){

    boxNum=getRandomNum(0,8);

      while((document.getElementById(boxNum).innerText!=="")||(boxNum==1||boxNum==3||boxNum==5||boxNum==7)){
        //alert(boxNum==1||boxNum==3||boxNum==5||boxNum==7);
        boxNum=getRandomNum(0,8);

      }


    insertCompChoice(boxNum);

  }

  else if(turnCount==2){

    if(document.getElementById("4").innerText==="")
      boxNum=4;

    else{

      boxNum=getRandomNum(0,8);

      while((document.getElementById(boxNum).innerText!=="")||(boxNum==1||boxNum==3||boxNum==5||boxNum==7))
          boxNum=getRandomNum(0,8);

    }

    insertCompChoice(boxNum);
  }

  else if(turnCount>2){
    //alert("s");
    //alert(checkForWin()+"win");
      //alert(blockOppWin()+"bloxck");

    if(checkForWin()!==-1){
      boxNum=checkForWin();
      insertCompChoice(boxNum);
      checkWin();
    }

    else if(blockOppWin()!==-1){
      boxNum=blockOppWin();
      insertCompChoice(boxNum);
    }

    else if(blockOppFork()!==-1){

      boxNum=blockOppFork();
      insertCompChoice(boxNum);

    }

    else if(createFork()!==-1){

      boxNum=createFork();
      insertCompChoice(boxNum);

    }

    else if(document.getElementById("4").innerText==="")
      insertCompChoice(4);

    else if(document.getElementById("0").innerText===""||document.getElementById("2").innerText===""||document.getElementById("8").innerText===""||document.getElementById("6").innerText===""){

      boxNum=getRandomNum(0,8);

      while((document.getElementById(boxNum).innerText!=="")||(boxNum==1||boxNum==3||boxNum==5||boxNum==7))
          boxNum=getRandomNum(0,8);

      insertCompChoice(boxNum);

    }

    else{

      boxNum=getRandomNum(0,8);
      while(document.getElementById(boxNum).innerText!=="")
          boxNum=getRandomNum(0,8);

      insertCompChoice(boxNum);

    }

  }

  if(turnCount>3)
      checkWin();

  if(turnCount>8&&winFlag==false)
    if(confirm("Its A Draw!!"))
       gameReset();

}

function checkWin(){

  var symToCompare;

  if(whoseTurn=="Player")
    symToCompare=playerSym;
  else if(whoseTurn=="Computer")
    symToCompare=computerSym;

  if(grid[0].innerText==symToCompare&&grid[1].innerText==symToCompare&&grid[2].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[3].innerText==symToCompare&&grid[4].innerText==symToCompare&&grid[5].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[6].innerText==symToCompare&&grid[7].innerText==symToCompare&&grid[8].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[0].innerText==symToCompare&&grid[3].innerText==symToCompare&&grid[6].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[1].innerText==symToCompare&&grid[4].innerText==symToCompare&&grid[7].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[2].innerText==symToCompare&&grid[5].innerText==symToCompare&&grid[8].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[0].innerText==symToCompare&&grid[4].innerText==symToCompare&&grid[8].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

  if(grid[2].innerText==symToCompare&&grid[4].innerText==symToCompare&&grid[6].innerText==symToCompare){
    winFlag=true;
    if(confirm(whoseTurn+" wins. Play Again??"))
      gameReset();

  }

}

function gameReset(){

  var cells=document.getElementsByClassName("box");

  var i=0;
  while(i<cells.length)
    cells[i++].innerText="";

  turnCount=0;
  whoseTurn="";
  winFlag=false;

}


window.addEventListener("load",initialiseGrid);

document.getElementById("x").addEventListener("click",function(){

  playerSym="X";
  computerSym="O";

});

document.getElementById("o").addEventListener("click",function(){

  playerSym="O";
  computerSym="X";

});

document.getElementById("reset").addEventListener("click",gameReset);

document.getElementById("0").addEventListener("click",enterPlayerChoice);
document.getElementById("1").addEventListener("click",enterPlayerChoice);
document.getElementById("2").addEventListener("click",enterPlayerChoice);
document.getElementById("3").addEventListener("click",enterPlayerChoice);
document.getElementById("4").addEventListener("click",enterPlayerChoice);
document.getElementById("5").addEventListener("click",enterPlayerChoice);
document.getElementById("6").addEventListener("click",enterPlayerChoice);
document.getElementById("7").addEventListener("click",enterPlayerChoice);
document.getElementById("8").addEventListener("click",enterPlayerChoice);

Full code here.

Also, is there any replacement for the ugly "if-else" that I can implement in the code?

\$\endgroup\$
2
  • \$\begingroup\$ I don't see why you don't want to use MiniMax, unless you want sub-optimal AI (you can probably even do that with MiniMax by limiting the stack depth). There are at most 9 possible moves, and the number of possible moves decreases each turn. It is the fastest optimal algorithm for a game like TicTacToe, and also very easy to implement. Although, it will cause ties all the time unless you decrease the depth of the MiniMax recursive search. \$\endgroup\$
    – EvilTak
    Commented May 26, 2016 at 17:10
  • \$\begingroup\$ Tic Tac Toe has a small search space. I did not want to use MinMax as it executes till the end of game on each turn. Eg. if there is a win, no need to check further. \$\endgroup\$
    – leoOrion
    Commented May 27, 2016 at 5:29

1 Answer 1

1
\$\begingroup\$

You need to give your operators some space, minify it later with a standard minifier if you need to make it smaller or remove spaces or whatever.

Also use Curly braces when nesting if statements, otherwise it gets really messy and hard to read. throughout your code I saw the lack of braces and it made the reading a little difficult. in one spot you didn't even indent the code inside the if statement even though it was on a new line, either do it on the same line or add braces.

in JavaScript the else statement is on the same line as the end curly brace for the if block, you will see what I mean in a second.

If you want to negate a boolean use a "bang"(!) instead of == false.

Your code:

if(turnCount>8&&winFlag==false){
  if(confirm("Its A Draw!!"))
  gameReset();
}
else
  computersTurn();

My version

if (turnCount > 8 && !winFlag){
    if(confirm("Its A Draw!!")) gameReset();
} else {
    computersTurn();
}

You may encounter a bug here if the user clicks cancel instead of OK because the function gameReset() won't be called. something to figure out in the next iteration. You probably should use an alert instead of a confirm

You check for a draw all over the place, and it makes me want to create a function for that instead of re-writing that over and over again, I would also use the alert inside the function and then use the returned boolean to either reset the game or do whatever.

Your Repeated code

if(turnCount>8&&winFlag==false){
  if(confirm("Its A Draw!!"))
      gameReset();

Function and usage:

function IsDraw (turnCount, winFlag) {
    if (turnCount > 8 && !winFlag) {
        alert("It's a draw!!");
        return true;
    }
    return false;
}

if (isDraw) {
    gameReset();
}
\$\endgroup\$
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.