anycoder-8cd657a4 / index.html
jeevav62's picture
Upload folder using huggingface_hub
8271925 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Tic Tac Toe</title>
<!-- Google Fonts for Modern Typography -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #0f172a;
--surface-color: #1e293b;
--primary-color: #8b5cf6; /* Violet */
--secondary-color: #06b6d4; /* Cyan */
--text-color: #f1f5f9;
--text-muted: #94a3b8;
--win-color: #10b981;
--font-main: 'Outfit', sans-serif;
--cell-size: 100px;
--gap: 15px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
}
body {
font-family: var(--font-main);
background-color: var(--bg-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-x: hidden;
}
/* --- Header --- */
header {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
z-index: 10;
}
h1 {
font-weight: 700;
font-size: 2.5rem;
letter-spacing: -1px;
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 5px;
}
.built-with {
font-size: 0.85rem;
color: var(--text-muted);
text-decoration: none;
opacity: 0.7;
transition: opacity 0.3s;
}
.built-with:hover {
opacity: 1;
text-decoration: underline;
}
/* --- Container --- */
.app-container {
width: 100%;
max-width: 500px;
padding: 20px;
position: relative;
perspective: 1000px;
}
/* --- Screens --- */
.screen {
background: var(--surface-color);
padding: 2rem;
border-radius: 24px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
}
.hidden {
display: none;
opacity: 0;
transform: scale(0.9);
pointer-events: none;
}
.active {
display: flex;
opacity: 1;
transform: scale(1);
}
/* --- Input Forms --- */
.input-group {
width: 100%;
text-align: center;
}
label {
display: block;
font-size: 0.9rem;
color: var(--text-muted);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 1px;
}
input {
width: 100%;
padding: 15px;
border-radius: 12px;
border: 2px solid transparent;
background: rgba(0,0,0,0.2);
color: white;
font-size: 1.1rem;
font-family: inherit;
text-align: center;
transition: 0.3s;
outline: none;
}
input:focus {
border-color: var(--primary-color);
background: rgba(0,0,0,0.4);
}
/* --- Buttons --- */
.btn {
padding: 15px 40px;
border-radius: 50px;
border: none;
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
font-family: inherit;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color), #6d28d9);
color: white;
box-shadow: 0 10px 20px -5px rgba(139, 92, 246, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 15px 25px -5px rgba(139, 92, 246, 0.5);
}
.btn-secondary {
background: transparent;
color: var(--text-muted);
font-size: 0.9rem;
padding: 10px;
}
.btn-secondary:hover {
color: white;
}
/* --- Scoreboard --- */
.scoreboard {
display: flex;
justify-content: space-between;
width: 100%;
background: rgba(0,0,0,0.2);
padding: 15px;
border-radius: 16px;
margin-bottom: 10px;
}
.player-score {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.score-val {
font-size: 1.5rem;
font-weight: 700;
}
.score-label {
font-size: 0.75rem;
color: var(--text-muted);
text-transform: uppercase;
}
.vs-divider {
display: flex;
align-items: center;
color: var(--text-muted);
font-weight: 700;
padding: 0 10px;
}
.active-turn {
color: var(--win-color);
text-shadow: 0 0 10px rgba(16, 185, 129, 0.4);
}
/* --- Game Grid --- */
.game-board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--gap);
margin: 20px 0;
}
.cell {
width: var(--cell-size);
height: var(--cell-size);
background: rgba(255,255,255,0.03);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 3.5rem;
font-weight: 700;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
box-shadow: inset 0 0 0 2px transparent;
}
.cell:hover:not(.taken) {
background: rgba(255,255,255,0.07);
transform: scale(1.02);
}
.cell.x {
color: var(--primary-color);
text-shadow: 0 0 15px rgba(139, 92, 246, 0.3);
}
.cell.o {
color: var(--secondary-color);
text-shadow: 0 0 15px rgba(6, 182, 212, 0.3);
}
.cell.win {
background: rgba(16, 185, 129, 0.2);
box-shadow: inset 0 0 0 2px var(--win-color);
animation: pulse 1s infinite;
}
/* --- Turn Indicator --- */
.turn-display {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 10px;
height: 30px;
}
/* --- Animations --- */
@keyframes popIn {
0% { transform: scale(0); opacity: 0; }
80% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(1); }
}
.pop {
animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.03); }
100% { transform: scale(1); }
}
/* --- Canvas for Confetti --- */
#confetti-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 100;
}
/* --- Responsive --- */
@media (max-width: 400px) {
:root { --cell-size: 80px; --gap: 10px; }
h1 { font-size: 2rem; }
}
</style>
</head>
<body>
<canvas id="confetti-canvas"></canvas>
<header>
<h1>Tic Tac Toe</h1>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a>
</header>
<div class="app-container">
<!-- Screen 1: Setup -->
<div id="setup-screen" class="screen active">
<h2 style="font-weight: 300;">Enter Player Names</h2>
<div class="input-group">
<label>Player X</label>
<input type="text" id="p1-name" placeholder="Name" maxlength="10">
</div>
<div class="input-group">
<label>Player O</label>
<input type="text" id="p2-name" placeholder="Name" maxlength="10">
</div>
<button class="btn btn-primary" onclick="startGame()">Start Game</button>
<div id="reset-history-container" style="display:none; text-align: center; margin-top: 10px;">
<button class="btn btn-secondary" onclick="clearHistory()">Reset History</button>
</div>
</div>
<!-- Screen 2: Gameplay -->
<div id="game-screen" class="screen hidden">
<div class="turn-display" id="turn-indicator">Player X's Turn</div>
<div class="scoreboard">
<div class="player-score" id="p1-score-box">
<span class="score-val" id="score-p1">0</span>
<span class="score-label" id="name-p1-display">P1</span>
<span style="font-size: 0.7rem; color: var(--primary-color)">Wins</span>
</div>
<div class="vs-divider">VS</div>
<div class="player-score" id="p2-score-box">
<span class="score-val" id="score-p2">0</span>
<span class="score-label" id="name-p2-display">P2</span>
<span style="font-size: 0.7rem; color: var(--secondary-color)">Wins</span>
</div>
</div>
<div class="game-board" id="board">
<!-- Cells generated by JS -->
</div>
<button class="btn btn-secondary" onclick="backToMenu()">Back to Menu</button>
<button class="btn btn-secondary" onclick="resetRound()">New Round</button>
</div>
</div>
<script>
/* --- State Management --- */
const gameState = {
board: Array(9).fill(null),
currentPlayer: 'X',
isGameActive: true,
players: {
p1: { name: 'Player X', score: 0, symbol: 'X' },
p2: { name: 'Player O', score: 0, symbol: 'O' }
}
};
/* --- DOM Elements --- */
const setupScreen = document.getElementById('setup-screen');
const gameScreen = document.getElementById('game-screen');
const boardEl = document.getElementById('board');
const p1Input = document.getElementById('p1-name');
const p2Input = document.getElementById('p2-name');
const turnIndicator = document.getElementById('turn-indicator');
const resetHistoryBtn = document.getElementById('reset-history-container');
/* --- Initialization --- */
window.onload = () => {
loadHistory();
createBoard();
};
function createBoard() {
boardEl.innerHTML = '';
for (let i = 0; i < 9; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.setAttribute('data-index', i);
cell.addEventListener('click', handleCellClick);
boardEl.appendChild(cell);
}
}
/* --- Game Logic --- */
function startGame() {
const p1Val = p1Input.value.trim() || 'Player X';
const p2Val = p2Input.value.trim() || 'Player O';
gameState.players.p1.name = p1Val;
gameState.players.p2.name = p2Val;
// Save names to local storage for next time
localStorage.setItem('ttt_p1_name', p1Val);
localStorage.setItem('ttt_p2_name', p2Val);
updateUI();
switchScreen('game');
resetHistoryBtn.style.display = 'block';
}
function backToMenu() {
switchScreen('setup');
}
function resetRound() {
gameState.board = Array(9).fill(null);
gameState.currentPlayer = 'X';
gameState.isGameActive = true;
const cells = document.querySelectorAll('.cell');
cells.forEach(cell => {
cell.textContent = '';
cell.classList.remove('x', 'o', 'taken', 'win', 'pop');
});
updateTurnIndicator();
}
function handleCellClick(e) {
const cell = e.target;
const index = cell.getAttribute('data-index');
if (gameState.board[index] !== null || !gameState.isGameActive) return;
// Update Logic
gameState.board[index] = gameState.currentPlayer;
// Update Visuals
cell.textContent = gameState.currentPlayer;
cell.classList.add(gameState.currentPlayer.toLowerCase(), 'taken', 'pop');
// Check Win
if (checkWin()) {
endGame(false);
} else if (checkDraw()) {
endGame(true);
} else {
// Swap Turn
gameState.currentPlayer = gameState.currentPlayer === 'X' ? 'O' : 'X';
updateTurnIndicator();
}
}
function checkWin() {
const winConditions = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // Rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], // Cols
[0, 4, 8], [2, 4, 6] // Diagonals
];
return winConditions.some(combination => {
const [a, b, c] = combination;
if (gameState.board[a] &&
gameState.board[a] === gameState.board[b] &&
gameState.board[a] === gameState.board[c]) {
// Highlight winning cells
highlightWin(combination);
return true;
}
return false;
});
}
function checkDraw() {
return gameState.board.every(cell => cell !== null);
}
function endGame(isDraw) {
gameState.isGameActive = false;
if (isDraw) {
turnIndicator.textContent = "It's a Draw!";
turnIndicator.style.color = "var(--text-muted)";
} else {
const winnerName = gameState.currentPlayer === 'X'
? gameState.players.p1.name
: gameState.players.p2.name;
turnIndicator.textContent = `${winnerName} Wins!`;
turnIndicator.style.color = "var(--win-color)";
// Update Score
if (gameState.currentPlayer === 'X') {
gameState.players.p1.score++;
document.getElementById('score-p1').textContent = gameState.players.p1.score;
} else {
gameState.players.p2.score++;
document.getElementById('score-p2').textContent = gameState.players.p2.score;
}
saveScore();
triggerConfetti();
}
}
function highlightWin(combo) {
const cells = document.querySelectorAll('.cell');
combo.forEach(index => {
cells[index].classList.add('win');
});
}
function updateTurnIndicator() {
const name = gameState.currentPlayer === 'X'
? gameState.players.p1.name
: gameState.players.p2.name;
turnIndicator.textContent = `${name}'s Turn (${gameState.currentPlayer})`;
turnIndicator.style.color = gameState.currentPlayer === 'X' ? 'var(--primary-color)' : 'var(--secondary-color)';
}
function updateUI() {
document.getElementById('name-p1-display').textContent = gameState.players.p1.name;
document.getElementById('name-p2-display').textContent = gameState.players.p2.name;
document.getElementById('score-p1').textContent = gameState.players.p1.score;
document.getElementById('score-p2').textContent = gameState.players.p2.score;
updateTurnIndicator();
}
/* --- Storage & History --- */
function saveScore() {
const history = {
p1Score: gameState.players.p1.score,
p2Score: gameState.players.p2.score
};
localStorage.setItem('ttt_scores', JSON.stringify(history));
}
function loadHistory() {
// Load Names
const savedP1 = localStorage.getItem('ttt_p1_name');
const savedP2 = localStorage.getItem('ttt_p2_name');
if (savedP1) p1Input.value = savedP1;
if (savedP2) p2Input.value = savedP2;
// Load Scores
const savedScores = localStorage.getItem('ttt_scores');
if (savedScores) {
const scores = JSON.parse(savedScores);
gameState.players.p1.score = scores.p1Score;
gameState.players.p2.score = scores.p2Score;
resetHistoryBtn.style.display = 'block';
} else {
resetHistoryBtn.style.display = 'none';
}
}
function clearHistory() {
if(confirm("Reset all scores and history?")) {
localStorage.removeItem('ttt_scores');
localStorage.removeItem('ttt_p1_name');
localStorage.removeItem('ttt_p2_name');
gameState.players.p1.score = 0;
gameState.players.p2.score = 0;
p1Input.value = '';
p2Input.value = '';
resetHistoryBtn.style.display = 'none';
location.reload();
}
}
/* --- Helper --- */
function switchScreen(screenName) {
if (screenName === 'setup') {
setupScreen.classList.remove('hidden');
setupScreen.classList.add('active');
gameScreen.classList.remove('active');
gameScreen.classList.add('hidden');
} else {
setupScreen.classList.remove('active');
setupScreen.classList.add('hidden');
gameScreen.classList.remove('hidden');
gameScreen.classList.add('active');
resetRound();
}
}
/* --- Gamification: Simple Confetti Engine --- */
const canvas = document.getElementById('confetti-canvas');
const ctx = canvas.getContext('2d');
let particles = [];
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function triggerConfetti() {
particles = [];
for(let i=0; i<150; i++) {
particles.push({
x: canvas.width / 2,
y: canvas.height / 2,
vx: (Math.random() - 0.5) * 20,
vy: (Math.random() - 1) * 20,
size: Math.random() * 8 + 4,
color: `hsl(${Math.random() * 360}, 100%, 50%)`,
life: 100
});
}
animateConfetti();
}
function animateConfetti() {
if(particles.length === 0) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach((p, index) => {
p.x += p.vx;
p.y += p.vy;
p.vy += 0.5; // Gravity
p.life--;
p.size *= 0.96;
ctx.fillStyle = p.color;
ctx.fillRect(p.x, p.y, p.size, p.size);
if(p.life <= 0) particles.splice(index, 1);
});
requestAnimationFrame(animateConfetti);
}
</script>
</body>
</html>