DEV Community

Cover image for Unbeatable Emoji Tic Tac Toe
Abhi Develops - SunTech
Abhi Develops - SunTech

Posted on

Unbeatable Emoji Tic Tac Toe

Hi Everyone!
In this post, I will show you how to make an unbeatable emoji tic-tac-toe that makes fun of you if you make a move or lose. It is made with HTML, CSS, and Javascript. You cannot win this game. The most you can get is a draw. Follow me if you want more projects.

Let's start with the HTML Design:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="style.css>
    <title>Unbeatable Tic Tac Toe | Abhinav Gupta</title>
  </head>

<body>
<section id="start-select">
 <div>
  <div>
   <h3>Go First or Second?</h3>
   <button id="1st" class="active" onclick="pickTurn(true);" style='font-size: 14px;'>1st</button><button id="2nd" onclick="pickTurn(false);" style='font-size: 14px;'>2nd</button>
   <h3>Choose Your Character</h3>
   <span id="charSymbols"></span>

  <button id ="start-btn" onclick="startGame();" style='font-size: 18px;'>Start Game</button>
  </div>
 </div>
</section>

<header id="header" style="opacity:0.6;">  
  <div id="emoji-outer-div">
    <div id="emoji">
     <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/win02.png" id="emoji-img">
    </div>
    <div id="emoji-text">
     <span id="aiTalk">"Hi, I'm Unbeatable-AI<br>Wanna Play Against Me?"</span>
    </div>
  </div>
</header>

  <nav id="menu-nav">
 <div>
  <div>
   <h3>Next Round Go First or Second?</h3>
   <button id="1st-next" class="active" onclick="pickTurn(true);">1st</button><button id="2nd-next" onclick="pickTurn(false);">2nd</button>
   <h3>Change Your Character</h3>
   <span id="menu-chars"></span>

  <button id ="menu-close" onclick="openMenu(false);" style="">Close</button>
  </div>
 </div>
</nav>

<section id='main-section' style="opacity:0.6;">
  <section id="side-section">

  <a id="menu-open" href="javascript:void(0);" onclick="openMenu(true);"><div>&#x2630</div></a>

  <div id="score">
   <div><span>Score</span></div>
    <div><table>
     <tr>
       <th>You</th>
      <th style="width: 12px;"></th>
       <th>Ai</th>
     </tr>
     <tr>
       <td>0</td>
      <td> </td>
       <td id="score-ai">0</td>
     </tr> 
     </table></div>
   <div style="height: 10px; width:0;"></div>
  <div><table>
    <tr>
      <th>Tie</th>
    </tr>
    <tr>
      <td id="score-tie">0</td>
    </tr>
  </table></div>

</div>

 </section>

 <div id="outer-grid">

<section id="grid"> 
 <div id="pos0"><a href="javascript:void(0);" onclick='playerMove(0);' class="pos"></a></div>
 <div id="pos1"><a href="javascript:void(0);" onclick='playerMove(1);' class="pos"></a></div>
 <div id="pos2"><a href="javascript:void(0);" onclick='playerMove(2);' class="pos"></a></div>
 <div id="pos3"><a href="javascript:void(0);" onclick='playerMove(3);' class="pos"></a></div>
 <div id="pos4"><a href="javascript:void(0);" onclick='playerMove(4);' class="pos"></a></div>
 <div id="pos5"><a href="javascript:void(0);" onclick='playerMove(5);' class="pos"></a></div>
 <div id="pos6"><a href="javascript:void(0);" onclick='playerMove(6);' class="pos"></a></div>
 <div id="pos7"><a href="javascript:void(0);" onclick='playerMove(7);' class="pos"></a></div>
 <div id="pos8"><a href="javascript:void(0);" onclick='playerMove(8);' class="pos"></a></div>
</section>
 </div>
</section>
<script href="script.js"></script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Next, let's make the CSS for the nice neat look:

* {
 box-sizing: border-box;
 font-family: 'Cabin', sans-serif;
}

a {
 text-decoration: none;
 color: inherit;
}

button:focus {outline:0;}

body {
 margin: 20px auto;
 padding: 5px;
 max-width: 510px;
 background-color: #4ECCB0;
}

#start-select {
 position: absolute;
 width: 100vw;
 height: 100vh;
 z-index: 100;
 left: 50%;
 top: 40%;
 transform: translate(-50%,-50%);
}

#start-select div {
 position: fixed;
 display: flex;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%);
 width: 350px;
 height: 300px;
 border-radius: 35px;
 background-color: #5A8BCE;
 box-shadow: 3px 3px 6px #0B3A97;
}

#start-select div div {
 display: flex;
 justify-content: center;
 flex-wrap: wrap;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%);
}

#start-select div div h3 {
 margin: auto;
 text-align: center;
 width: 100%;
}

#start-select div div span {
 display: flex;
 flex-wrap: wrap;
 justify-content: center;
}

#start-select div div button {
 background-color: #4ECCB0;
 border-radius: 8px;
 margin: 3px;
 padding: 15px;
 height: 50px;
 min-width: 55px;
 border: none;
 -webkit-transition: background-color 500ms; /* Safari */
 transition: background-color 500ms;
}

#start-select div div button:hover {
 background-color: #77E0C9;
 box-shadow: 0.5px 0.5px 5px black;
 cursor: pointer; cursor: hand;
 -webkit-transition: background-color 300ms; /* Safari */
 transition: background-color 300ms;
}

#start-select div div button.active {
 border: 2px solid;
 background-color: #2EB798;
 padding: 13px;
 -webkit-transition: background-color 500ms; /* Safari */
 transition: background-color 500ms;
}

#start-select div div button.charBtn {
 min-width: 40px;
 height: 40px;
 width: 40px;
 text-align: center;
 padding: 6px;
}

header {
 display: flex;
 height: 110px;
 margin: 0 15px;
 opacity: 1;
 -webkit-transition: opacity 750ms; /* Safari */
 transition: opacity 750ms;
}

#emoji-outer-div {
 flex: 1;
 display: flex;
 align-items: center;
}

#emoji {
 width: 100px;
 height: 100px;
}

#emoji img {
 height: 100%;
}

#emoji-text {
 flex: 1;
 text-align: center;
 margin: 0 30px;
 font-size: 20px;
}

#aiTalk {
 font-family: 'Poiret One', Segoe, sans-serif;
 font-weight: bold;
}

nav {
 display: none;
 position: absolute;
 width: 100vw;
 height: 100vh;
 z-index: 99;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%);
}

nav div {
 position: fixed;
 display: flex;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%);
 width: 310px;
 height: 350px;
 border-radius: 35px;
 background-color: #5A8BCE;
 box-shadow: 3px 3px 6px #0B3A97;
}

nav div div {
 display: flex;
 justify-content: center;
 flex-wrap: wrap;
 left: 50%;
 top: 50%;
 transform: translate(-50%,-50%);
}

nav div div h3 {
 margin: auto;
 text-align: center;
 width: 100%;
}

nav div div span {
 width: 270px;
 display: flex;
 flex-wrap: wrap;
 justify-content: center;
}

nav div div button {
 background-color: #4ECCB0;
 border-radius: 8px;
 margin: 3px;
 padding: 15px;
 height: 50px;
 min-width: 55px;
 border: none;
 -webkit-transition: background-color 750ms; /* Safari */
 transition: background-color 750ms;
}

nav div div button:hover {
 background-color: #77E0C9;
 box-shadow: 0.5px 0.5px 5px black;
 cursor: pointer; cursor: hand;
 -webkit-transition: background-color 300ms; /* Safari */
 transition: background-color 300ms;
}

nav div div button.active {
 border: 2px solid;
 background-color: #2EB798;
 padding: 13px;
 -webkit-transition: background-color 500ms; /* Safari */
 transition: background-color 500ms;
}

nav div div button.charBtn {
 min-width: 40px;
 height: 40px;
 width: 40px;
 text-align: center;
 padding: 6px;
}

#main-section {
 display: flex;
 flex-wrap: wrap;
 margin: auto;
 opacity: 1;
 -webkit-transition: opacity 500ms; /* Safari */
 transition: opacity 500ms;
}

#side-section {
 margin: 15px;
}

#menu-open div {
 margin: auto;
 text-align: center;
 width: 60px;
 border-radius: 10px;
 font-size: 35px;
 border: solid #009271;
 margin: 25px;
 padding: 2.5px;
 background-color: #1BA485;
}

#menu-open div:hover {
 background-color: #28C09E;
 border: solid #009271;
}

#score {
 margin: auto;
}

#score table, #score div {
 font-size: 20px;
 text-align: center;
 margin: auto;
 display: flex;
}

tr, th, td {
 padding: 1px;
}

#score div span {
 font-size: 25px;
 margin: 10px auto;
}

@media screen and (max-width: 530px) {
 body {
  margin: 10px auto;
  padding: 5px;
 }
 header {
  margin: auto;
  max-width: 400px;
 }
 #emoji-text {
  margin: 0 10px;
  font-size: 20px;
 }
 #emoji {
  height: 65px;
  width: 65px;
 }
 #start-select div {
  width: 300px;
  height: 380px;
  padding: 13px;
 }
 #score-2 {
  position: relative;
  right: 15px;
 }
 #menu-open div {
  margin: 0 5px;
 }
 #score {
  margin: auto;
  display: flex;
  flex: 1;
 }
 #side-section {
  margin: auto;
  width: 360px;
  display: flex;
 }
}

#outer-grid {
 height: 300px;
 width: 300px;
 margin: 8px auto;
}

@media screen and (min-width: 380px) {
 #outer-grid {
  width: 360px;
  height: 360px;
  margin: 15px auto;
 }
}

#grid {
 height: 100%;
 width: 100%;
 display: flex;
 flex-wrap: wrap;
}

#grid div {
 width: 33.3%;
 height: 33.3%;
 padding: 2.5px;
 display: flex;
}

#grid div div, #grid div a {
 background-color: #134DBF;
 text-shadow: 1px 1px grey;
 font-size: 45px;
 width: 100%;
 height: 100%;
 text-align: center;
 border-radius: 15px;
 display: flex;
 box-shadow: 2px 2px 4px #008B81;
}

#grid div div.taken {
 background-color: #0B3A96;
 box-shadow: 4px 4px 5px #008B81;
}

#grid div div.win {
 background-color: #E26200;
 box-shadow: 4px 4px 5px #008B81;
}

#grid div div span {
 margin: auto;
}

.pos-span {
 opacity: 0;
 display: flex;
 width: 100%;
 height: 100%;
 -webkit-transition: opacity 250ms; /* Safari */
 transition: opacity 250ms;
}

.pos-span span {
 margin: auto;
}

.pos-span:hover {
 opacity: 0.4;
 -webkit-transition: opacity 550ms; /* Safari */
 transition: opacity 550ms;
}

#grid div div:hover {
 background-color: #0B3A96;
 box-shadow: 4px 4px 5px #008B81;
 -webkit-transition: background-color 300ms; /* Safari */
 transition: background-color 300ms;
}
Enter fullscreen mode Exit fullscreen mode

Lastly, let's write the Javascript to actually make it "Unbeatable":

 var aiTalksWin = [[["win4"],"Sorry, humans made me to powerful!"], [["win01"],"Honestly, I thought you could win, but I guess I was wrong."], [["win02"],"<del>Win Tic-Tac-Toe?</del> <br>2. First, win against ME!"], [["win3"],"What Did You Expect? You Are Only a Human..."], [["win01"],"Unbeatable Is In My Name, I can't do it is in yours."], [["win3"],"The score counter is pointless."], [["win4"],"Let You Win? I'm Afraid I Can't Do That."], [["win02"],""]];

var aiTalksMove = [[["move00"],"..."], [["move00"],"Hmmm..."], [["move05"],"Go AI !!"], [["move08"],"Sadness is Victory"],[["move08"], "Your Defeat Is a WIN for me..."], [["move03"],"Nice Try (not)"], [["move03"],"Knock Knock. Who's there? R O B O T"], [["move4"],"There are 255,168 Possible Board Combinations, Yet You Picked That One?"], [["win4"],"Infinity x Infinity Wins for me, not you!"], [["draw02"],"When Was The Last Time You Rebooted Your Device?"], [["draw04"],"I feel strange..."], [["move01"],"A Wise Computer Once Told Me That The Meaning Of Life Is 42"], [["draw01"],"Whoops, wrong move."], [["win02"],"The end is upon! "], [["move06"], "Can't Touch This!"], [["move07"], "Your Last Move Goes In The loosing Category"]];

var aiTalksTie = [[["draw01"],"..."], [["draw02"],"..."], [["draw03"],"..."], [["draw04"],"..."]];

// </> Ai Talking
function randomEmoji(chance, arr) {
 var randTest = Math.random() < chance;
 if (randTest) {
  var rand = Math.floor(Math.random()*arr.length);
  console.log(rand);
  document.getElementById("emoji-img")
   .src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/"+arr[rand][0][0]+".png";
  document.getElementById("aiTalk")
   .innerHTML = '"'+arr[rand][1]+'"';
 }
}

var winCond = [[0,1,2],[3,4,5],[6,7,8],
               [0,3,6],[1,4,7],[2,5,8],
               [0,4,8],[2,4,6]];

var gameMain = ["0", "0", "0",
                "0", "0", "0",
                "0", "0", "0"]; 

var chars = ["01","02","03","04","05","06","07","08","09","10","11","12","13"];
function charsBtnGen() {
 for (var i = 0; i < chars.length; i++) {

  document.getElementById("charSymbols").innerHTML += '<button id="char'+i+'" class="charBtn" onclick="chrChoose('+i+');"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon'+chars[i]+'.png" style="width: 25px"></button>';

  document.getElementById("menu-chars").innerHTML += '<button id="char-chng'+i+'" class="charBtn" onclick="chrChange('+i+');"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon'+chars[i]+'.png" style="width: 25px"></button>';

 }
} 

function openMenu(open) {
 if (open) {
  document.getElementById('menu-nav').style.display = 'flex';
  document.getElementById('header').style.opacity = '0.6';
  document.getElementById('main-section').style.opacity = '0.6';
 } else {
  document.getElementById('menu-nav').style.display = 'none';
  document.getElementById('header').style.opacity = '';
  document.getElementById('main-section').style.opacity = '';
 }
}

var aiChar = 'O';
var plChar = 'X';
var aiScore = 0;
var tieScore = 0;

var gameStarted = false;
// --- \/ \/ \/ Before Game Start \/ \/ \/ ---

// </> Player 1st or 2nd 
plFirst = true;
function pickTurn(first) {
 if (first) {
  document.getElementById("1st").className = "active";
  document.getElementById("2nd").className = "";
  document.getElementById("1st-next").className = "active";
  document.getElementById("2nd-next").className = "";
 }
 if (!first) {
  document.getElementById("2nd").className = "active";
  document.getElementById("1st").className = "";
  document.getElementById("2nd-next").className = "active";
  document.getElementById("1st-next").className = "";
 }
 plFirst = first;
}

// </> Character Chooser
function chrChoose(x) {
 for (var i = 0; i < chars.length; i++) {
  document.getElementById("char"+i).className = "charBtn";
 }
 document.getElementById("char"+x).className += " active";
 plChar = chars[x];
}

// </> Character Change
function chrChange(x) {
 for (var i = 0; i < chars.length; i++) {
  document.getElementById("char-chng"+i).className = "charBtn";
 }
 document.getElementById("char-chng"+x).className += " active";

  if (aiChar === chars[x]) {
   var y = -1;
   while (y === x || y === -1) {y = Math.floor(Math.random()*chars.length);}
   for (var j = 0; j < 9; j++) {
    if (gameMain[j] === aiChar) {
     gameMain[j] = chars[y];
     document.getElementById("div"+j)
     .innerHTML = "<span style='display: flex;'><img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon"+chars[y]+".png' style='width: 50px; margin: auto;'></span>";
    }
   }
    aiChar = chars[y];
 }
 // "<span style='display: flex;'><img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon"+chars[x]+".png' style='width: 50px; margin: auto;'></span>"
 for (var i = 0; i < 9; i++) {
  if (gameMain[i] === plChar) {
   gameMain[i] = chars[x];
   document.getElementById("div"+i)
    .innerHTML = "<span style='display: flex;'><img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon"+chars[x]+".png' style='width: 50px; margin: auto;'></span>";
  } else if (gameMain[i] === "0") {
   document.getElementById("transpChars"+i)
    .innerHTML = "<span style='display: flex;'><img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon"+chars[x]+".png' style='width: 50px; margin: auto;'></span>";
  }
 }
 plChar = chars[x];
}

// </> Random Ai Char
function randChar() {
  var rand =  Math.floor(Math.random()*chars.length);
  aiChar = chars[rand];
  if (aiChar === plChar) {return randChar();}
  return;
 }

// </> Start Game
var round = 0;
function startGame() {
 gameStarted = true;
 plMoveDisable = false;
 document.getElementById('start-select').style.display = 'none';
 document.getElementById('header').style.opacity = '';
 document.getElementById('main-section').style.opacity = '';
 if (round === 0) {
  document.getElementById("aiTalk").innerHTML = '"Have Fun"';
  document.getElementById("emoji-img").src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/win3.png";
 }
 round++;
 !function () {
  var randPl =  Math.floor(Math.random()*chars.length);
  if (plChar === "X") {plChar = chars[randPl];}
 }();
 randChar();
 var pos = document.getElementsByClassName("pos");
 for (var i = 0; i < 9; i++) {
  pos[i].innerHTML = '<div><span class="pos-span"><span id="transpChars'+i+'"><span style="display: flex;"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon'+plChar+'.png" style="width: 50px; margin: auto;"></span></span></span></div>';
 }
 if (!plFirst) {
  aiTurn();
 }
}
// --- /\ /\ /\  Before Game Start /\ /\ /\ ---


// --- \/ \/ \/  After Game Start \/ \/ \/ ---
// </> Checks for Victory
function checkVictory(who) {
   var inx = [], i;
   for (i = 0; i < 9; i++) {
    if (gameMain[i] === who) {
     inx.push(i);     }   
   }
   for (var j = 0; j < 8; j++) {
    var win = winCond[j];
    if (inx.indexOf(win[0]) !== -1 && 
        inx.indexOf(win[1]) !== -1 && 
        inx.indexOf(win[2]) !== -1) {
     randomEmoji(1, aiTalksWin);
     for (let k = 0; k < 3; k++) {
      setTimeout(function() {
       document.getElementById("div"+win[k]).className = "win";
      },350*(k+1));
     }

      gameStarted = false;
      aiScore++;
      document.getElementById("score-ai").innerHTML = aiScore;
      setTimeout(function() {restart("tie");},2000);
      return true;    
     }   
   }  
   if (gameMain.indexOf("0") === -1) {
    gameStarted = false;
    randomEmoji(1, aiTalksTie);
    setTimeout(function() {
     for (let k = 0; k < 9; k++) {
       setTimeout(function() {
        document.getElementById("div"+[k]).innerHTML = "";
       },125*(k+1));
      }
    },500);

    setTimeout(function() {restart("tie");},2100);
    tieScore++;
    document.getElementById("score-tie").innerHTML = tieScore;
    return true;
   } else if (who === aiChar && gameMain.indexOf(plChar) !== -1) {randomEmoji(0.3, aiTalksMove);}       
 return false;  
}

// </> Restart Game
function restart(x) {
 for (var i = 0; i < 9; i++) {
  document.getElementById("pos"+i).innerHTML = '<a href="javascript:void('+i+');" onclick="playerMove('+i+');" class="pos"></a>';
 }
 gameMain = ["0", "0", "0",
             "0", "0", "0",
             "0", "0", "0"];
  startGame();
  disableRestart = false;
}

// </> Write a Move
function writeOnGame(pos, char) {
 gameMain[pos] = char;
 document.getElementById("pos"+pos)
  .innerHTML = "<div  class='taken' id='div"+pos+"'><span style='display: flex;'><img src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/1134440/icon"+char+".png' style='width: 50px; margin: auto;'></span></div>";
}

// </> Ai Triger and Equal Value Ai Move Randomizer
function aiTurn() {
 var posArr = ai();
 var ran = Math.floor(Math.random() * posArr.length);
 writeOnGame(posArr[ran], aiChar);
 checkVictory(aiChar);
}

// </> Player Click
var plMoveDisable = false
function playerMove(pos) {
 if (gameStarted && !plMoveDisable) {
  plMoveDisable = true;
  writeOnGame(pos, plChar);
  var win = checkVictory(plChar);
  if (win) {return;}
  setTimeout(function() {
   aiTurn();
   plMoveDisable = false;
  },450);
 }
}
// --- /\ /\ /\  After Game Start /\ /\ /\ ---

// --- \/ \/ \/ AI \/ \/  \/ ---
// </> MinMax algo
function ai() {
 if (gameStarted) {

  function isOpen(gameState,pos) {
   return gameState[pos] === "0";
  }

  function didWin(gameState, val) {
   var inx = [], i;
   for (i = 0; i < 9; i++) {
    if (gameState[i] === val) {
     inx.push(i);     }    }
   for (var i = 0; i < 8; i++) {
     if (inx.indexOf(winCond[i][0]) !== -1 && 
         inx.indexOf(winCond[i][1]) !== -1 && 
         inx.indexOf(winCond[i][2]) !== -1) {
      return true;    }   }    return false;  }

  function findScore(scores, x) {
   if (scores.indexOf(x) !== -1) {return x;}
   else if (scores.indexOf(0) !== -1) {return 0;}
   else if (scores.indexOf(x * -1) !== -1) {return x * -1;}
   else {return 0;}
  }

  var scoresMain = ['0','0','0','0','0','0','0','0','0'];
  function findBestMove() { // MAIN FUNCTION
   for (var i = 0; i < 9; i++) {
    if (isOpen(gameMain, i)) {
     var simGame = gameMain.slice();
     simGame[i] = aiChar;
     if (didWin(simGame, aiChar)) {
      scoresMain[i] = 1;
     } else {
      scoresMain[i] = plSim(simGame);
     }
    }    
   }
   var bigest = -99;
   for (var j = 0; j < 9; j++) {
    if (scoresMain[j] !== '0' && scoresMain[j] > bigest) {
     bigest = scoresMain[j];
    }
   }
   var inx = [], i;
   for (i = 0; i < 9; i++) {
    if (scoresMain[i] === bigest) {
     inx.push(i);     }    }
   console.log(gameMain.slice(0,3), scoresMain.slice(0,3));
   console.log(gameMain.slice(3,6), scoresMain.slice(3,6));
   console.log(gameMain.slice(6,9), scoresMain.slice(6,9));
   return inx;
  }

  function plSim(simGame) { // PL SIM
   var simGameTest = simGame.slice();
   for (var i = 0; i < 9; i++) {
    if (isOpen(simGame, i)) {
     simGameTest = simGame.slice();
     simGameTest[i] = plChar;
     if (didWin(simGameTest, plChar)) {
      return -1;
     }
    }
   }
   var plScores = ['0','0','0','0','0','0','0','0','0'];
   for (var j = 0; j < 9; j++) {
    if (isOpen(simGame, j)) {
     simGameTest = simGame.slice();
     simGameTest[j] = plChar;
     plScores[j] = aiSim(simGameTest);
    }
   }
   return findScore(plScores, -1);
  }

  function aiSim(simGame) { // AI SIM
   var simGameTest = simGame.slice();
   for (var i = 0; i < 9; i++) {
    if (isOpen(simGame, i)) {
     simGameTest = simGame.slice();
     simGameTest[i] = aiChar;
     if (didWin(simGameTest, aiChar)) {
      return 1;
     }
    }
   }
   var aiScores = ['0','0','0','0','0','0','0','0','0'];
   for (var j = 0; j < 9; j++) {
    if (isOpen(simGame, j)) {
     simGameTest = simGame.slice();
     simGameTest[j] = aiChar;
     aiScores[j] = plSim(simGameTest);
    }
   }
   return findScore(aiScores, 1);
  } // aiSim()
 return findBestMove();
 }
} // ai() end

charsBtnGen();

Enter fullscreen mode Exit fullscreen mode

That's It! You have now successfully created an Unbeatable Emoji Tic-Tac-Toe.

Live Demo

Full Code

Discussion (3)

Collapse
lukeshiru profile image
Luke Shiru

A few things that I noticed:

  • Instead of using className you could use classList to add/remove/toggle classes.
  • Instead of using style you could use classList and toggle classes.
  • You should move all styles from HTML to CSS.
  • You should move all event listeners from HTML to JS (instead of onclick you can use element.addEventListener).
  • You could use template literals instead of concatenating strings with the + symbol.
  • Instead of using document.getElementById every time you need an element, you could catch them in constants and use the constants directly. You could also use document.querySelector instead.
  • Use the button element instead of a for stuff like the menu-open.
  • You could make use of dataset attributes of elements instead of using ids or calling the same function with different values:
<!-- Instead of this: -->
<button onclick="playerMove(8);" class="pos"></button>

<!-- You could do this: -->
<button class="pos" data-pos="8"></button>
Enter fullscreen mode Exit fullscreen mode

And then from JS:

// Instead of this
const playerMove = pos => {/* Do something with pos */}

// You could do this:
const playerMove = button => {
  const pos = button.dataset.pos;
  /* Do something with pos */
}
Enter fullscreen mode Exit fullscreen mode

I know it looks longer in JS, but is more versatile that way because instead of having 9 times this in your html: onclick="playerMove(0);" you have it once as an event listener in JS listening to clicks in any .pos element.

Cheers!

Collapse
lukeshiru profile image
Luke Shiru • Edited on

@abhidevelops bro, why did you hide the comment? Is not offensive or anything, is just a bunch of tips to improve the code a little ... why post in DEV if you don't want to get feedback of your code? :/

Collapse
abhidevelopssuntech profile image
Abhi Develops - SunTech Author

Sorry, I must have hit "Hide".