// Game State let currentPlayer = 'X'; let gameBoard = ['', '', '', '', '', '', '', '', '']; let gameActive = true; let player1Name = ''; let player2Name = ''; let player1Avatar = '⭐'; let player2Avatar = '🌸'; let player1Score = 0; let player2Score = 0; // DOM Elements const nameScreen = document.getElementById('nameScreen'); const gameScreen = document.getElementById('gameScreen'); const winnerScreen = document.getElementById('winnerScreen'); const player1Input = document.getElementById('player1Name'); const player2Input = document.getElementById('player2Name'); const startBtn = document.getElementById('startBtn'); const player1Display = document.getElementById('player1Display'); const player2Display = document.getElementById('player2Display'); const gameStatus = document.getElementById('gameStatus'); const gameBoardElement = document.getElementById('gameBoard'); const cells = document.querySelectorAll('[data-cell]'); const resetBtn = document.getElementById('resetBtn'); const changeNamesBtn = document.getElementById('changeNamesBtn'); const changeNamesBtn2 = document.getElementById('changeNamesBtn2'); const playAgainBtn = document.getElementById('playAgainBtn'); const winnerName = document.getElementById('winnerName'); const winnerAvatar = document.getElementById('winnerAvatar'); // Avatar elements const player1AvatarOptions = document.getElementById('player1Avatars'); const player2AvatarOptions = document.getElementById('player2Avatars'); // Score elements const score1Element = document.getElementById('score1'); const score2Element = document.getElementById('score2'); const score1Name = document.getElementById('score1Name'); const score2Name = document.getElementById('score2Name'); const score1Avatar = document.getElementById('score1Avatar'); const score2Avatar = document.getElementById('score2Avatar'); // Symbol elements const player1SymbolElement = document.getElementById('player1Symbol'); const player2SymbolElement = document.getElementById('player2Symbol'); // Winning Combinations const winningCombinations = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; // Event Listeners startBtn.addEventListener('click', startGame); resetBtn.addEventListener('click', resetGame); changeNamesBtn.addEventListener('click', showNameScreen); changeNamesBtn2.addEventListener('click', showNameScreen); playAgainBtn.addEventListener('click', resetGame); cells.forEach(cell => { cell.addEventListener('click', handleCellClick); }); // Avatar selection listeners player1AvatarOptions.addEventListener('click', (e) => { if (e.target.classList.contains('avatar-option')) { player1AvatarOptions.querySelectorAll('.avatar-option').forEach(opt => { opt.classList.remove('selected'); }); e.target.classList.add('selected'); player1Avatar = e.target.dataset.avatar; } }); player2AvatarOptions.addEventListener('click', (e) => { if (e.target.classList.contains('avatar-option')) { player2AvatarOptions.querySelectorAll('.avatar-option').forEach(opt => { opt.classList.remove('selected'); }); e.target.classList.add('selected'); player2Avatar = e.target.dataset.avatar; } }); // Allow Enter key to start game player1Input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { player2Input.focus(); } }); player2Input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { startGame(); } }); // Functions function startGame() { // Get player names or use defaults player1Name = player1Input.value.trim() || 'Player 1'; player2Name = player2Input.value.trim() || 'Player 2'; // Randomly choose starting player currentPlayer = Math.random() < 0.5 ? 'X' : 'O'; // Update displays player1Display.textContent = player1Name; player2Display.textContent = player2Name; // Update symbols with avatars player1SymbolElement.textContent = player1Avatar; player2SymbolElement.textContent = player2Avatar; // Update score board score1Name.textContent = player1Name; score2Name.textContent = player2Name; score1Avatar.textContent = player1Avatar; score2Avatar.textContent = player2Avatar; score1Element.textContent = player1Score; score2Element.textContent = player2Score; // Switch to game screen showGameScreen(); updateStatus(); updatePlayerHighlight(); } function showNameScreen() { nameScreen.classList.add('active'); gameScreen.classList.remove('active'); winnerScreen.classList.remove('active'); } function showGameScreen() { nameScreen.classList.remove('active'); gameScreen.classList.add('active'); winnerScreen.classList.remove('active'); } function showWinnerScreen() { nameScreen.classList.remove('active'); gameScreen.classList.remove('active'); winnerScreen.classList.add('active'); } function handleCellClick(e) { const cell = e.target; const cellIndex = Array.from(cells).indexOf(cell); // Check if cell is already filled or game is not active if (gameBoard[cellIndex] !== '' || !gameActive) { return; } // Make move makeMove(cell, cellIndex); // Check for win or draw checkResult(); } function makeMove(cell, index) { gameBoard[index] = currentPlayer; // Use avatar instead of X/O const avatar = currentPlayer === 'X' ? player1Avatar : player2Avatar; cell.textContent = avatar; cell.classList.add(currentPlayer.toLowerCase()); // Create particle burst effect createParticleBurst(cell, avatar); // Play move sound playMoveSound(currentPlayer); } function checkResult() { let roundWon = false; let winningCombo = null; // Check all winning combinations for (let i = 0; i < winningCombinations.length; i++) { const [a, b, c] = winningCombinations[i]; if (gameBoard[a] && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) { roundWon = true; winningCombo = [a, b, c]; break; } } if (roundWon) { // Highlight winning cells winningCombo.forEach(index => { cells[index].classList.add('winning'); }); // Determine winner const winner = currentPlayer === 'X' ? player1Name : player2Name; const winnerAvatarSymbol = currentPlayer === 'X' ? player1Avatar : player2Avatar; gameActive = false; // Update score if (currentPlayer === 'X') { player1Score++; score1Element.textContent = player1Score; score1Element.classList.add('score-update'); setTimeout(() => score1Element.classList.remove('score-update'), 500); } else { player2Score++; score2Element.textContent = player2Score; score2Element.classList.add('score-update'); setTimeout(() => score2Element.classList.remove('score-update'), 500); } // Show winner screen after animation setTimeout(() => { winnerName.textContent = winner; winnerAvatar.textContent = winnerAvatarSymbol; showWinnerScreen(); playWinSound(); }, 1000); return; } // Check for draw if (!gameBoard.includes('')) { gameActive = false; gameStatus.textContent = "It's a Draw! 🤝"; setTimeout(() => { resetGame(); }, 2000); return; } // Switch player currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; updateStatus(); updatePlayerHighlight(); } function updateStatus() { const currentPlayerName = currentPlayer === 'X' ? player1Name : player2Name; gameStatus.textContent = `${currentPlayerName}'s Turn (${currentPlayer})`; } function updatePlayerHighlight() { const player1Div = document.querySelector('.player1-display'); const player2Div = document.querySelector('.player2-display'); if (currentPlayer === 'X') { player1Div.classList.add('active'); player2Div.classList.remove('active'); } else { player1Div.classList.remove('active'); player2Div.classList.add('active'); } } function resetGame() { // Reset game state currentPlayer = Math.random() < 0.5 ? 'X' : 'O'; gameBoard = ['', '', '', '', '', '', '', '', '']; gameActive = true; // Clear cells cells.forEach(cell => { cell.textContent = ''; cell.classList.remove('x', 'o', 'winning'); }); // Update display updateStatus(); updatePlayerHighlight(); // Show game screen showGameScreen(); } function playWinSound() { // Create a simple celebratory sound using Web Audio API try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); // Play a series of ascending notes const notes = [523.25, 587.33, 659.25, 783.99]; // C, D, E, G notes.forEach((frequency, index) => { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.frequency.value = frequency; oscillator.type = 'sine'; const startTime = audioContext.currentTime + (index * 0.15); const stopTime = startTime + 0.3; gainNode.gain.setValueAtTime(0.3, startTime); gainNode.gain.exponentialRampToValueAtTime(0.01, stopTime); oscillator.start(startTime); oscillator.stop(stopTime); }); } catch (error) { console.log('Audio not supported'); } } function playMoveSound(player) { // Create different sound for each player try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); // Different frequencies for different players oscillator.frequency.value = player === 'X' ? 523.25 : 659.25; // C or E oscillator.type = 'sine'; const startTime = audioContext.currentTime; const stopTime = startTime + 0.1; gainNode.gain.setValueAtTime(0.2, startTime); gainNode.gain.exponentialRampToValueAtTime(0.01, stopTime); oscillator.start(startTime); oscillator.stop(stopTime); } catch (error) { console.log('Audio not supported'); } } function createParticleBurst(cell, emoji) { // Get cell position const rect = cell.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; // Create particles const particleCount = 8; const particles = ['✨', '💫', '⭐', '🌟', emoji]; for (let i = 0; i < particleCount; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.textContent = particles[Math.floor(Math.random() * particles.length)]; // Random direction const angle = (Math.PI * 2 * i) / particleCount; const distance = 80 + Math.random() * 40; const tx = Math.cos(angle) * distance; const ty = Math.sin(angle) * distance; particle.style.left = centerX + 'px'; particle.style.top = centerY + 'px'; particle.style.setProperty('--tx', tx + 'px'); particle.style.setProperty('--ty', ty + 'px'); document.body.appendChild(particle); // Remove particle after animation setTimeout(() => { particle.remove(); }, 800); } } // Initialize player highlight updatePlayerHighlight();