<div>
<div id="menuScreen">
<div>
<h1>Tic Tac Toe</h1>
<h2>ONLINE</h2>
</div>
<div>
Start Game
Game Stats
Settings
Exit
</div>
<div>
<div>by: Gameind</div>
</div>
</div>
<div id="modeSelection">
<h2>Select Game Mode</h2>
<div>
Classic 3×3
Advanced 4×4
Expert 5×5
</div>
Back to Menu
</div>
<div id="playerSelection">
<h2>Select Opponent</h2>
<div>
With Friends
With Computer
</div>
Back
</div>
<div id="difficultySelection">
<h2>Select Difficulty</h2>
<div>
Easy
Normal
Hard
</div>
Back
</div>
<div id="settingsScreen">
<h2>Settings</h2>
<div>
Music Volume
</div>
<div>
Sound Effects Volume
</div>
<div>
Select Theme
<div>
</div>
</div>
Back to Menu
</div>
<div id="statsScreen">
<h2>Game Statistics</h2>
<div>
<div>
<span>Games Played:</span>
<span id="gamesPlayed">0</span>
</div>
<div>
<span>Player 1 Wins:</span>
<span id="player1Wins">0</span>
</div>
<div>
<span>Player 2 Wins:</span>
<span id="player2Wins">0</span>
</div>
<div>
<span>Computer Wins:</span>
<span id="computerWins">0</span>
</div>
<div>
<span>Draws:</span>
<span id="draws">0</span>
</div>
<div>
<span>Win Rate:</span>
<span id="winRate">0%</span>
</div>
</div>
Reset Stats
Back to Menu
</div>
<div id="gameScreen">
Menu
<div>
<div id="player1">Player 1 (X)</div>
<div id="player2">Player 2 (O)</div>
</div>
<div>
<div id="gameBoard"></div>
</div>
</div>
<div id="winScreen">
<div id="winMessage"></div>
<div>
Menu
Play Again
</div>
</div>
</div>
{
button.addEventListener('mouseenter', () => {
if (!isMuted) {
hoverSound.currentTime = 0;
hoverSound.play().catch(e => console.log("Sound play prevented:", e));
}
});
button.addEventListener('click', () => {
if (!isMuted) {
clickSound.currentTime = 0;
clickSound.play().catch(e => console.log("Sound play prevented:", e));
}
});
});
// Event Listeners
startBtn.addEventListener('click', () => {
showScreen(modeSelection);
});
statsBtn.addEventListener('click', () => {
updateStatsDisplay();
showScreen(statsScreen);
});
settingsBtn.addEventListener('click', () => {
showScreen(settingsScreen);
});
exitBtn.addEventListener('click', () => {
window.close();
});
modeBackBtn.addEventListener('click', backToMenu);
playerBackBtn.addEventListener('click', () => {
showScreen(modeSelection);
});
difficultyBackBtn.addEventListener('click', () => {
showScreen(playerSelection);
});
settingsBackBtn.addEventListener('click', backToMenu);
statsBackBtn.addEventListener('click', backToMenu);
resetStatsBtn.addEventListener('click', resetStats);
gameMenuBtn.addEventListener('click', backToMenu);
winMenuBtn.addEventListener('click', backToMenu);
playAgainBtn.addEventListener('click', () => {
winScreen.style.display = 'none';
initializeGame();
});
// Mode selection buttons
document.querySelectorAll('.mode-btn').forEach(btn => {
btn.addEventListener('click', () => {
boardSize = parseInt(btn.dataset.size);
showScreen(playerSelection);
});
});
// Player type selection
friendBtn.addEventListener('click', () => {
vsComputer = false;
startGame();
});
computerBtn.addEventListener('click', () => {
vsComputer = true;
showScreen(difficultySelection);
});
// Difficulty selection
difficultyButtons.forEach(btn => {
btn.addEventListener('click', () => {
computerDifficulty = btn.dataset.difficulty;
startGame();
});
});
// Volume controls
musicVolume.addEventListener('input', () => {
menuMusic.volume = musicVolume.value;
gameMusic.volume = musicVolume.value;
});
sfxVolume.addEventListener('input', () => {
moveSound.volume = sfxVolume.value;
winSound.volume = sfxVolume.value;
drawSound.volume = sfxVolume.value;
hoverSound.volume = sfxVolume.value * 0.6;
clickSound.volume = sfxVolume.value;
});
// Theme buttons
themeButtons.forEach(btn => {
btn.addEventListener('click', () => {
document.body.className = `theme-${btn.dataset.theme}`;
});
});
// Functions
function showScreen(screen) {
// Hide all screens
const screens = [
menuScreen, modeSelection, playerSelection,
difficultySelection, settingsScreen, statsScreen, gameScreen, winScreen
];
screens.forEach(s => s.style.display = 'none');
// Show the requested screen
screen.style.display = 'flex';
}
function backToMenu() {
showScreen(menuScreen);
// Switch music
gameMusic.pause();
menuMusic.currentTime = 0;
menuMusic.play();
}
function startGame() {
showScreen(gameScreen);
// Switch music
menuMusic.pause();
gameMusic.currentTime = 0;
gameMusic.play();
initializeGame();
}
function initializeGame() {
// Clear the board
gameBoard.innerHTML = '';
// Initialize game state
gameState = Array(boardSize * boardSize).fill('');
gameActive = true;
currentPlayer = 'X';
// Set active player
player1Display.classList.add('active-player');
player2Display.classList.remove('active-player');
player2Display.textContent = vsComputer ? 'Computer (O)' : 'Player 2 (O)';
// Create the game board
gameBoard.style.gridTemplateColumns = `repeat(${boardSize}, 1fr)`;
gameBoard.style.gridTemplateRows = `repeat(${boardSize}, 1fr)`;
// Calculate cell size based on board size to keep the board reasonable
const maxBoardWidth = window.innerWidth * 0.9;
const maxBoardHeight = window.innerHeight * 0.6;
const cellSize = Math.min(
80,
Math.min(maxBoardWidth, maxBoardHeight) / boardSize
);
gameBoard.style.width = `${cellSize * boardSize + (boardSize - 1) * 5}px`;
gameBoard.style.height = `${cellSize * boardSize + (boardSize - 1) * 5}px`;
// Create cells
for (let i = 0; i < boardSize * boardSize; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.index = i;
cell.style.width = `${cellSize}px`;
cell.style.height = `${cellSize}px`;
cell.addEventListener('click', handleCellClick);
gameBoard.appendChild(cell);
}
}
function handleCellClick(e) {
const clickedCell = e.target;
const clickedCellIndex = parseInt(clickedCell.dataset.index);
if (gameState[clickedCellIndex] !== '' || !gameActive) {
return;
}
// Play move sound immediately to reduce delay
if (!isMuted) {
moveSound.currentTime = 0;
moveSound.play().catch(e => console.log("Sound play prevented:", e));
}
// Animate cell
clickedCell.style.transform = 'scale(0.9)';
setTimeout(() => {
clickedCell.style.transform = 'scale(1)';
}, 100);
// Update game state
makeMove(clickedCellIndex, clickedCell);
}
function makeMove(index, cell) {
gameState[index] = currentPlayer;
cell.classList.add(currentPlayer.toLowerCase());
// Check for win or draw
checkGameResult();
// If playing against computer and game is still active
if (vsComputer && gameActive && currentPlayer === 'O') {
setTimeout(computerMove, 500); // Add slight delay for better UX
}
}
function computerMove() {
if (!gameActive) return;
let moveIndex;
// AI logic based on difficulty
switch (computerDifficulty) {
case 'easy':
// Random moves
moveIndex = findRandomMove();
break;
case 'normal':
// Mix of random and strategic moves
if (Math.random() < 0.7) {
moveIndex = findWinningMove('O') || findWinningMove('X') || findStrategicMove() || findRandomMove();
} else {
moveIndex = findRandomMove();
}
break;
case 'hard':
// Always tries to win or block
moveIndex = findWinningMove('O') || findWinningMove('X') || findStrategicMove() || findRandomMove();
break;
default:
moveIndex = findRandomMove();
}
// Make sure the move is valid
if (gameState[moveIndex] === '') {
const cell = document.querySelector(`.cell[data-index="${moveIndex}"]`);
// Play move sound
if (!isMuted) {
moveSound.currentTime = 0;
moveSound.play().catch(e => console.log("Sound play prevented:", e));
}
// Animate cell
cell.style.transform = 'scale(0.9)';
setTimeout(() => {
cell.style.transform = 'scale(1)';
}, 100);
// Update game state
makeMove(moveIndex, cell);
}
}
function findRandomMove() {
const emptyCells = gameState.map((cell, index) => cell === '' ? index : null).filter(val => val !== null);
return emptyCells.length > 0 ? emptyCells[Math.floor(Math.random() * emptyCells.length)] : null;
}
function findWinningMove(player) {
// Check rows
for (let row = 0; row < boardSize; row++) {
const start = row * boardSize;
const cells = gameState.slice(start, start + boardSize);
const emptyIndex = cells.indexOf('');
if (cells.filter(cell => cell === player).length === boardSize - 1 && emptyIndex !== -1) {
return start + emptyIndex;
}
}
// Check columns
for (let col = 0; col < boardSize; col++) {
const columnCells = [];
for (let row = 0; row < boardSize; row++) {
columnCells.push(gameState[row * boardSize + col]);
}
const emptyIndex = columnCells.indexOf('');
if (columnCells.filter(cell => cell === player).length === boardSize - 1 && emptyIndex !== -1) {
return emptyIndex * boardSize + col;
}
}
// Check diagonals
const diag1 = []; // Top-left to bottom-right
const diag2 = []; // Top-right to bottom-left
for (let i = 0; i < boardSize; i++) {
diag1.push(gameState[i * boardSize + i]);
diag2.push(gameState[i * boardSize + (boardSize - 1 - i)]);
}
let emptyIndex = diag1.indexOf('');
if (diag1.filter(cell => cell === player).length === boardSize - 1 && emptyIndex !== -1) {
return emptyIndex * boardSize + emptyIndex;
}
emptyIndex = diag2.indexOf('');
if (diag2.filter(cell => cell === player).length === boardSize - 1 && emptyIndex !== -1) {
return emptyIndex * boardSize + (boardSize - 1 - emptyIndex);
}
return null;
}
function findStrategicMove() {
// For larger boards, prioritize center and corners
if (boardSize > 3) {
const center = Math.floor(boardSize / 2);
const centerIndex = center * boardSize + center;
if (gameState[centerIndex] === '') return centerIndex;
// Check corners
const corners = [
0, // top-left
boardSize - 1, // top-right
(boardSize - 1) * boardSize, // bottom-left
boardSize * boardSize - 1 // bottom-right
];
for (const corner of corners) {
if (gameState[corner] === '') return corner;
}
} else {
// For 3x3, just pick center if available
const centerIndex = 4;
if (gameState[centerIndex] === '') return centerIndex;
}
return null;
}
function checkGameResult() {
// Check for win
if (checkWin()) {
gameActive = false;
const winner = currentPlayer === 'X' ? '1' : vsComputer ? 'Computer' : '2';
showWinScreen(`${winner} Wins!`);
// Update stats
if (currentPlayer === 'X') {
stats.player1Wins++;
} else if (vsComputer) {
*********************
} else {
stats.player2Wins++;
}
stats.gamesPlayed++;
saveStats();
if (!isMuted) {
winSound.currentTime = 0;
winSound.play().catch(e => console.log("Sound play prevented:", e));
}
createConfetti();
return;
}
// Check for draw
if (!gameState.includes('')) {
gameActive = false;
showWinScreen("It's a Draw!");
// Update stats
stats.draws++;
stats.gamesPlayed++;
saveStats();
if (!isMuted) {
drawSound.currentTime = 0;
drawSound.play().catch(e => console.log("Sound play prevented:", e));
}
return;
}
// Switch player
switchPlayer();
}
function checkWin() {
// Check rows
for (let row = 0; row < boardSize; row++) {
const start = row * boardSize;
const end = start + boardSize;
const rowCells = gameState.slice(start, end);
if (rowCells.every(cell => cell === currentPlayer && cell !== '')) {
highlightWinningCells(Array.from({length: boardSize}, (_, i) => start + i));
return true;
}
}
// Check columns
for (let col = 0; col < boardSize; col++) {
const colCells = [];
for (let row = 0; row < boardSize; row++) {
colCells.push(gameState[row * boardSize + col]);
}
if (colCells.every(cell => cell === currentPlayer && cell !== '')) {
highlightWinningCells(Array.from({length: boardSize}, (_, i) => i * boardSize + col));
return true;
}
}
// Check diagonals
const diag1 = []; // Top-left to bottom-right
const diag2 = []; // Top-right to bottom-left
for (let i = 0; i < boardSize; i++) {
diag1.push(gameState[i * boardSize + i]);
diag2.push(gameState[i * boardSize + (boardSize - 1 - i)]);
}
if (diag1.every(cell => cell === currentPlayer && cell !== '')) {
highlightWinningCells(Array.from({length: boardSize}, (_, i) => i * boardSize + i));
return true;
}
if (diag2.every(cell => cell === currentPlayer && cell !== '')) {
highlightWinningCells(Array.from({length: boardSize}, (_, i) => i * boardSize + (boardSize - 1 - i)));
return true;
}
return false;
}
function highlightWinningCells(cellIndices) {
cellIndices.forEach(index => {
document.querySelector(`.cell[data-index="${index}"]`).classList.add('winning-cell');
});
}
function switchPlayer() {
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
// Update active player display
if (currentPlayer === 'X') {
player1Display.classList.add('active-player');
player2Display.classList.remove('active-player');
player2Display.textContent = vsComputer ? 'Computer (O)' : 'Player 2 (O)';
} else {
player2Display.classList.add('active-player');
player1Display.classList.remove('active-player');
player2Display.textContent = vsComputer ? 'Computer (O)' : 'Player 2 (O)';
}
}
function showWinScreen(message) {
winMessage.textContent = message;
showScreen(winScreen);
}
function createConfetti() {
const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'];
const container = document.querySelector('.container');
for (let i = 0; i < 100; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.top = -10 + 'px';
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.width = Math.random() * 10 + 5 + 'px';
confetti.style.height = Math.random() * 10 + 5 + 'px';
confetti.style.animationDuration = Math.random() * 3 + 2 + 's';
container.appendChild(confetti);
// Remove confetti after animation
setTimeout(() => {
confetti.remove();
}, 5000);
}
}
function saveStats() {
localStorage.setItem('ticTacToeStats', JSON.stringify(stats));
}
function loadStats() {
const savedStats = localStorage.getItem('ticTacToeStats');
if (savedStats) {
stats = JSON.parse(savedStats);
}
updateStatsDisplay();
}
function resetStats() {
stats = {
gamesPlayed: 0,
player1Wins: 0,
player2Wins: 0,
computerWins: 0,
draws: 0
};
saveStats();
updateStatsDisplay();
}
function updateStatsDisplay() {
gamesPlayedDisplay.textContent = stats.gamesPlayed;
player1WinsDisplay.textContent = stats.player1Wins;
player2WinsDisplay.textContent = stats.player2Wins;
computerWinsDisplay.textContent = *******************
drawsDisplay.textContent = stats.draws;
const totalWins = stats.player1Wins + stats.player2Wins + *******************
const winRate = totalWins > 0 ? Math.round((stats.player1Wins / totalWins) * 100) : 0;
winRateDisplay.textContent = `${winRate}%`;
}
});