Games
Tic-Tac-Toe Javascript

Tic-Tac-Toe Javascript

We will be making a simple tic-tac-toe using Javascript.

Basic Set Up

Create index.html and app.js file.

Add following command in index.html file.

<!DOCTYPE html>
<html>
    <head>
        <title>
            Tic Tac Toe
        </title>
    </head>
    <body>
        <h1>
            Canvas
        </h1>
        <canvas id="tic-tac-toe" width="800" height="800"></canvas>
        <script src="app.js"></script>
    </body>
</html>

The Grid

Making 3×3 grid in javascript.

var canvas= document.getElementById("tic-tac-toe")
var context = canvas.getContext("2d")

let x = 75
let y = 75
let length = 150

let boxes = []
let boxCordinates = []


//making grid
for(let i=0; i<3; i++) {
    for(let j=0; j<3; j++){
        boxCordinates = [x,x+length,y,y+length]
        boxes.push(boxCordinates)
        context.rect(x,y,length,length)
        context.stroke()
        x = x+length
    }
    x = 75
    y = y+length
}

The Box coordinate function

This function takes the pixel coordinates of point where mouse has been clicked and return the Box no if any.

//this function takes the x and y position of cursor when the mouse clicked and retuns the position of box
function returnBoxPosition(cursorX,cursorY) {
    let center = [];
    let centerX,centerY,boxNo;
    for (let i=0; i<boxes.length; i++){
        //console.log(boxes[i])
        if(cursorX > boxes[i][0] && cursorX < boxes[i][1] && cursorY > boxes[i][2] && cursorY < boxes[i][3]){
            //console.log(i)
            centerX = boxes[i][0] + (length/2)
            centerY = boxes[i][2] + (length/2)
            //console.log(centerX)
            //console.log(centerY)
            boxNo = i;
            
        }

    }

    center = [centerX,centerY,boxNo]

    return center
}

Drawing Circle and Cross

This function takes the pixels (x and y) as input and draw circle around it.

//function to draw circle
function drawCircle(centerX,centerY){
    
    const lineWidth = 10
    const lineColor = "blue"

    //set the circle's properties
    const radius = 50
    const startAngle = 0
    const endAngle = Math.PI*2

    //draw a circle
    context.beginPath();
    context.arc(centerX,centerY,radius,startAngle,endAngle)
    context.lineWidth = lineWidth
    context.strokeStyle = lineColor
    context.stroke()

}

This function takes the pixels (x and y) as input and draw cross with this pixel as center.

//function to draw cross
function drawCross(x,y){
    const lineWidth = 10
    const lineColor = "red"

    //draw the horizontal line
    context.beginPath()
    context.moveTo(x-30,y-30)
    context.lineTo(x+30,y+30)
    context.moveTo(x-30,y+30)
    context.lineTo(x+30,y-30)
    context.lineWidth = lineWidth
    context.strokeStyle = lineColor
    context.stroke()
}

The following code will set the turn of either “circle” or “cross” randomly.

// randomly choose between circle or cross
const turns = ['circle','cross']
const randomIndex = Math.floor(Math.random() * turns.length);
let turn = turns[randomIndex]

The following code checks if the selected box has been used or not. If it has been used before then the following code does nothing, otherwise it will check the value of turn, and draw the circle or cross accordingly and then flip the value of turn.

This function is called when the mouse is clicked.

let turn = turns[randomIndex]

let usedBoxes = []
//the main function
function mainFunction() {
    const posX = event.clientX - canvas.offsetLeft;
    const posY = event.clientY - canvas.offsetTop;
    let center;
    

    let centerX,centerY,boxNo;
    center = returnBoxPosition(posX,posY)
    
    centerX = center[0]
    centerY = center[1]
    boxNo = center[2]


    if(usedBoxes.indexOf(boxNo) == -1){
        if(turn==='circle'){
            drawCircle(centerX,centerY);
            turn = 'cross'
    
        }
    
        else {
            drawCross(centerX,centerY);
            turn = 'circle'
    
        }

    }
    else {
        //console.log(usedBoxes.indexOf(boxNo))
    }

    usedBoxes.push(boxNo)
}
//Calling the main function on click
canvas.addEventListener("click",mainFunction)

Keeping track of position of Circle and Cross

posOfCircleCross will keep the track of position of circle and cross.

let posOfCircleCross = []

//Inside main function
//the main function
function mainFunction() {
    const posX = event.clientX - canvas.offsetLeft;
    const posY = event.clientY - canvas.offsetTop;
    let center;
    

    let centerX,centerY,boxNo;
    center = returnBoxPosition(posX,posY)
    
    centerX = center[0]
    centerY = center[1]
    boxNo = center[2]

 
    posOfCircleCross[boxNo] = turn
............................................
  }

Checking the winner

An array containing all the winning combinations.

const winningPos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]

The following code goes through each sets of winning combinations and check whether in each position of a set has “circle” or “cross”.

When either “circle” or “cross” has won the game, we do not want anything to happen after the mouse has been clicked so we remove the event listener from the canvas.

function mainFunction(){
............................
for (let i=0; i<winningPos.length; i++){
        //console.log(posOfCircleCross[winningPos[i][0]])
        if(posOfCircleCross[winningPos[i][0]]==='circle' && posOfCircleCross[winningPos[i][1]]==='circle' && posOfCircleCross[winningPos[i][2]]==='circle'){
            console.log("circle wins")
            canvas.removeEventListener("click",mainFunction);
        }

        else if (posOfCircleCross[winningPos[i][0]]==='cross' && posOfCircleCross[winningPos[i][1]]==='cross' && posOfCircleCross[winningPos[i][2]]==='cross'){
            console.log("cross wins")
            canvas.removeEventListener("click",mainFunction);
           
        }
    }

......................................

}

Drawing the Line indicating the winning combination

For this purpose, we will define two more functions. The first function will return the center pixel coordinates of first and third boxes in the winning combinations.

The second function will draw the line on those boxes.

//function to draw center pixels of box
function returnPixelCoordinates(box){
    x = 150
    y = 150 
    //console.log(box % 3 == 0)
    x = x + (box % 3)*150
    if(box >2 && box <6){
        y = y + 150
    }

    else if(box >5){
        y = y + 2*150 
    }

    return [x,y]
}
//function to draw  line after
function drawWinningLine(winningBoxes,turn){
    console.log(turn)
    //returnPixelCoordinates(winningBoxes[0])
    //console.log(returnPixelCoordinates(winningBoxes[0]))
    //console.log(returnPixelCoordinates(winningBoxes[2]))

    

    if(turn === 'circle'){
        const lineWidth = 10
        const lineColor = "orange"

        //draw the horizontal line
        context.beginPath()
        context.moveTo(returnPixelCoordinates(winningBoxes[0])[0],returnPixelCoordinates(winningBoxes[0])[1])
        context.lineTo(returnPixelCoordinates(winningBoxes[2])[0],returnPixelCoordinates(winningBoxes[2])[1])
    
        context.lineWidth = lineWidth
        context.strokeStyle = lineColor
        context.stroke()

    }

    else if(turn === "cross"){
        const lineWidth = 10
        const lineColor = "cyan"

        //draw the horizontal line
        context.beginPath()
        context.moveTo(returnPixelCoordinates(winningBoxes[0])[0],returnPixelCoordinates(winningBoxes[0])[1])
        context.lineTo(returnPixelCoordinates(winningBoxes[2])[0],returnPixelCoordinates(winningBoxes[2])[1])
    
        context.lineWidth = lineWidth
        context.strokeStyle = lineColor
        context.stroke()

    }

}

Call drawWinningLine(), inside the main function as follows.

......................................

for (let i=0; i<winningPos.length; i++){
        //console.log(posOfCircleCross[winningPos[i][0]])
        if(posOfCircleCross[winningPos[i][0]]==='circle' && posOfCircleCross[winningPos[i][1]]==='circle' && posOfCircleCross[winningPos[i][2]]==='circle'){
            console.log("circle wins")
            drawWinningLine(winningPos[i],turn)
            canvas.removeEventListener("click",mainFunction);
        }

        else if (posOfCircleCross[winningPos[i][0]]==='cross' && posOfCircleCross[winningPos[i][1]]==='cross' && posOfCircleCross[winningPos[i][2]]==='cross'){
            console.log("cross wins")
            drawWinningLine(winningPos[i],turn)
            canvas.removeEventListener("click",mainFunction);
           
        }
    }

............................

The complete code of app.js

var canvas= document.getElementById("tic-tac-toe")
var context = canvas.getContext("2d")

let x = 75
let y = 75
let length = 150

let boxes = []
let boxCordinates = []


//making grid
for(let i=0; i<3; i++) {
    for(let j=0; j<3; j++){
        boxCordinates = [x,x+length,y,y+length]
        boxes.push(boxCordinates)
        context.rect(x,y,length,length)
        context.stroke()
        x = x+length
    }
    x = 75
    y = y+length
}

//this function takes the x and y position of cursor when the mouse clicked and retuns the position of box
function returnBoxPosition(cursorX,cursorY) {
    let center = [];
    let centerX,centerY,boxNo;
    for (let i=0; i<boxes.length; i++){
        //console.log(boxes[i])
        if(cursorX > boxes[i][0] && cursorX < boxes[i][1] && cursorY > boxes[i][2] && cursorY < boxes[i][3]){
            //console.log(i)
            centerX = boxes[i][0] + (length/2)
            centerY = boxes[i][2] + (length/2)
            //console.log(centerX)
            //console.log(centerY)
            boxNo = i;
            
        }

    }

    center = [centerX,centerY,boxNo]

    return center
}


//function to draw circle
function drawCircle(centerX,centerY){
    
    const lineWidth = 10
    const lineColor = "blue"

    //set the circle's properties
    const radius = 50
    const startAngle = 0
    const endAngle = Math.PI*2

    //draw a circle
    context.beginPath();
    context.arc(centerX,centerY,radius,startAngle,endAngle)
    context.lineWidth = lineWidth
    context.strokeStyle = lineColor
    context.stroke()

}

//function to draw cross
function drawCross(x,y){
    const lineWidth = 10
    const lineColor = "red"

    //draw the horizontal line
    context.beginPath()
    context.moveTo(x-30,y-30)
    context.lineTo(x+30,y+30)
    context.moveTo(x-30,y+30)
    context.lineTo(x+30,y-30)
    context.lineWidth = lineWidth
    context.strokeStyle = lineColor
    context.stroke()
}


//function to draw center pixels of box
function returnPixelCoordinates(box){
    x = 150
    y = 150 
    //console.log(box % 3 == 0)
    x = x + (box % 3)*150
    if(box >2 && box <6){
        y = y + 150
    }

    else if(box >5){
        y = y + 2*150 
    }

    return [x,y]
}



//function to draw  line after
function drawWinningLine(winningBoxes,turn){
    console.log(turn)
    //returnPixelCoordinates(winningBoxes[0])
    //console.log(returnPixelCoordinates(winningBoxes[0]))
    //console.log(returnPixelCoordinates(winningBoxes[2]))

    

    if(turn === 'circle'){
        const lineWidth = 10
        const lineColor = "orange"

        //draw the horizontal line
        context.beginPath()
        context.moveTo(returnPixelCoordinates(winningBoxes[0])[0],returnPixelCoordinates(winningBoxes[0])[1])
        context.lineTo(returnPixelCoordinates(winningBoxes[2])[0],returnPixelCoordinates(winningBoxes[2])[1])
    
        context.lineWidth = lineWidth
        context.strokeStyle = lineColor
        context.stroke()

    }

    else if(turn === "cross"){
        const lineWidth = 10
        const lineColor = "cyan"

        //draw the horizontal line
        context.beginPath()
        context.moveTo(returnPixelCoordinates(winningBoxes[0])[0],returnPixelCoordinates(winningBoxes[0])[1])
        context.lineTo(returnPixelCoordinates(winningBoxes[2])[0],returnPixelCoordinates(winningBoxes[2])[1])
    
        context.lineWidth = lineWidth
        context.strokeStyle = lineColor
        context.stroke()

    }

}

// randomly choose between circle or cross
const turns = ['circle','cross']
const randomIndex = Math.floor(Math.random() * turns.length);

let turn = turns[randomIndex]

let usedBoxes = []
let posOfCircleCross = []
const winningPos = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]


//the main function
function mainFunction() {
    const posX = event.clientX - canvas.offsetLeft;
    const posY = event.clientY - canvas.offsetTop;
    let center;
    

    let centerX,centerY,boxNo;
    center = returnBoxPosition(posX,posY)
    
    centerX = center[0]
    centerY = center[1]
    boxNo = center[2]

    posOfCircleCross[boxNo] = turn
    console.log(posOfCircleCross)


    if(usedBoxes.indexOf(boxNo) == -1){
        if(turn==='circle'){
            drawCircle(centerX,centerY);
            turn = 'cross'
    
        }
    
        else {
            drawCross(centerX,centerY);
            turn = 'circle'
    
        }

    }
    else {
        //console.log(usedBoxes.indexOf(boxNo))
    }


    for (let i=0; i<winningPos.length; i++){
        //console.log(posOfCircleCross[winningPos[i][0]])
        if(posOfCircleCross[winningPos[i][0]]==='circle' && posOfCircleCross[winningPos[i][1]]==='circle' && posOfCircleCross[winningPos[i][2]]==='circle'){
            console.log("circle wins")
            drawWinningLine(winningPos[i],turn)
            canvas.removeEventListener("click",mainFunction);
        }

        else if (posOfCircleCross[winningPos[i][0]]==='cross' && posOfCircleCross[winningPos[i][1]]==='cross' && posOfCircleCross[winningPos[i][2]]==='cross'){
            console.log("cross wins")
            drawWinningLine(winningPos[i],turn)
            canvas.removeEventListener("click",mainFunction);
           
        }
    }

    usedBoxes.push(boxNo)
}


//Calling the main function on click
canvas.addEventListener("click",mainFunction)


Tags :