7
\$\begingroup\$

I just completed the Tic Tac Toe challenge at freecodecamp. I am looking for input on how I could shorten the code. For the click events such as s1.onclick, I feel that it could have been shortened in some way, maybe using the this object. I feel as though that my detectWin function has too much code as well.

Codepen

//Game function
    function tictac() {
      "use strict";
      var tableI = document.getElementById("table");
      var X = document.querySelector(".X");
      X.addEventListener("click", function() {
        player.gameSym = "X";
    
        AI.gameSym = "O";
        start();
      });
      var O = document.querySelector(".O");
      O.addEventListener("click", function() {
        player.gameSym = "O";
    
        AI.gameSym = "X";
        start();
      });
      var cells = document.querySelectorAll("td");
      var para = document.querySelectorAll(".first");
      var table = document.querySelector(".tablecon");
      var gameEnd = false;
      var s1 = document.getElementById("s1");
      var s2 = document.getElementById("s2");
      var s3 = document.getElementById("s3");
      var s4 = document.getElementById("s4");
      var s5 = document.getElementById("s5");
      var s6 = document.getElementById("s6");
      var s7 = document.getElementById("s7");
      var s8 = document.getElementById("s8");
      var s9 = document.getElementById("s9");
      var winner = document.querySelector(".winner");
      var td = document.querySelectorAll("td");
      //create player and computer objects
      var player = {
        gameSym: "",
        hasTurn: false,
        hasWon: false
    
      };
    
      var AI = {
        gameSym: "",
        hasTurn: false,
        hasWon: false
    
      };
    //Contains game control functions
      function start() {
    
        for (var i = 0; i < para.length; i++) {
          para[i].style.display = "none";
        }
        table.style.display = "initial";
        //decide who goes first
        var determine = Math.round(Math.random());
    
        player.hasTurn = true;
    //Function for changing the player/AI's turn
        function changeTurn() {
          if (player.hasTurn) {
            player.hasTurn = false;
            AI.hasTurn = true;
            console.log("AI's turn");
            gameControl();
    
          } else if (AI.hasTurn) {
            AI.hasTurn = false;
            player.hasTurn = true;
            console.log("Player's turn");
            gameControl();
    
          }
          detectWin();
        }
    
        //If it's the player's turn, player chooses what cell to click, it is not finished yet. 
        //If it's the AI's turn, AI chooses a random number between 1 and 8, then should pick a cell that's not occupied. 
        var sqcounter = 0;
        //Alternates between player and AI for turns while keeping track of open squares 
        function gameControl() {
          if (sqcounter !== 9) {
            if (player.hasTurn) {
              sqcounter += 1;
              s1.onclick = function() {
                if ($.trim($("#s1").html()) === '') {
                  s1.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s2.onclick = function() {
                if ($.trim($("#s2").html()) === '') {
                  s2.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s3.onclick = function() {
                if ($.trim($("#s3").html()) === '') {
                  s3.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s4.onclick = function() {
                if ($.trim($("#s4").html()) === '') {
                  s4.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s5.onclick = function() {
                if ($.trim($("#s5").html()) === '') {
                  s5.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s6.onclick = function() {
                if ($.trim($("#s6").html()) === '') {
                  s6.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s7.onclick = function() {
                if ($.trim($("#s7").html()) === '') {
                  s7.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s8.onclick = function() {
                if ($.trim($("#s8").html()) === '') {
                  s8.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
              s9.onclick = function() {
                if ($.trim($("#s9").html()) === '') {
                  s9.innerHTML = player.gameSym;
                  changeTurn();
    
                }
              };
    
            } else if (AI.hasTurn) {
              sqcounter += 1;
              var randomch = Math.floor((Math.random() * 8) + 0);
              console.log(randomch + "random ch");
              if (cells[randomch].innerHTML !== "X" && cells[randomch].innerHTML !== "O") {
                cells[randomch].innerHTML = AI.gameSym;
                changeTurn();
    
              } else {
                for (var i = 0; i < cells.length - 1; i++) {
    
                  if (cells[i].innerHTML !== "X" && cells[i].innerHTML !== "O") {
                    console.log(i + "inloop");
                    cells[i].innerHTML = AI.gameSym;
                    changeTurn();
                    break;
                  }
                }
              }
            }
          }
        }
        gameControl();
    
        function detectWin() {
          for (i = 0; i < cells.length; i++) {
            if (cells[0].innerHTML === player.gameSym && cells[1].innerHTML === player.gameSym && cells[2].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[3].innerHTML === player.gameSym && cells[4].innerHTML === player.gameSym && cells[5].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[6].innerHTML === player.gameSym && cells[7].innerHTML === player.gameSym && cells[8].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[0].innerHTML === player.gameSym && cells[3].innerHTML === player.gameSym && cells[6].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[1].innerHTML === player.gameSym && cells[4].innerHTML === player.gameSym && cells[7].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[6].innerHTML === player.gameSym && cells[7].innerHTML === player.gameSym && cells[8].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[0].innerHTML === player.gameSym && cells[4].innerHTML === player.gameSym && cells[8].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[2].innerHTML === player.gameSym && cells[5].innerHTML === player.gameSym && cells[8].innerHTML === player.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[0].innerHTML === AI.gameSym && cells[1].innerHTML === AI.gameSym && cells[2].innerHTML === AI.gameSym) {
              winner.innerHTML = "You have been defeated! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[3].innerHTML === AI.gameSym && cells[5].innerHTML === AI.gameSym && cells[4].innerHTML === AI.gameSym) {
              winner.innerHTML = "You have been defeated! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[3].innerHTML === AI.gameSym && cells[5].innerHTML === AI.gameSym && cells[4].innerHTML === AI.gameSym) {
              winner.innerHTML = "You have been defeated! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[0].innerHTML === AI.gameSym && cells[4].innerHTML === AI.gameSym && cells[8].innerHTML === AI.gameSym) {
              winner.innerHTML = "You have been defeated! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[1].innerHTML === AI.gameSym && cells[4].innerHTML === AI.gameSym && cells[7].innerHTML === AI.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[2].innerHTML === AI.gameSym && cells[5].innerHTML === AI.gameSym && cells[8].innerHTML === AI.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[0].innerHTML === AI.gameSym && cells[3].innerHTML === AI.gameSym && cells[6].innerHTML === AI.gameSym) {
              winner.innerHTML = "You are the winner! A new game will start in 5 seconds.";
              gameEnd = true;
              endReset();
              break;
            } else if (cells[2].innerHTML === AI.gameSym && cells[4].innerHTML === AI.gameSym && cells[8].innerHTML === AI.gameSym) {
              winner.innerHTML = "You have been defeated! A new game will start in 5 seconds.";
              
              endReset();
              break;
            }
          }
        }
    
        function endReset() {
    
          setTimeout(function() {
            if (gameEnd === true) {
              for (var i = 0; i <= 8; i++) {
                td[i].innerHTML = "";
              }
              for (var i = 0; i < para.length; i++) {
                para[i].style.display = "initial";
              }
              sqcounter = 0;
              winner.innerHTML = "";
              tictac();
    
            }
          }, 5000);
    
        }
      }
    }
    
    
    
    tictac();
body { 
background-color: gray;
}
table, td{ 
border: 2px solid black;
margin:0 auto;

}

.tablecon { 
display:none;

}

#table { 
background-color: #47DBE0;
color: #343436;
font-size: 150%;
}

td { 
width: 100px;
height: 100px;
text-align: center;

}

.X, .O { 
display: inline-block;


}

.prompt, tablecon{ 
text-align: center;
margin-top: 75px;
color: black;
}

.prompt, .tablecon { 
font-size: 140%;
}

.winner { 
text-align: center;
color: #FF9D00;
font-size: 130%;
}
    <!-- Make a table for tic tac toe 
board here -->
<div class="prompt">
  <h1>A game of Tic Tac Toe</h2>
  <p class="quest first">Do you want to be X or O?</p>
  <div class="xocon">
  <p class="X first">X</p>
  <p class="O first">O</p>
  </div>
</div>

<div class="tablecon">
  <p class="pscore"></p>
  <p class="cscore"></p>
  <p class="winner"></p>
<table id = "table">
  <tr>
    <td id = "s1">
      
    </td>
    <td id = "s2">
    </td>
    <td id = "s3">
    </td>
  </tr>
  <tr>
    <td id = "s4">
    </td>
    <td id = "s5">
    </td>
    <td id = "s6">
    </td>
  </tr>
  <tr>
    <td id = "s7">
    </td>
    <td id = "s8">
    </td>
    <td id = "s9">
    </td>
  </tr>

</table>
</div>

\$\endgroup\$
1
  • \$\begingroup\$ Why not trying to make it more OOP-like using prototipal inheritance? Also, I don't like the use of a "main" big function that gets called one-time just after its creation. Try to split it and make it more modular \$\endgroup\$ – Zorgatone Nov 22 '16 at 16:50
3
\$\begingroup\$

This game plays OK--if you can use a mouse. If you try to use a keyboard or voice, it is unplayable.

First, your prompt:

<div class="prompt">
  <h1>A game of Tic Tac Toe</h2>
  <p class="quest first">Do you want to be X or O?</p>
  <div class="xocon">
  <p class="X first">X</p>
  <p class="O first">O</p>
  </div>
</div>

You cannot "click" the X or O text with your keyboard since the browser just sees it as a text element with an event handler. These should be button elements; you can style them so they look the same, but now the browser can make the buttons available to the keyboard, so you can TAB to them. Then, you handle the click event in your JS, just the same as you are now.


Now, your gameControl function repeats itself something fierce. For example:

s1.onclick = function() {
  if ($.trim($("#s1").html()) === '') {
    s1.innerHTML = player.gameSym;
    changeTurn();

  }
};
s2.onclick = function() {
  if ($.trim($("#s2").html()) === '') {
    s2.innerHTML = player.gameSym;
    changeTurn();

  }
};

You could put a class on each square, then do something like this (I'm using jQuery here since I'm familiar with it, but you can do it without jQuery too).

$('.square').click(function(e) {
  var square = $(e.target);  // get clicked element

  if ($.trim(square.html()) === '') {
    square.innerHTML = player.gameSym;
    changeTurn();
  }
});

Now you have one block of code that handles all of your squares.

As for your detectWin function, I'd load the data into a 2d array. Then you can loop over your data, as in:

for (let row = 0; row < 3; row++) {
  if (arr[row][0] !== '' &&           // make sure it has an entered value
      arr[row][0] === arr[row][1] &&  // compare square 1 and 2 in the row
      arr[row][0] === arr[row][2])    // compare square 1 and 3 in the row
  {
    // horizontal win--handle text for X/O win
    if (arr[row][0] === 'x') {}
    else if (arr[row][0] === 'o') {}
    break;
  }
}

for (let col = 0; col < 3; col++) {
  if (arr[0][col] !== '' &&           // make sure it has an entered value
      arr[0][col] === arr[1][col] &&  // compare square 1 and 2 in the column
      arr[0][col] === arr[2][col])    // compare square 1 and 3 in the column
  {
    // vertical win--handle text for X/O win
    if (arr[0][col] === 'x') {}
    else if (arr[0][col] === 'o') {}
    break;
  }
}

if (arr[0][col] !== '' &&
    arr[0][0] === arr[1][1] &&
    arr[0][0] === arr[2][2] ||
    arr[0][2] !== '' &&
    arr[0][2] === arr[1][1] &&
    arr[0][2] === arr[2][0])
{
  // diagonal win--handle text for X/O win
  if (arr[row][0] === 'x') {}
  else if (arr[row][0] === 'o') {}
}

You should also pull your win/lose strings into constants so you only have one value to update if you ever change them.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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