Tic Tac Toe

Version 2

View Build

HTML

            
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tic Tac Toe v 2</title>
    <link rel="stylesheet" href="style.css">
    <script src="script.js" defer></script>
</head>
<body>
    <header>
        Turn: <span class="turn">X</span>
    </header>
    <canvas width = 300 height=300></canvas>
</body>
</html>
            
        

CSS

            
body{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
}
canvas{
    border: 1px solid black;
}
header{
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 40px;
    text-align: center;
    background: #ace;
    font-size: 2rem;
    font-family: sans-serif;
}
            
        

JS

            
let canvas = document.querySelector("canvas") //grab the canvas element
let ctx = canvas.getContext('2d') //drawing tools for changing the image


//A 2D array representing the state of the game
let data = [
    [0,0,0],
    [0,0,0],
    [0,0,0]
]

//keep track of whose turn it is and how many turns have been made
let player = "X"
let turnCount = 1


function nextTurn(){
    turnCount ++                                            //add one to the turn
    if(player == "X") player = "O"                          //switch players
    else player = "X"
    document.querySelector(".turn").innerHTML = player      //show whose turn it is
}


function drawData(){
    for(let row in data){
        for(let col in data[row]){
            //for each cell, draw the color then draw an "X" or "O"
            ctx.beginPath()
            ctx.rect(canvas.width/3 * col, canvas.height/3 * row, canvas.width/3, canvas.height/3)
            switch(data[row][col]){
                case "X":
                    ctx.fillStyle = "#8af"
                    ctx.fill()
                    ctx.fillStyle = "white"
                    ctx.font = "30pt Arial"
                    ctx.textAlign = "center"
                    ctx.fillText(data[row][col], canvas.width/3 * (col)+50, canvas.height/3 * (row) +65)
                    break;
                case "O":
                    ctx.fillStyle = "#fa8"
                    ctx.fill()
                    ctx.fillStyle = "white"
                    ctx.font = "30pt Arial"
                    ctx.textAlign = "center"
                    ctx.fillText(data[row][col], canvas.width/3 * (col)+50, canvas.height/3 * (row) +65)
                    break;
                default:
                    ctx.fillStyle = "#333"
                    ctx.fill()
                    break;
            }
        }
    }
}



drawData()      //draw the empty board at the start

//show possible moves on hover
canvas.onmousemove = (e) =>{
    //redraw the board
    ctx.clearRect(0,0,canvas.width,canvas.height)
    drawData()
    //find the mouse on the 3x3 grid in terms of row and column
    let mx = e.clientX - canvas.getBoundingClientRect().left
    let my = e.clientY - canvas.getBoundingClientRect().top
    let c = Math.floor(mx / canvas.width *3)
    let r = Math.floor(my / canvas.height *3)
    //if an empty square, show your symbol
    if(data[r][c] == 0){
        ctx.fillStyle = "#fff6"
        ctx.font = "30pt Arial"
        ctx.textAlign = "center"
        ctx.fillText(player, canvas.width/3 * (c)+50, canvas.height/3 * (r) +65)
    }
}


//handle player choice
canvas.onclick = (e) =>{
    //find the mouse on the 3x3 grid in terms of row and column
    let mx = e.clientX - canvas.getBoundingClientRect().left
    let my = e.clientY - canvas.getBoundingClientRect().top
    let c = Math.floor(mx / canvas.width *3)
    let r = Math.floor(my / canvas.height *3)
    //if an empty square, claim the square
    if(data[r][c] == 0){
        data[r][c] = player
        checkForWin(player)     //check if player won
        nextTurn()              //check to next players turn
    }
    //redraw the screen to show new state
    ctx.clearRect(0,0,canvas.width,canvas.height)
    drawData()
} 


function checkForWin(turn){
    let win = false
    //check rows
    if(data[0][0] == turn && data[0][1] == turn && data[0][2] == turn) win = true
    if(data[1][0] == turn && data[1][1] == turn && data[1][2] == turn) win = true
    if(data[2][0] == turn && data[2][1] == turn && data[2][2] == turn) win = true
    //check columns
    if(data[0][0] == turn && data[1][0] == turn && data[2][0] == turn) win = true
    if(data[0][1] == turn && data[1][1] == turn && data[2][1] == turn) win = true
    if(data[0][2] == turn && data[1][2] == turn && data[2][2] == turn) win = true
    //check diagonals
    if(data[0][0] == turn && data[1][1] == turn && data[2][2] == turn) win = true
    if(data[0][2] == turn && data[1][1] == turn && data[2][0] == turn) win = true
    if(win) {
        canvas.onclick = null
        canvas.onmousemove = null
        setTimeout(function(){alert("player " +  turn + " wins!")},500)
    }
    else{
        //check if turns are up without a winner
        if(turnCount > 8){
            canvas.onclick = null
            canvas.onmousemove = null
            setTimeout(function(){alert("The game has ended in a tie!")},500)
        }
    }
}