JavaScript: Build a Tic Tac Toe Game (without AI)

Demo: https://codepen.io/dragoncoin/pen/VpaOdJ

JavaScript Code:

/* Author: Kim Doan
- This is a simple Tic-Tac-Toe game implemented using JavaScript, with the help of CSS on the drawing aspects.
- Each small box is named from top to bottom with a number for easier identification upon onclick() triggering.
- There shall be 2 arrays named arrayX to store the positions of X-player, and arrayO to store O-player's spots.
- Since this game is quite limited, its rules will consist of a maximum number of moves and winning combinations (yup, pritty simple stuff)
- At this time, I'm not smart enough to program AI into this board. I'll come back in the future when I've learned how to implement those awesome algorithms.
*/
$(document).ready(function(){

// Declaring variables
$square = $(".square");
$row = $(".row");
$gameBoard=$("#gameBoard");
$reset=$(".reset");
$1=$("#1");
$2=$("#2");
$3=$("#3");
$4=$("#4");
$5=$("#5");
$6=$("#6");
$7=$("#7");
$8=$("#8");
$9=$("#9");

var player1, player2;
var arrX, arrO, emptySlots;
var xo, flip, gameOver;
var turnCount=0;
var winning=[[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]];


initiateGame();
function initiateGame(){
gameOver=false;
arrX=[];
arrO=[];
emptySlots=[1,2,3,4,5,6,7,8,9];
turnCount=0;
wipeBoard();
lightDown();
$square.hide();
$("h4").hide();
$("h1").show();
$("h3").show();
$(".reset").hide();
}

$(".choice").click (function(){
var choice=$(this);
if (choice.hasClass("x")){
player1="X";
xo="X";
} else {
player1="O";
xo="O";
}
flip=1;
$("h3").hide();
$("h1").hide();
$("h2").hide();
$square.show();
$("h4").html("<br/>Player 1's Turn<br/><br/>");
$("h4").show();
});

$square.click (function(){
var selected=$(this);
var id=parseInt($(this).attr("id"));
if (gameOver) {$("h4").html("<br/>Game Over - Please Click Reset.<br/>"); }
else if(emptySlots.indexOf(id)==-1){
$("h4").html("<br/>Spot Already Taken - It's Player "+flip+"'s turn.");
checkMaxMoves();
} else {
selected.text(xo);
emptySlots.splice(emptySlots.indexOf(id),1);
if (xo =="X") {
arrX.push(id);
checkWinning(arrX);
} else {
arrO.push(id);
checkWinning(arrO);
}
nextTurn();
}

function nextTurn(){
if (!gameOver){
turnCount++;
xo=="X" ? xo="O" : xo="X";// switch between X and O
flip==1?flip=2:flip=1;
$("h4").html("<br/>Player "+xo+"'s Turn<br/><br/>");
computerMove();
}
}

function checkMaxMoves(){
if (turnCount>8 && gameOver==false) {
$("h4").html("<br/>It's a Draw!<br/>");
$(".reset").show();
gameOver=true;
}
}



}); // Ends $square.click (function(){

$reset.click (function(){
initiateGame();
});

function lightUp(combo){
gameOver=true;
if(xo=="X"){
for (i=0;i<combo.length;i++){
switch(combo[i]) {
case 1: $1.addClass("btn-danger"); break;
case 2: $2.addClass("btn-danger"); break;
case 3: $3.addClass("btn-danger"); break;
case 4: $4.addClass("btn-danger"); break;
case 5: $5.addClass("btn-danger"); break;
case 6: $6.addClass("btn-danger"); break;
case 7: $7.addClass("btn-danger"); break;
case 8: $8.addClass("btn-danger"); break;
case 9: $9.addClass("btn-danger"); break;
}
}
}
else{
for (i=0;i<combo.length;i++){
switch(combo[i]) {
case 1: $1.addClass("btn-primary"); $1.removeClass("btn-secondary"); break;
case 2: $2.addClass("btn-primary"); $2.removeClass("btn-secondary"); break;
case 3: $3.addClass("btn-primary"); $3.removeClass("btn-secondary"); break;
case 4: $4.addClass("btn-primary"); $4.removeClass("btn-secondary"); break;
case 5: $5.addClass("btn-primary"); $5.removeClass("btn-secondary"); break;
case 6: $6.addClass("btn-primary"); $6.removeClass("btn-secondary"); break;
case 7: $7.addClass("btn-primary"); $7.removeClass("btn-secondary"); break;
case 8: $8.addClass("btn-primary"); $8.removeClass("btn-secondary"); break;
case 9: $9.addClass("btn-primary"); $9.removeClass("btn-secondary"); break;
}
}
}
}

function lightDown(){
$1.removeClass("btn-danger");
$2.removeClass("btn-danger");
$3.removeClass("btn-danger");
$4.removeClass("btn-danger");
$5.removeClass("btn-danger");
$6.removeClass("btn-danger");
$7.removeClass("btn-danger");
$8.removeClass("btn-danger");
$9.removeClass("btn-danger");
$1.removeClass("btn-secondary");
$2.removeClass("btn-secondary");
$3.removeClass("btn-secondary");
$4.removeClass("btn-secondary");
$5.removeClass("btn-secondary");
$6.removeClass("btn-secondary");
$7.removeClass("btn-secondary");
$8.removeClass("btn-secondary");
$9.removeClass("btn-secondary");
$1.addClass("btn-secondary");
$2.addClass("btn-secondary");
$3.addClass("btn-secondary");
$4.addClass("btn-secondary");
$5.addClass("btn-secondary");
$6.addClass("btn-secondary");
$7.addClass("btn-secondary");
$8.addClass("btn-secondary");
$9.addClass("btn-secondary");
}

function wipeBoard(){
$1.text("");
$2.text("");
$3.text("");
$4.text("");
$5.text("");
$6.text("");
$7.text("");
$8.text("");
$9.text("");
}

function checkWinning(arr){
var match;
winning.forEach(function(winningCombo){
match=0;
arr.forEach(function(idCollected){
if (winningCombo.indexOf(idCollected)!=-1){ match++; }
});
if(match==3) {
lightUp(winningCombo);
$("h4").html("<br/>Player "+flip+" Wins!<br/>");
$(".reset").show();
}
});
}
//===================================================AI Testing===========================================================

function board (x, o, slots, playerFlip,count,xoSwitch,over,score) {
this.arrX = x;
this.arrO = o;
this.emptySlots = slots;
this.flip = playerFlip;
this.turnCount=count;
this.xo=xoSwitch;
this.gameOver=over;
this.score=score;
}

function computerMove(){
// the minimax algorithm is super hard! I'll crack it when I get to level 9000 mastery. Hahah...
// var copyBoard = new board (arrX, arrO, emptySlots, flip,turnCount,xo,gameOver,0);
// var moveIndex = minimax(copyBoard);
move(Math.floor(Math.random()*(emptySlots.length)));
}

function minimax(simBoard){

// console.log("minimax sequence");
var scoreArray1=[];
depth1_loop : for (i=0;i<simBoard.emptySlots.length;i++){
var possibleMove=simBoard.emptySlots[i];
var testArr;
simBoard.xo=='X' ? testArr=simBoard.arrX.push(possibleMove):testArr=simBoard.arrO.push(possibleMove);
// console.log(simBoard.emptySlots);
if (checkWinSim(testArr)){
scoreArray1.push(10);
console.log(scoreArray1);
} else {
// check to see if next move is a loss
var opponentBoard = (JSON.parse(JSON.stringify(simBoard))); // clone simBoard
opponentBoard.emptySlots.splice(opponentBoard.emptySlots.indexOf(possibleMove),1);
opponentBoard.xo=='X'?opponentBoard.xo='O':opponentBoard.xo='X';

depth2_loop: for (j=0;j<opponentBoard.emptySlots.length;j++){
var opponentMove=opponentBoard.emptySlots[i];
var opponentArr;
opponentBoard.xo=='X' ? opponentArr=opponentBoard.arrX.push(opponentMove) : opponentArr=opponentBoard.arrO.push(opponentMove);
if (checkWinSim(opponentArr)){
// found winning move
scoreArray1.push(-10);
console.log(scoreArray1);
break depth2_loop;
}
}
}
simBoard.xo=='X' ? simBoard.arrX.pop():testArr=simBoard.arrO.pop();
scoreArray1.push(0);
console.log(scoreArray1);
}
return 0;
}

function checkWinSim(someArr){
var match;
winning.forEach(function(winningCombo){
match=0;
someArr.forEach(function(idCollected){
if (winningCombo.indexOf(idCollected)!=-1){ match++; }
});
if(match==3) {
return true;
}
});
return false;
}

function move(moveIndex){
var id = emptySlots[moveIndex];
var selected;
switch(id) {
case 1: selected=$1; break;
case 2: selected=$2; break;
case 3: selected=$3; break;
case 4: selected=$4; break;
case 5: selected=$5; break;
case 6: selected=$6; break;
case 7: selected=$7; break;
case 8: selected=$8; break;
case 9: selected=$9;
}
// console.log(selected.text());
if (gameOver) {$("h4").html("<br/>Game Over - Please Click Reset.<br/>"); }
else {
// checkMaxMoves();
selected.text(xo);
emptySlots.splice(emptySlots.indexOf(id),1);
if (xo =="X") {
arrX.push(id);
checkWinning(arrX);
} else {
arrO.push(id);
checkWinning(arrO);
}
if (!gameOver){
turnCount++;
xo=="X" ? xo="O" : xo="X";// switch between X and O
flip==1?flip=2:flip=1;
$("h4").html("<br/>Player "+flip+"'s Turn<br/><br/>");
}
}
}


//===================================================AI Testing===========================================================

}); // Ends document.ready function

HTML Code:

<body>
<div id="gameBoard">
<h1 class="text-center">Tic Tac Toe</h1>
<h2 class="text-center">A Free Code Camp Student JavaScript Project</h2>
<h3 class="text-center"><hr>Which Symbol Would You Prefer?<br/><br/><a class='x choice btn btn-danger'>X</a>&nbspor&nbsp<a class='o choice btn btn-primary'>O</a></h3>
<div class="row">
<div class="square btn-secondary" id="1"></div>
<div class="square btn-secondary" id="2"></div>
<div class="square btn-secondary" id="3"></div>
</div>

<div class="row">
<div class="square btn-secondary" id="4"></div>
<div class="square btn-secondary" id="5"></div>
<div class="square btn-secondary" id="6"></div>
</div>

<div class="row">
<div class="square btn-secondary" id="7"></div>
<div class="square btn-secondary" id="8"></div>
<div class="square btn-secondary" id="9"></div>
</div>

</div>
<h4 class="text-center"></h4>
<div class="text-center"><a class='reset btn btn-warning'>Reset</a></div>
</a>

CSS Code:

@import url('https://fonts.googleapis.com/css?family=Merienda');

body{
font-family: 'Merienda', cursive;
font-weight: bold;
}
#gameBoard {
width: 396px; // between 320px & 480px is optimal for phones
background-color: red;
margin: 80px auto 0px auto;
}

.row{
// background-color: blue;
// clear: both;
overflow: hidden;
// width: 396px;
}

.square {
cursor: pointer;
width: 132px;
height: 132px;
border: 1px solid black;
// background-color: white;
text-align: center;
// padding: 0px;
font-weight: bold;
font-size: 86px;
}

Leave a Reply

Your email address will not be published. Required fields are marked *