CyberSmartTicTaeToe / index.html
fmlemos's picture
Update index.html
a2c86b9 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cyber Neon Tic Tac Toe</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
:root {
--neon-pink: #ff2a6d;
--neon-blue: #05d9e8;
--neon-purple: #d300c5;
--neon-green: #00ff9d;
--dark-bg: #0d0221;
}
body {
font-family: 'Orbitron', sans-serif;
background-color: var(--dark-bg);
color: white;
overflow-x: hidden;
}
.neon-text-pink {
color: var(--neon-pink);
text-shadow: 0 0 10px var(--neon-pink), 0 0 20px var(--neon-pink);
}
.neon-text-blue {
color: var(--neon-blue);
text-shadow: 0 0 10px var(--neon-blue), 0 0 20px var(--neon-blue);
}
.neon-border {
border: 2px solid var(--neon-blue);
box-shadow: 0 0 10px var(--neon-blue), inset 0 0 10px var(--neon-blue);
}
.neon-glow {
animation: glow 2s infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 5px var(--neon-blue), 0 0 10px var(--neon-blue);
}
to {
box-shadow: 0 0 15px var(--neon-blue), 0 0 30px var(--neon-blue);
}
}
.cell {
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 3rem;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.cell:hover {
background-color: rgba(5, 217, 232, 0.1);
}
.cell.x::before, .cell.x::after {
content: '';
position: absolute;
width: 80%;
height: 8px;
background-color: var(--neon-pink);
transform: rotate(45deg);
box-shadow: 0 0 10px var(--neon-pink), 0 0 20px var(--neon-pink);
}
.cell.x::after {
transform: rotate(-45deg);
}
.cell.o::before {
content: '';
position: absolute;
width: 60%;
height: 60%;
border-radius: 50%;
border: 8px solid var(--neon-blue);
box-shadow: 0 0 10px var(--neon-blue), 0 0 20px var(--neon-blue);
}
.explosion {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
.particle {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--neon-green);
box-shadow: 0 0 5px var(--neon-green), 0 0 10px var(--neon-green);
}
.cyber-btn {
background: linear-gradient(45deg, var(--neon-purple), var(--neon-blue));
border: none;
color: white;
padding: 12px 24px;
font-family: 'Orbitron', sans-serif;
font-weight: bold;
letter-spacing: 2px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.cyber-btn:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(5, 217, 232, 0.4);
}
.cyber-btn:active {
transform: translateY(1px);
}
.cyber-btn::before {
content: '';
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
background: linear-gradient(45deg, var(--neon-purple), var(--neon-blue), var(--neon-green));
z-index: -1;
filter: blur(10px);
opacity: 0;
transition: opacity 0.3s ease;
}
.cyber-btn:hover::before {
opacity: 0.7;
}
.grid-lines {
position: absolute;
background-color: rgba(5, 217, 232, 0.3);
}
.grid-line-h {
width: 100%;
height: 2px;
}
.grid-line-v {
width: 2px;
height: 100%;
}
.ai-selection {
display: flex;
gap: 20px;
margin-top: 20px;
}
.ai-option {
padding: 10px 20px;
border: 2px solid var(--neon-blue);
cursor: pointer;
transition: all 0.3s ease;
}
.ai-option:hover {
background-color: rgba(5, 217, 232, 0.2);
}
.ai-option.selected {
background-color: var(--neon-blue);
color: var(--dark-bg);
font-weight: bold;
}
.scanline {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
transparent 0%,
rgba(13, 2, 33, 0.1) 50%,
transparent 100%
);
background-size: 100% 8px;
pointer-events: none;
animation: scanline 6s linear infinite;
z-index: 100;
opacity: 0.3;
}
@keyframes scanline {
0% {
background-position: 0 0;
}
100% {
background-position: 0 100vh;
}
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center p-4 relative">
<div class="scanline"></div>
<div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0">
<div class="grid-line-h top-1/3" style="top: 33.33%"></div>
<div class="grid-line-h top-2/3" style="top: 66.66%"></div>
<div class="grid-line-v left-1/3" style="left: 33.33%"></div>
<div class="grid-line-v left-2/3" style="left: 66.66%"></div>
</div>
<h1 class="text-5xl font-bold mb-8 neon-text-pink">CYBER TAC TOE</h1>
<div id="game-container" class="relative z-10">
<div id="ai-selection" class="mb-8 text-center">
<h2 class="text-xl neon-text-blue mb-4">SELECT AI OPPONENT</h2>
<div class="ai-selection justify-center">
<div class="ai-option selected" data-difficulty="easy">NOVICE</div>
<div class="ai-option" data-difficulty="medium">ADEPT</div>
<div class="ai-option" data-difficulty="hard">MASTER</div>
</div>
</div>
<div id="status" class="text-xl neon-text-blue mb-6 text-center h-8"></div>
<div class="grid grid-cols-3 gap-2 neon-border p-2 mb-8 relative">
<div class="cell" data-index="0"></div>
<div class="cell" data-index="1"></div>
<div class="cell" data-index="2"></div>
<div class="cell" data-index="3"></div>
<div class="cell" data-index="4"></div>
<div class="cell" data-index="5"></div>
<div class="cell" data-index="6"></div>
<div class="cell" data-index="7"></div>
<div class="cell" data-index="8"></div>
</div>
<div class="flex justify-center">
<button id="reset-btn" class="cyber-btn neon-glow">NEW GAME</button>
</div>
</div>
<div class="mt-8 text-sm neon-text-blue">
<p>SYSTEM STATUS: <span class="neon-text-green">OPERATIONAL</span></p>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const cells = document.querySelectorAll('.cell');
const statusDisplay = document.getElementById('status');
const resetButton = document.getElementById('reset-btn');
const aiOptions = document.querySelectorAll('.ai-option');
let gameActive = true;
let currentPlayer = 'X';
let gameState = ['', '', '', '', '', '', '', '', ''];
let aiDifficulty = 'easy';
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
];
// AI difficulty selection
aiOptions.forEach(option => {
option.addEventListener('click', () => {
aiOptions.forEach(opt => opt.classList.remove('selected'));
option.classList.add('selected');
aiDifficulty = option.dataset.difficulty;
resetGame();
});
});
// Handle cell click
function handleCellClick(e) {
const clickedCell = e.target;
const clickedCellIndex = parseInt(clickedCell.getAttribute('data-index'));
if (gameState[clickedCellIndex] !== '' || !gameActive) return;
handleCellPlayed(clickedCell, clickedCellIndex);
handleResultValidation();
// AI move if game is still active and it's O's turn
if (gameActive && currentPlayer === 'O') {
setTimeout(() => {
makeAIMove();
}, 800);
}
}
// Handle cell played
function handleCellPlayed(clickedCell, clickedCellIndex) {
gameState[clickedCellIndex] = currentPlayer;
clickedCell.classList.add(currentPlayer.toLowerCase());
createExplosion(clickedCell);
}
// Create explosion effect
function createExplosion(element) {
const explosion = document.createElement('div');
explosion.className = 'explosion';
element.appendChild(explosion);
const color = currentPlayer === 'X' ? 'var(--neon-pink)' : 'var(--neon-blue)';
// Create particles
for (let i = 0; i < 20; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.backgroundColor = color;
particle.style.boxShadow = `0 0 5px ${color}, 0 0 10px ${color}`;
// Random position
const angle = Math.random() * Math.PI * 2;
const distance = 10 + Math.random() * 50;
const x = 50 + Math.cos(angle) * distance;
const y = 50 + Math.sin(angle) * distance;
particle.style.left = `${x}%`;
particle.style.top = `${y}%`;
// Animation
gsap.to(particle, {
x: Math.cos(angle) * 100,
y: Math.sin(angle) * 100,
opacity: 0,
duration: 1,
ease: 'power2.out',
onComplete: () => {
particle.remove();
}
});
explosion.appendChild(particle);
}
// Remove explosion after animation
setTimeout(() => {
explosion.remove();
}, 1000);
}
// Validate game result
function handleResultValidation() {
let roundWon = false;
for (let i = 0; i < winningConditions.length; i++) {
const [a, b, c] = winningConditions[i];
if (gameState[a] === '' || gameState[b] === '' || gameState[c] === '') continue;
if (gameState[a] === gameState[b] && gameState[b] === gameState[c]) {
roundWon = true;
break;
}
}
if (roundWon) {
statusDisplay.textContent = `PLAYER ${currentPlayer} DOMINATES!`;
gameActive = false;
highlightWinningCells();
return;
}
const roundDraw = !gameState.includes('');
if (roundDraw) {
statusDisplay.textContent = 'SYSTEM STALEMATE!';
gameActive = false;
return;
}
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
statusDisplay.textContent = `PLAYER ${currentPlayer} TURN`;
}
// Highlight winning cells
function highlightWinningCells() {
for (let condition of winningConditions) {
const [a, b, c] = condition;
if (gameState[a] === '' || gameState[b] === '' || gameState[c] === '') continue;
if (gameState[a] === gameState[b] && gameState[b] === gameState[c]) {
cells[a].classList.add('neon-glow');
cells[b].classList.add('neon-glow');
cells[c].classList.add('neon-glow');
const color = currentPlayer === 'X' ? 'var(--neon-pink)' : 'var(--neon-blue)';
cells[a].style.boxShadow = `0 0 15px ${color}, 0 0 30px ${color}`;
cells[b].style.boxShadow = `0 0 15px ${color}, 0 0 30px ${color}`;
cells[c].style.boxShadow = `0 0 15px ${color}, 0 0 30px ${color}`;
break;
}
}
}
// AI move logic
function makeAIMove() {
if (!gameActive) return;
let move;
switch (aiDifficulty) {
case 'easy':
move = getRandomMove();
break;
case 'medium':
// 50% chance to make a smart move, 50% random
move = Math.random() < 0.5 ? getSmartMove() : getRandomMove();
break;
case 'hard':
move = getSmartMove();
break;
default:
move = getRandomMove();
}
if (move !== -1) {
const cell = cells[move];
handleCellPlayed(cell, move);
handleResultValidation();
}
}
// Get random available move
function getRandomMove() {
const availableMoves = gameState.map((cell, index) => cell === '' ? index : null).filter(val => val !== null);
if (availableMoves.length === 0) return -1;
return availableMoves[Math.floor(Math.random() * availableMoves.length)];
}
// Get smart move (tries to win or block)
function getSmartMove() {
// Check if AI can win
for (let condition of winningConditions) {
const [a, b, c] = condition;
if (gameState[a] === 'O' && gameState[b] === 'O' && gameState[c] === '') return c;
if (gameState[a] === 'O' && gameState[c] === 'O' && gameState[b] === '') return b;
if (gameState[b] === 'O' && gameState[c] === 'O' && gameState[a] === '') return a;
}
// Check if player can win and block
for (let condition of winningConditions) {
const [a, b, c] = condition;
if (gameState[a] === 'X' && gameState[b] === 'X' && gameState[c] === '') return c;
if (gameState[a] === 'X' && gameState[c] === 'X' && gameState[b] === '') return b;
if (gameState[b] === 'X' && gameState[c] === 'X' && gameState[a] === '') return a;
}
// Try to take center
if (gameState[4] === '') return 4;
// Try to take a corner
const corners = [0, 2, 6, 8];
const availableCorners = corners.filter(index => gameState[index] === '');
if (availableCorners.length > 0) {
return availableCorners[Math.floor(Math.random() * availableCorners.length)];
}
// Take any available move
return getRandomMove();
}
// Reset game
function resetGame() {
gameActive = true;
currentPlayer = 'X';
gameState = ['', '', '', '', '', '', '', '', ''];
statusDisplay.textContent = `PLAYER ${currentPlayer} TURN`;
cells.forEach(cell => {
cell.classList.remove('x', 'o', 'neon-glow');
cell.style.boxShadow = '';
});
}
// Event listeners
cells.forEach(cell => cell.addEventListener('click', handleCellClick));
resetButton.addEventListener('click', resetGame);
// Initialize game
resetGame();
});
</script>
</body>
</html>