Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Beach Tic-Tac-Toe</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| min-height: 100vh; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); | |
| font-family: 'Arial', sans-serif; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* Animated waves background */ | |
| .waves { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 200px; | |
| background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none"><path d="M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113-14.29,1200,52.47V0Z" opacity=".25" fill="%23fff"/><path d="M0,0V15.81C13,36.92,27.64,56.86,47.69,72.05,99.41,111.27,165,111,224.58,91.58c31.15-10.15,60.09-26.07,89.67-39.8,40.92-19,84.73-46,130.83-49.67,36.26-2.85,70.9,9.42,98.6,31.56,31.77,25.39,62.32,62,103.63,73,40.44,10.79,81.35-6.69,119.13-24.28s75.16-39,116.92-43.05c59.73-5.85,113.28,22.88,168.9,38.84,30.2,8.66,59,6.17,87.09-7.5,22.43-10.89,48-26.93,60.65-49.24V0Z" opacity=".5" fill="%23fff"/><path d="M0,0V5.63C149.93,59,314.09,71.32,475.83,42.57c43-7.64,84.23-20.12,127.61-26.46,59-8.63,112.48,12.24,165.56,35.4C827.93,77.22,886,95.24,951.2,90c86.53-7,172.46-45.71,248.8-84.81V0Z" fill="%23fff"/></svg>') repeat-x; | |
| animation: wave 8s ease-in-out infinite; | |
| } | |
| @keyframes wave { | |
| 0%, 100% { transform: translateX(0); } | |
| 50% { transform: translateX(-25%); } | |
| } | |
| .game-container { | |
| background: rgba(255, 255, 255, 0.15); | |
| backdrop-filter: blur(20px); | |
| border-radius: 25px; | |
| padding: 40px; | |
| box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| z-index: 10; | |
| text-align: center; | |
| max-width: 500px; | |
| width: 90%; | |
| } | |
| h1 { | |
| color: white; | |
| font-size: 2.5rem; | |
| margin-bottom: 10px; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); | |
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .subtitle { | |
| color: rgba(255, 255, 255, 0.9); | |
| font-size: 1.1rem; | |
| margin-bottom: 30px; | |
| font-weight: 300; | |
| } | |
| .game-info { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 30px; | |
| color: white; | |
| font-size: 1.1rem; | |
| } | |
| .current-player { | |
| font-weight: bold; | |
| padding: 10px 20px; | |
| background: rgba(255, 255, 255, 0.2); | |
| border-radius: 15px; | |
| backdrop-filter: blur(10px); | |
| } | |
| .game-board { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 10px; | |
| margin: 30px auto; | |
| max-width: 300px; | |
| background: rgba(255, 255, 255, 0.1); | |
| padding: 15px; | |
| border-radius: 20px; | |
| backdrop-filter: blur(10px); | |
| } | |
| .cell { | |
| width: 80px; | |
| height: 80px; | |
| background: rgba(255, 255, 255, 0.9); | |
| border: none; | |
| border-radius: 15px; | |
| font-size: 2.5rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .cell:hover:not(.filled) { | |
| background: rgba(255, 255, 255, 1); | |
| transform: translateY(-3px); | |
| box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2); | |
| } | |
| .cell.filled { | |
| cursor: not-allowed; | |
| transform: scale(1.05); | |
| } | |
| .cell.winner { | |
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4); | |
| animation: winner-pulse 1s ease-in-out infinite alternate; | |
| } | |
| @keyframes winner-pulse { | |
| from { transform: scale(1.05); } | |
| to { transform: scale(1.1); } | |
| } | |
| .shell { | |
| color: #ff6b6b; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); | |
| animation: shell-appear 0.5s ease-out; | |
| } | |
| .starfish { | |
| color: #4ecdc4; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); | |
| animation: starfish-appear 0.5s ease-out; | |
| } | |
| @keyframes shell-appear { | |
| from { transform: scale(0) rotate(-180deg); opacity: 0; } | |
| to { transform: scale(1) rotate(0deg); opacity: 1; } | |
| } | |
| @keyframes starfish-appear { | |
| from { transform: scale(0) rotate(180deg); opacity: 0; } | |
| to { transform: scale(1) rotate(0deg); opacity: 1; } | |
| } | |
| .game-status { | |
| font-size: 1.5rem; | |
| font-weight: bold; | |
| margin: 20px 0; | |
| color: white; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); | |
| min-height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .game-controls { | |
| display: flex; | |
| gap: 15px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| } | |
| .reset-btn, .play-again-btn { | |
| background: linear-gradient(45deg, #ff6b6b, #4ecdc4); | |
| color: white; | |
| border: none; | |
| padding: 15px 30px; | |
| font-size: 1.1rem; | |
| border-radius: 25px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); | |
| font-weight: bold; | |
| min-width: 140px; | |
| } | |
| .play-again-btn { | |
| background: linear-gradient(45deg, #4ecdc4, #45b7d1); | |
| animation: pulse-glow 2s ease-in-out infinite; | |
| } | |
| @keyframes pulse-glow { | |
| 0%, 100% { | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2), 0 0 20px rgba(78, 205, 196, 0.3); | |
| } | |
| 50% { | |
| box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3), 0 0 30px rgba(78, 205, 196, 0.6); | |
| transform: translateY(-2px); | |
| } | |
| } | |
| .reset-btn:hover, .play-again-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); | |
| } | |
| .reset-btn:hover { | |
| background: linear-gradient(45deg, #ff5252, #26a69a); | |
| } | |
| .play-again-btn:hover { | |
| background: linear-gradient(45deg, #26a69a, #2196f3); | |
| } | |
| .score-board { | |
| display: flex; | |
| justify-content: space-around; | |
| margin-top: 20px; | |
| color: white; | |
| } | |
| .score { | |
| text-align: center; | |
| background: rgba(255, 255, 255, 0.1); | |
| padding: 10px 15px; | |
| border-radius: 15px; | |
| backdrop-filter: blur(10px); | |
| } | |
| .score-label { | |
| font-size: 0.9rem; | |
| opacity: 0.8; | |
| margin-bottom: 5px; | |
| } | |
| .score-value { | |
| font-size: 1.5rem; | |
| font-weight: bold; | |
| } | |
| /* Floating particles */ | |
| .particle { | |
| position: absolute; | |
| pointer-events: none; | |
| opacity: 0.6; | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0px) rotate(0deg); } | |
| 50% { transform: translateY(-20px) rotate(180deg); } | |
| } | |
| /* Responsive design */ | |
| @media (max-width: 480px) { | |
| .game-container { | |
| padding: 20px; | |
| margin: 20px; | |
| } | |
| h1 { | |
| font-size: 2rem; | |
| } | |
| .cell { | |
| width: 70px; | |
| height: 70px; | |
| font-size: 2rem; | |
| } | |
| .game-info { | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="waves"></div> | |
| <div class="game-container"> | |
| <h1>ποΈ Beach Tic-Tac-Toe</h1> | |
| <p class="subtitle">Shells vs Starfish</p> | |
| <div class="game-info"> | |
| <div class="current-player" id="currentPlayer"> | |
| Current: π Shell's Turn | |
| </div> | |
| </div> | |
| <div class="game-board" id="gameBoard"> | |
| <button class="cell" data-cell="0"></button> | |
| <button class="cell" data-cell="1"></button> | |
| <button class="cell" data-cell="2"></button> | |
| <button class="cell" data-cell="3"></button> | |
| <button class="cell" data-cell="4"></button> | |
| <button class="cell" data-cell="5"></button> | |
| <button class="cell" data-cell="6"></button> | |
| <button class="cell" data-cell="7"></button> | |
| <button class="cell" data-cell="8"></button> | |
| </div> | |
| <div class="game-status" id="gameStatus"></div> | |
| <div class="game-controls"> | |
| <button class="reset-btn" onclick="resetGame()">π New Game</button> | |
| <button class="play-again-btn" id="playAgainBtn" onclick="playAgain()" style="display: none;">π Play Again</button> | |
| </div> | |
| <div class="score-board"> | |
| <div class="score"> | |
| <div class="score-label">π Shells</div> | |
| <div class="score-value" id="shellScore">0</div> | |
| </div> | |
| <div class="score"> | |
| <div class="score-label">β Starfish</div> | |
| <div class="score-value" id="starfishScore">0</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentPlayer = 'shell'; | |
| let gameBoard = ['', '', '', '', '', '', '', '', '']; | |
| let gameActive = true; | |
| let scores = { shell: 0, starfish: 0 }; | |
| const winningConditions = [ | |
| [0, 1, 2], [3, 4, 5], [6, 7, 8], // rows | |
| [0, 3, 6], [1, 4, 7], [2, 5, 8], // columns | |
| [0, 4, 8], [2, 4, 6] // diagonals | |
| ]; | |
| const cells = document.querySelectorAll('.cell'); | |
| const currentPlayerDisplay = document.getElementById('currentPlayer'); | |
| const gameStatus = document.getElementById('gameStatus'); | |
| const shellScoreDisplay = document.getElementById('shellScore'); | |
| const starfishScoreDisplay = document.getElementById('starfishScore'); | |
| const playAgainBtn = document.getElementById('playAgainBtn'); | |
| // Audio Context for sound effects | |
| let audioContext; | |
| function initAudio() { | |
| if (!audioContext) { | |
| audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| } | |
| } | |
| function playSound(frequency, duration = 0.3, type = 'sine', volume = 0.1) { | |
| initAudio(); | |
| const oscillator = audioContext.createOscillator(); | |
| const gainNode = audioContext.createGain(); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(audioContext.destination); | |
| oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); | |
| oscillator.type = type; | |
| gainNode.gain.setValueAtTime(0, audioContext.currentTime); | |
| gainNode.gain.linearRampToValueAtTime(volume, audioContext.currentTime + 0.01); | |
| gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration); | |
| oscillator.start(audioContext.currentTime); | |
| oscillator.stop(audioContext.currentTime + duration); | |
| } | |
| function playWinSound() { | |
| // Victory fanfare | |
| const notes = [523.25, 659.25, 783.99, 1046.50]; // C, E, G, C | |
| notes.forEach((note, index) => { | |
| setTimeout(() => { | |
| playSound(note, 0.4, 'triangle', 0.15); | |
| }, index * 150); | |
| }); | |
| // Add some sparkle | |
| setTimeout(() => { | |
| for (let i = 0; i < 5; i++) { | |
| setTimeout(() => { | |
| playSound(1567.98 + Math.random() * 200, 0.2, 'sine', 0.1); | |
| }, i * 100); | |
| } | |
| }, 600); | |
| } | |
| function playPlaceSound() { | |
| // Soft ocean wave sound | |
| playSound(220 + Math.random() * 100, 0.2, 'sine', 0.08); | |
| } | |
| function playTieSound() { | |
| // Gentle tie sound | |
| playSound(330, 0.6, 'triangle', 0.1); | |
| setTimeout(() => playSound(277, 0.6, 'triangle', 0.1), 300); | |
| } | |
| function playHoverSound() { | |
| playSound(440 + Math.random() * 100, 0.1, 'sine', 0.05); | |
| } | |
| // Add floating particles | |
| function createParticles() { | |
| const particles = ['π', 'β', 'π', 'ποΈ']; | |
| for (let i = 0; i < 8; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| particle.textContent = particles[Math.floor(Math.random() * particles.length)]; | |
| particle.style.left = Math.random() * 100 + '%'; | |
| particle.style.top = Math.random() * 100 + '%'; | |
| particle.style.animationDelay = Math.random() * 6 + 's'; | |
| particle.style.fontSize = (Math.random() * 20 + 15) + 'px'; | |
| document.body.appendChild(particle); | |
| } | |
| } | |
| function handleCellClick(e) { | |
| const cellIndex = parseInt(e.target.getAttribute('data-cell')); | |
| if (gameBoard[cellIndex] !== '' || !gameActive) { | |
| return; | |
| } | |
| // Play placement sound | |
| playPlaceSound(); | |
| gameBoard[cellIndex] = currentPlayer; | |
| e.target.textContent = currentPlayer === 'shell' ? 'π' : 'β'; | |
| e.target.classList.add('filled', currentPlayer); | |
| if (checkWinner()) { | |
| gameActive = false; | |
| highlightWinningCells(); | |
| gameStatus.innerHTML = `π ${currentPlayer === 'shell' ? 'Shells' : 'Starfish'} Win! <br><span style="font-size: 0.8em; opacity: 0.8;">Congratulations!</span>`; | |
| scores[currentPlayer]++; | |
| updateScores(); | |
| // Show play again button | |
| playAgainBtn.style.display = 'inline-block'; | |
| // Play win sound | |
| setTimeout(() => { | |
| playWinSound(); | |
| }, 300); | |
| // Celebration effect | |
| setTimeout(() => { | |
| createCelebrationEffect(); | |
| }, 500); | |
| // Screen shake effect | |
| document.body.style.animation = 'shake 0.5s ease-in-out'; | |
| setTimeout(() => { | |
| document.body.style.animation = ''; | |
| }, 500); | |
| } else if (gameBoard.every(cell => cell !== '')) { | |
| gameActive = false; | |
| gameStatus.innerHTML = "π€ It's a Tie! <br><span style='font-size: 0.8em; opacity: 0.8;'>Great game!</span>"; | |
| playAgainBtn.style.display = 'inline-block'; | |
| playTieSound(); | |
| } else { | |
| currentPlayer = currentPlayer === 'shell' ? 'starfish' : 'shell'; | |
| currentPlayerDisplay.innerHTML = `Current: ${currentPlayer === 'shell' ? 'π Shell\'s Turn' : 'β Starfish\'s Turn'}`; | |
| } | |
| } | |
| function checkWinner() { | |
| return winningConditions.some(condition => { | |
| const [a, b, c] = condition; | |
| return gameBoard[a] !== '' && | |
| gameBoard[a] === gameBoard[b] && | |
| gameBoard[a] === gameBoard[c]; | |
| }); | |
| } | |
| function highlightWinningCells() { | |
| winningConditions.forEach(condition => { | |
| const [a, b, c] = condition; | |
| if (gameBoard[a] !== '' && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) { | |
| cells[a].classList.add('winner'); | |
| cells[b].classList.add('winner'); | |
| cells[c].classList.add('winner'); | |
| } | |
| }); | |
| } | |
| function createCelebrationEffect() { | |
| const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#ff9ff3', '#54a0ff']; | |
| const emojis = ['π', 'π', 'β', 'π', 'β¨', 'π']; | |
| // Confetti burst | |
| for (let i = 0; i < 50; i++) { | |
| const confetti = document.createElement('div'); | |
| confetti.style.position = 'absolute'; | |
| confetti.style.left = Math.random() * 100 + 'vw'; | |
| confetti.style.top = '-10px'; | |
| confetti.style.width = Math.random() * 8 + 5 + 'px'; | |
| confetti.style.height = Math.random() * 8 + 5 + 'px'; | |
| confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; | |
| confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '0'; | |
| confetti.style.pointerEvents = 'none'; | |
| confetti.style.zIndex = '1000'; | |
| confetti.style.animation = `fall ${Math.random() * 3 + 2}s linear`; | |
| confetti.style.transform = `rotate(${Math.random() * 360}deg)`; | |
| document.body.appendChild(confetti); | |
| setTimeout(() => { | |
| confetti.remove(); | |
| }, 5000); | |
| } | |
| // Emoji celebration | |
| for (let i = 0; i < 15; i++) { | |
| const emoji = document.createElement('div'); | |
| emoji.textContent = emojis[Math.floor(Math.random() * emojis.length)]; | |
| emoji.style.position = 'absolute'; | |
| emoji.style.left = Math.random() * 100 + 'vw'; | |
| emoji.style.top = '-20px'; | |
| emoji.style.fontSize = Math.random() * 20 + 20 + 'px'; | |
| emoji.style.pointerEvents = 'none'; | |
| emoji.style.zIndex = '1001'; | |
| emoji.style.animation = `fall ${Math.random() * 4 + 3}s linear`; | |
| document.body.appendChild(emoji); | |
| setTimeout(() => { | |
| emoji.remove(); | |
| }, 7000); | |
| } | |
| } | |
| function updateScores() { | |
| shellScoreDisplay.textContent = scores.shell; | |
| starfishScoreDisplay.textContent = scores.starfish; | |
| } | |
| function playAgain() { | |
| resetGame(); | |
| playAgainBtn.style.display = 'none'; | |
| // Play a fresh start sound | |
| playSound(523.25, 0.3, 'triangle', 0.1); | |
| } | |
| function resetGame() { | |
| gameBoard = ['', '', '', '', '', '', '', '', '']; | |
| gameActive = true; | |
| currentPlayer = 'shell'; | |
| currentPlayerDisplay.innerHTML = 'Current: π Shell\'s Turn'; | |
| gameStatus.innerHTML = ''; | |
| playAgainBtn.style.display = 'none'; | |
| cells.forEach(cell => { | |
| cell.textContent = ''; | |
| cell.classList.remove('filled', 'shell', 'starfish', 'winner'); | |
| }); | |
| } | |
| // Initialize game | |
| cells.forEach(cell => { | |
| cell.addEventListener('click', handleCellClick); | |
| cell.addEventListener('mouseenter', () => { | |
| if (!cell.classList.contains('filled')) { | |
| playHoverSound(); | |
| } | |
| }); | |
| }); | |
| // Add CSS for animations | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes fall { | |
| to { | |
| transform: translateY(100vh) rotate(720deg); | |
| opacity: 0; | |
| } | |
| } | |
| @keyframes shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 10%, 30%, 50%, 70%, 90% { transform: translateX(-2px); } | |
| 20%, 40%, 60%, 80% { transform: translateX(2px); } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // Create initial particles | |
| createParticles(); | |
| </script> | |
| </body> | |
| </html> |