In: Computer Science
For this assignment, you will write a tic-tac-toe application in HTML and JavaScript, using an HTML <canvas> tag. The game will be played "hot seat" where players take turns using the same device.
Requirements:
I am pasting the codes with its filenames.
Make sure you make the same filenames :
Put all the files in same folder and the 3 png images in a img folder
file : index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Tic Tac Toe</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<canvas id="cvs" width="450" height="450"></canvas>
<div class="options">
<h1>Tic Tac Toe</h1>
<input type="text" id="player-name" placeholder="Enter Name">
<h2>Play Versus</h2>
<div class="computer">COMPUTER</div>
<div class="friend">FRIEND</div>
<h2>Symbol</h2>
<div class="x">X</div>
<div class="o">O</div>
<div class="play">PLAY</div>
</div>
<div class="gameover hide"></div>
</div>
<script src="options.js"></script>
<script src="game.js"></script>
</body>
</html>
file : style.css
@font-face {
font-family: 'Fascinate Inline';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Fascinate Inline'), local('FascinateInline-Regular'), url(font/jVyR7mzzB3zc-jp6QCAu60poNqIy5grIfA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
.container{
width: 450px;
height: 450px;
display: block;
margin: 25px auto;
position: relative;
}
#cvs{
position: absolute;
border: 1px solid #000;
}
.options{
position: absolute;
width: 450px;
height: 450px;
}
.options h1{
text-align: center;
font-size: 65px;
font-family: 'Fascinate Inline', cursive;
color: #f00;
margin: 30px 0 20px 0;
}
.options h2{
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
text-align: center;
font-weight: bold;
}
.options div{
display: inline-block;
}
.options .computer, .x{
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
padding: 5px;
background-color: #FFF;
color: #000;
font-size: 1.5em;
border-radius: 5px;
margin-left: 70px;
width: 150px;
text-align: center;
cursor: pointer;
border: 1px solid #000;
}
.options .friend, .o{
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
padding: 5px;
background-color: #FFF;
color: #000;
font-size: 1.5em;
border-radius: 5px;
margin-left: 10px;
width: 150px;
text-align: center;
cursor: pointer;
border: 1px solid #000;
}
.options .friend:hover, .computer:hover, .x:hover, .o:hover{
background-color: #011627;
color: #FFF;
}
.options .play{
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
font-size: 1.5em;
width: 70px;
display: block;
margin: 25px auto;
border-radius: 5px;
border: 1px solid #011627;
text-align: center;
padding: 10px;
background-color: #011627;
color: #FFF;
cursor: pointer;
transition: 100ms width ease-in;
}
.options .play:hover{
width: 110px;
}
.active{
background-color: #011627 !important;
color: #FFF !important;
}
.hide{
display: none;
}
.gameover{
position: absolute;
width: 450px;
height: 450px;
background-color: rgba(0, 0, 0, 0.95);
}
.gameover h1{
text-align: center;
font-size: 50px;
font-family: 'Fascinate Inline', cursive;
color: #fff;
margin: 40px 0 20px 0;
}
.gameover .winner-img{
display: block;
margin: 20px auto;
}
.gameover .play{
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
font-size: 1.5em;
width: 150px;
display: block;
margin: 25px auto;
border-radius: 5px;
border: 1px solid #fff;
text-align: center;
padding: 10px;
background-color: #fff;
color: #000;
cursor: pointer;
transition: 100ms width ease-in;
}
.gameover .play:hover{
width: 200px;
}
#player-name{
border: 3px solid black;
border-radius: 6px;
padding: 10px;
width: 80%;
margin-left: 35px;
font-size: 20px;
text-transform: uppercase;
font-weight: bold;
}
file : options.js
const options = document.querySelector(".options");
const computerBtn = document.querySelector(".computer");
const friendBtn = document.querySelector(".friend");
const xBtn = document.querySelector(".x");
const oBtn = document.querySelector(".o");
const playBtn = document.querySelector(".play");
const gameOverElement = document.querySelector(".gameover");
const player = new Object;
let OPPONENT;
oBtn.addEventListener("click", function(){
player.man = "O";
player.computer = "X";
player.friend = "X";
switchActive(xBtn, oBtn);
});
xBtn.addEventListener("click", function(){
player.man = "X";
player.computer = "O";
player.friend = "O";
switchActive(oBtn, xBtn);
});
computerBtn.addEventListener("click", function(){
OPPONENT = "computer";
switchActive(friendBtn, computerBtn);
});
friendBtn.addEventListener("click", function(){
OPPONENT = "friend";
switchActive(computerBtn, friendBtn);
});
playBtn.addEventListener("click", function(){
let name=document.querySelector("#player-name").value;
if( !OPPONENT){
computerBtn.style.backgroundColor = "red";
friendBtn.style.backgroundColor = "red";
return;
}
if( !player.man ){
oBtn.style.backgroundColor = "red";
xBtn.style.backgroundColor = "red";
return;
}
init(player, OPPONENT,name);
console.log(player)
options.classList.add("hide");
});
function switchActive(off, on){
off.classList.remove("active");
on.classList.add("active");
}
file : game.js
function init(player, OPPONENT,name){
console.log(name);
const canvas = document.getElementById("cvs");
const ctx = canvas.getContext("2d");
let board = [];
const COLUMN = 3;
const ROW = 3;
const SPACE_SIZE = 150;
let gameData = new Array(9);
let currentPlayer = player.man;
const xImage = new Image();
xImage.src = "img/X.png";
const oImage = new Image();
oImage.src = "img/O.png";
const COMBOS = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
let GAME_OVER = false;
function drawBoard(){
let id = 0
for(let i = 0; i < ROW; i++){
board[i] = [];
for(let j = 0; j < COLUMN; j++){
board[i][j] = id;
id++;
ctx.strokeStyle = "#000";
ctx.strokeRect(j * SPACE_SIZE, i * SPACE_SIZE, SPACE_SIZE, SPACE_SIZE);
}
}
}
drawBoard();
canvas.addEventListener("click", function(event){
if(GAME_OVER) return;
let X = event.clientX - canvas.getBoundingClientRect().x;
let Y = event.clientY - canvas.getBoundingClientRect().y;
let i = Math.floor(Y/SPACE_SIZE);
let j = Math.floor(X/SPACE_SIZE);
let id = board[i][j];
if(gameData[id]) return;
gameData[id] = currentPlayer;
drawOnBoard(currentPlayer, i, j);
if(isWinner(gameData, currentPlayer)){
showGameOver(currentPlayer);
GAME_OVER = true;
return;
}
if(isTie(gameData)){
showGameOver("tie");
GAME_OVER = true;
return;
}
if( OPPONENT == "computer"){
let id = minimax( gameData, player.computer ).id;
gameData[id] = player.computer;
let space = getIJ(id);
drawOnBoard(player.computer, space.i, space.j);
if(isWinner(gameData, player.computer)){
showGameOver(player.computer);
GAME_OVER = true;
return;
}
if(isTie(gameData)){
showGameOver("tie");
GAME_OVER = true;
return;
}
}else{
currentPlayer = currentPlayer == player.man ? player.friend : player.man;
}
});
function minimax(gameData, PLAYER){
if( isWinner(gameData, player.computer) ) return { evaluation : +10 };
if( isWinner(gameData, player.man) ) return { evaluation : -10 };
if( isTie(gameData) ) return { evaluation : 0 };
let EMPTY_SPACES = getEmptySpaces(gameData);
let moves = [];
for( let i = 0; i < EMPTY_SPACES.length; i++){
let id = EMPTY_SPACES[i];
let backup = gameData[id];
gameData[id] = PLAYER;
let move = {};
move.id = id;
if( PLAYER == player.computer){
move.evaluation = minimax(gameData, player.man).evaluation;
}else{
move.evaluation = minimax(gameData, player.computer).evaluation;
}
gameData[id] = backup;
moves.push(move);
}
let bestMove;
if(PLAYER == player.computer){
let bestEvaluation = -Infinity;
for(let i = 0; i < moves.length; i++){
if( moves[i].evaluation > bestEvaluation ){
bestEvaluation = moves[i].evaluation;
bestMove = moves[i];
}
}
}else{
let bestEvaluation = +Infinity;
for(let i = 0; i < moves.length; i++){
if( moves[i].evaluation < bestEvaluation ){
bestEvaluation = moves[i].evaluation;
bestMove = moves[i];
}
}
}
return bestMove;
}
function getEmptySpaces(gameData){
let EMPTY = [];
for( let id = 0; id < gameData.length; id++){
if(!gameData[id]) EMPTY.push(id);
}
return EMPTY;
}
function getIJ(id){
for(let i = 0; i < board.length; i++){
for(let j = 0; j < board[i].length; j++){
if(board[i][j] == id) return { i : i, j : j}
}
}
}
function isWinner(gameData, player){
for(let i = 0; i < COMBOS.length; i++){
let won = true;
for(let j = 0; j < COMBOS[i].length; j++){
let id = COMBOS[i][j];
won = gameData[id] == player && won;
}
if(won){
return true;
}
}
return false;
}
function isTie(gameData){
let isBoardFill = true;
for(let i = 0; i < gameData.length; i++){
isBoardFill = gameData[i] && isBoardFill;
}
if(isBoardFill){
return true;
}
return false;
}
function showGameOver(player){
console.log(currentPlayer,name,player)
let str=name;
if(currentPlayer!==player){
str="Player 2";
}
let message = player == "tie" ? "Oops No Winner" : `The Winner is ${str}`;
let imgSrc = `img/${player}.png`;
gameOverElement.innerHTML = `
<h1>${message}</1>
<img class="winner-img" src=${imgSrc} </img>
<div class="play" onclick="location.reload()">Play Again!</div>
`;
gameOverElement.classList.remove("hide");
}
function drawOnBoard(player, i, j){
let img = player == "X" ? xImage : oImage;
ctx.drawImage(img, j * SPACE_SIZE, i * SPACE_SIZE);
}
}
'X.png', 'O.png' and "tie.png" (place in "img" folder)