<html><head><base href="https://webbox-ai.vercel.app/tic-tac-toe/" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tic Tac Toe with AI</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
padding: 20px;
}
h1 {
text-align: center;
margin-bottom: 20px;
color: #333;
}
.board {
width: 300px;
height: 300px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 5px;
margin-bottom: 20px;
background-color: #6c5ce7;
padding: 5px;
border-radius: 10px;
}
.square {
width: 100%;
height: 100%;
background-color: #f5f5f5;
font-size: 40px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 5px;
transition: all 0.3s ease;
}
.square:hover {
background-color: #e0e0e0;
}
.square.x {
color: #e74c3c;
}
.square.o {
color: #3498db;
}
.restart-button {
display: block;
margin: 20px auto;
padding: 12px 20px;
border: none;
background-color: #ffd700;
font-weight: bold;
font-size: 18px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.restart-button:hover {
background-color: #ffc400;
}
.message {
text-align: center;
margin-bottom: 20px;
font-size: 24px;
font-weight: bold;
color: #333;
}
.controls {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
.difficulty-selector {
padding: 10px;
font-size: 16px;
border-radius: 5px;
border: 1px solid #ccc;
}
.player-selector {
padding: 10px 20px;
font-size: 16px;
border-radius: 5px;
border: none;
background-color: #3498db;
color: white;
cursor: pointer;
transition: background-color 0.3s ease;
}
.player-selector:hover {
background-color: #2980b9;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.square.fade-in {
animation: fadeIn 0.5s;
}
</style>
</head>
<body>
<h1>Tic Tac Toe with AI</h1>
<div class="controls">
<select class="difficulty-selector">
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="hard">Hard</option>
</select>
<button class="player-selector">Play as O</button>
</div>
<div class="board">
<div class="square" id="square0"></div>
<div class="square" id="square1"></div>
<div class="square" id="square2"></div>
<div class="square" id="square3"></div>
<div class="square" id="square4"></div>
<div class="square" id="square5"></div>
<div class="square" id="square6"></div>
<div class="square" id="square7"></div>
<div class="square" id="square8"></div>
</div>
<p class="message"></p>
<button class="restart-button">Restart Game</button>
<script>
const board = document.querySelector(".board");
const squares = document.querySelectorAll(".square");
const message = document.querySelector(".message");
const restartBtn = document.querySelector(".restart-button");
const difficultySelector = document.querySelector(".difficulty-selector");
const playerSelector = document.querySelector(".player-selector");
const players = ["X", "O"];
let currentPlayer = players[0];
let humanPlayer = players[0];
let aiPlayer = players[1];
let gameBoard = ["", "", "", "", "", "", "", "", ""];
let gameActive = true;
const winningPatterns = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
message.textContent = `${currentPlayer}'s turn`;
function handleCellPlayed(clickedCell, clickedCellIndex) {
gameBoard[clickedCellIndex] = currentPlayer;
clickedCell.textContent = currentPlayer;
clickedCell.classList.add(currentPlayer.toLowerCase(), "fade-in");
}
function handlePlayerChange() {
currentPlayer = currentPlayer === players[0] ? players[1] : players[0];
message.textContent = `${currentPlayer}'s turn`;
}
function handleResultValidation() {
let roundWon = false;
for (let i = 0; i < winningPatterns.length; i++) {
const [a, b, c] = winningPatterns[i];
if (gameBoard[a] && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) {
roundWon = true;
break;
}
}
if (roundWon) {
message.textContent = `${currentPlayer} wins!`;
gameActive = false;
return;
}
let roundDraw = !gameBoard.includes("");
if (roundDraw) {
message.textContent = `Game ended in a draw!`;
gameActive = false;
return;
}
handlePlayerChange();
}
function handleCellClick(clickedCellEvent) {
const clickedCell = clickedCellEvent.target;
const clickedCellIndex = parseInt(clickedCell.id.replace("square", ""));
if (gameBoard[clickedCellIndex] !== "" || !gameActive) {
return;
}
handleCellPlayed(clickedCell, clickedCellIndex);
handleResultValidation();
if (gameActive && currentPlayer === aiPlayer) {
setTimeout(makeAiMove, 500);
}
}
function makeAiMove() {
const difficulty = difficultySelector.value;
let move;
switch (difficulty) {
case "easy":
move = getRandomEmptyCell();
break;
case "medium":
move = Math.random() < 0.5 ? getBestMove() : getRandomEmptyCell();
break;
case "hard":
move = getBestMove();
break;
}
const cell = document.getElementById(`square${move}`);
handleCellPlayed(cell, move);
handleResultValidation();
}
function getRandomEmptyCell() {
const emptyCells = gameBoard.reduce((acc, cell, index) => {
if (cell === "") acc.push(index);
return acc;
}, []);
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
function getBestMove() {
let bestScore = -Infinity;
let bestMove;
for (let i = 0; i < 9; i++) {
if (gameBoard[i] === "") {
gameBoard[i] = aiPlayer;
let score = minimax(gameBoard, 0, false);
gameBoard[i] = "";
if (score > bestScore) {
bestScore = score;
bestMove = i;
}
}
}
return bestMove;
}
function minimax(board, depth, isMaximizing) {
const scores = {
X: -1,
O: 1,
tie: 0
};
let result = checkWinner();
if (result !== null) {
return scores[result];
}
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === "") {
board[i] = aiPlayer;
let score = minimax(board, depth + 1, false);
board[i] = "";
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === "") {
board[i] = humanPlayer;
let score = minimax(board, depth + 1, true);
board[i] = "";
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
function checkWinner() {
for (let i = 0; i < winningPatterns.length; i++) {
const [a, b, c] = winningPatterns[i];
if (gameBoard[a] && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) {
return gameBoard[a];
}
}
if (!gameBoard.includes("")) {
return "tie";
}
return null;
}
function handleRestartGame() {
currentPlayer = humanPlayer;
gameBoard = ["", "", "", "", "", "", "", "", ""];
gameActive = true;
message.textContent = `${currentPlayer}'s turn`;
squares.forEach(cell => {
cell.textContent = "";
cell.classList.remove("x", "o", "fade-in");
});
if (currentPlayer === aiPlayer) {
setTimeout(makeAiMove, 500);
}
}
squares.forEach(cell => cell.addEventListener("click", handleCellClick));
restartBtn.addEventListener("click", handleRestartGame);
playerSelector.addEventListener("click", () => {
humanPlayer = humanPlayer === players[0] ? players[1] : players[0];
aiPlayer = aiPlayer === players[0] ? players[1] : players[0];
playerSelector.textContent = `Play as ${humanPlayer === players[0] ? "O" : "X"}`;
handleRestartGame();
});
difficultySelector.addEventListener("change", handleRestartGame);
</script>
</body>
</html>