<!--chosing sign box-->
<div id="dark">
  <div id="box">
    <p id="playX" data-sign="x">Play X</p>
    <p id="message" class="hidden"></p>
    <p id="playO" data-sign="o">Play O</p>
  </div>
</div>

<!-- title -->
<header>
  <h1>Tic Tac Toe</h1>
</header>

<!--OXO-->
<section class="main">
  <div class="playable play" id="0"></div>
  <div class="playable play" id="1"></div>
  <div class="playable play" id="2"></div>
  <div class="playable play" id="3"></div>
  <div class="playable play" id="4"></div>
  <div class="playable play" id="5"></div>
  <div class="playable play" id="6"></div>
  <div class="playable play" id="7"></div>
  <div class="playable play" id="8"></div>
</section>

<!-- footer -->
<footer>
  <p>Created by <a href="https://remybeumier.be" target="_blank">Rémy Beumier</a></p>
</footer>

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
  text-align: center;
  background: #ffb300;
  background-image: linear-gradient(to top left, #d35400, #ffb300, #f1c40f);
}

/* Modal window */
#dark {
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, 0.5);
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  z-index: 100;
}

#dark #box {
  display: flex;
  flex-flow: column;
  justify-content: space-around;
  align-items: center;
  width: 220px;
/*   height: 200px; */
  padding: 20px;
  background: whitesmoke;
  border-radius: 2px;
}

#box #playX, #box #playO, #box #message {
  min-width: 120px;
  margin: 10px;
  padding: 10px 30px;
  border: solid #EAA80B 1px;
  border-radius: 2px;
  color: #EAA80B;
  font-size: 20px;
  cursor: pointer;
  white-space: nowrap;
}
#box #playX:hover, #box #playO:hover {
  background: #EAA80B;
  color: whitesmoke;
}

/* title */
header h1 {
  letter-spacing: 0.2em;
  color: whitesmoke;
  font-size: 34px;
  padding: 20px 10px;
  font-weight: normal;
}
@media only screen and (min-width: 768px) {
  header h1 {
    font-size: 42px;
  }
}

/* main square */
.main {
  width: 262px;
  height: 262px;
  padding: 5px;
  margin: auto;
  background: whitesmoke;
  border-radius: 2px;
}
@media only screen and (min-width: 768px) {
  .main {
    width: 332px;
    height: 332px;
    padding: 10px;
  }
}

/* inside orange squares */
.main div {
  width: 80px;
  height: 80px;
  margin: 2px;
  background: #ffb300;
/*   background-image: linear-gradient(to top left, #d35400, #f1c40f); */
  background-size: 200%;
  float: left;
  border-radius: 2px;
  color: whitesmoke;
  font-size: 60px;
  line-height: 80px;
  font-family: monospace;
  text-transform: uppercase;
}
@media only screen and (min-width: 768px) {
  .main div {
    width: 100px;
    height: 100px;
    margin: 2px;
    line-height: 100px;
  }
}
.main .playable:hover {
  opacity: 0.6;
  cursor: pointer;
}

.main div::after {
/*   content: attr(id); */
}

.hidden {
  display: none !important;
}

footer {
  clear: both;
  padding: 5px;
  text-align: center;
  color: whitesmoke;
  font-size: 14px;
}
footer a {
  color: #333;
  text-decoration: none;
}
footer a:hover {
  color: whitesmoke;
  text-decoration: underline;
}

// improve AI to be offensive too

var playerSign = "";
var compSign = "";
var grid = ["e","e","e", "e","e","e", "e","e","e"];
var turn = 0;
var isFinished = false;

var dark = document.querySelector("#dark");
var message = document.querySelector("#message");
var playX = document.querySelector("#playX");
var playO = document.querySelector("#playO");
var plays = document.querySelectorAll(".play");

playX.addEventListener("click", startGame);
playO.addEventListener("click", startGame);

// choose sign
function startGame(e) {
  // clearInterval(checkPageInterval);
  dark.classList.add("hidden");
  playerSign = e.target.getAttribute("data-sign");
  compSign = playerSign === "x" ? "o" : "x";
  if (playerSign === "o") {
    compMove();
  }
  for (var i=0; i<plays.length; i++) {
    plays[i].addEventListener("click", playerMove);
  }
}

// player move
function playerMove(e) {
  e.target.innerHTML = playerSign;
  e.target.classList.remove("playable");
  grid[e.target.id] = playerSign;
  turn++;
  checker("player");
  if (!isFinished) {
    setTimeout(function() {
      compMove();
    }, 500);
  }
  e.target.removeEventListener("click", playerMove);
}

// AI move
function compMove() {
  var logicMove = aiLogic();
  plays[logicMove].innerHTML = compSign;
  plays[logicMove].classList.remove("playable");
  plays[logicMove].removeEventListener("click", playerMove);
  grid[logicMove] = compSign;
  turn++;
  checker("comp");
}

// end game checker (win, lose, tie)
function checker(whoo) {
  console.log(grid, turn);
  // win
  if ( (whoo === "player") && (gridCheck(playerSign)) ) {
    isFinished = true;
    setTimeout(function() {
      reset("You win!");
    }, 500);
  }
  // lose
  else if ( (whoo === "comp") && (gridCheck(compSign)) ) {
    isFinished = true;
    setTimeout(function() {
      reset("You lose!");
    }, 500);
  }
  // tie
  // else if (!grid.includes("e")) {
  else if (turn === 9) {
    isFinished = true;
    setTimeout(function() {
      reset("You tie!");
    }, 500);
  }
}

// check grid
function gridCheck(sign) {
  return (
    (grid[0] === sign && grid[1] === sign && grid[2] === sign) ||
    (grid[3] === sign && grid[4] === sign && grid[5] === sign) ||
    (grid[6] === sign && grid[7] === sign && grid[8] === sign) ||
    (grid[0] === sign && grid[3] === sign && grid[6] === sign) ||
    (grid[1] === sign && grid[4] === sign && grid[7] === sign) ||
    (grid[2] === sign && grid[5] === sign && grid[8] === sign) ||
    (grid[0] === sign && grid[4] === sign && grid[8] === sign) ||
    (grid[2] === sign && grid[4] === sign && grid[6] === sign)
  );
}

// ai logic
function aiLogic() {
  // add offensive moves
  
  // defensive moves
  if (
    grid[0] === "e" && (
      (grid[1] === playerSign && grid[2] === playerSign) ||
      (grid[3] === playerSign && grid[6] === playerSign) ||
      (grid[4] === playerSign && grid[8] === playerSign)
  )) { return 0 }
  
  else if (
    grid[1] === "e" && (
      (grid[0] === playerSign && grid[2] === playerSign) ||
      (grid[4] === playerSign && grid[7] === playerSign)
  )) { return 1 }
  
  else if (
    grid[2] === "e" && (
      (grid[0] === playerSign && grid[1] === playerSign) ||
      (grid[4] === playerSign && grid[6] === playerSign) ||
      (grid[5] === playerSign && grid[8] === playerSign)
  )) { return 2 }
  
  else if (
    grid[3] === "e" && (
    (grid[0] === playerSign && grid[6] === playerSign) ||
      (grid[4] === playerSign && grid[5] === playerSign)
  )) { return 3 }
  
  else if (
    grid[4] === "e" && (
    (grid[0] === playerSign && grid[8] === playerSign) ||
      (grid[1] === playerSign && grid[7] === playerSign) ||
      (grid[2] === playerSign && grid[6] === playerSign) ||
      (grid[3] === playerSign && grid[5] === playerSign)
  )) { return 4 }
  
  else if (
    grid[5] === "e" && (
    (grid[2] === playerSign && grid[8] === playerSign) ||
      (grid[3] === playerSign && grid[4] === playerSign)
  )) { return 5 }
  
  else if (
    grid[6] === "e" && (
    (grid[0] === playerSign && grid[3] === playerSign) ||
      (grid[2] === playerSign && grid[4] === playerSign) ||
      (grid[7] === playerSign && grid[8] === playerSign)
  )) { return 6 }
  
  else if (
    grid[7] === "e" && (
    (grid[1] === playerSign && grid[4] === playerSign) ||
      (grid[6] === playerSign && grid[8] === playerSign)
  )) { return 7 }
  
  else if (
    grid[8] === "e" && (
    (grid[0] === playerSign && grid[4] === playerSign) ||
      (grid[2] === playerSign && grid[5] === playerSign) ||
      (grid[6] === playerSign && grid[7] === playerSign)
  )) { return 8 }
  
  // random move
  else { return rand(grid.length) }
  
}

// reset func
function reset(msg) {
  playX.classList.add("hidden");
  playO.classList.add("hidden");
  message.textContent = msg;
  message.classList.remove("hidden");
  dark.classList.remove("hidden");
  grid = ["e","e","e", "e","e","e", "e","e","e"];
  turn = 0;
  isFinished = false;
  for (var j=0; j<plays.length; j++) {
    plays[j].innerHTML = "";
    plays[j].classList.add("playable");
    plays[j].removeEventListener("click", playerMove);
  }
  setTimeout(function() {
    playX.classList.remove("hidden");
    playO.classList.remove("hidden");
    message.classList.add("hidden");
  }, 1500);
}

// random func
function rand(num) {
  var randd = Math.floor(Math.random() * num);
  while (grid[randd] !== "e") {
    randd = Math.floor(Math.random() * num);
  }
  return randd; 
}

// handle focus of the page
// function checkPageFocus() {
//   if (document.hasFocus()) {
//     dark.classList.remove("hidden");
//   }
//   else {
//     dark.classList.add("hidden");
//   }
// }
// var checkPageInterval = setInterval(checkPageFocus, 300);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.