FUTURE TIC TAC TOE | Premium XO Arena
by TurboDragon72477 lines26.4 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>FUTURE TIC TAC TOE | Premium XO Arena</title>
<!-- Google Fonts & Icons -->
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,600;14..32,700;14..32,800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: 'Inter', sans-serif;
background: radial-gradient(circle at 20% 30%, #0a0f1e, #03050b);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: #eef5ff;
}
/* premium glass container */
.app-container {
max-width: 550px;
width: 100%;
background: rgba(12, 18, 28, 0.65);
backdrop-filter: blur(18px);
border-radius: 64px;
padding: 24px 20px 32px;
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(72, 187, 255, 0.15);
transition: all 0.3s ease;
}
/* animated glow header */
.glow-title {
text-align: center;
font-weight: 800;
font-size: 2rem;
background: linear-gradient(135deg, #fff, #3b82f6, #a855f7);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: -0.5px;
animation: titlePulse 2s infinite alternate;
margin-bottom: 16px;
}
@keyframes titlePulse {
0% { text-shadow: 0 0 5px rgba(59,130,246,0.3);}
100% { text-shadow: 0 0 20px rgba(168,85,247,0.6);}
}
/* nav / profile row */
.profile-stats-bar {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(0, 0, 0, 0.4);
border-radius: 100px;
padding: 8px 16px;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 12px;
}
.avatar-name {
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
width: 44px;
height: 44px;
background: linear-gradient(145deg, #2563eb, #7c3aed);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: bold;
box-shadow: 0 0 8px #3b82f6;
}
.stats {
display: flex;
gap: 16px;
font-size: 0.8rem;
background: rgba(0,0,0,0.5);
padding: 6px 14px;
border-radius: 40px;
}
.stats span i { margin-right: 5px; color: #fbbf24;}
/* menu buttons grid */
.menu-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
margin: 20px 0;
}
.premium-btn {
background: rgba(20, 28, 40, 0.8);
border: 1px solid rgba(59,130,246,0.5);
border-radius: 48px;
padding: 12px 5px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
backdrop-filter: blur(5px);
transition: 0.2s;
cursor: pointer;
color: white;
font-size: 1rem;
}
.premium-btn:hover { transform: scale(0.97); background: #1e2a3e; border-color: #3b82f6; box-shadow: 0 0 12px #3b82f680;}
.game-panel {
background: rgba(0, 0, 0, 0.4);
border-radius: 48px;
padding: 20px 15px;
margin-top: 10px;
transition: all 0.3s;
}
.board {
display: grid;
justify-content: center;
gap: 12px;
margin: 20px auto;
}
.cell {
background: rgba(20, 28, 40, 0.9);
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 3rem;
transition: 0.1s ease;
cursor: pointer;
backdrop-filter: blur(4px);
box-shadow: 0 8px 0 rgba(0,0,0,0.2);
aspect-ratio: 1 / 1;
color: white;
}
.cell.X { color: #60a5fa; text-shadow: 0 0 8px #3b82f6; }
.cell.O { color: #f472b6; text-shadow: 0 0 8px #ec4899; }
.cell.win-highlight { background: radial-gradient(circle, #2dd4bf30, #0f766e70); box-shadow: 0 0 20px cyan; animation: winPop 0.3s;}
@keyframes winPop { 0% { transform: scale(0.9);} 100% { transform: scale(1);}}
.game-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
flex-wrap: wrap;
gap: 10px;
}
.turn-indicator {
background: #1e293b;
padding: 8px 18px;
border-radius: 60px;
font-weight: bold;
}
.icon-btn {
background: none;
border: none;
font-size: 1.5rem;
color: #cbd5e6;
cursor: pointer;
transition: 0.1s;
padding: 8px;
}
.icon-btn:active { transform: scale(0.9);}
.timer-warning {
font-family: monospace;
font-size: 1.4rem;
font-weight: bold;
color: #facc15;
}
.settings-drawer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #0f172ad9;
backdrop-filter: blur(30px);
border-radius: 32px 32px 0 0;
padding: 24px;
transform: translateY(100%);
transition: 0.3s cubic-bezier(0.2, 0.9, 0.4, 1.1);
z-index: 1000;
}
.settings-drawer.open { transform: translateY(0);}
.overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.6);
z-index: 999;
display: none;
}
.overlay.active { display: block;}
@media (max-width: 480px) {
.app-container { padding: 16px; }
.cell { font-size: 2rem; }
.premium-btn { font-size: 0.8rem; padding: 10px; }
}
</style>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1"></script>
<script>
// ----------------------------- STORE / STATE ---------------------------------
let appState = {
currentScreen: 'home', // home, game
gameMode: null, // 'single', 'multi', 'online'(simulate)
difficulty: 'medium', // easy, medium, hard, impossible
boardSize: 3, // 3,5,7
board: [],
currentPlayer: 'X', // X always starts
gameActive: true,
winner: null,
winningLine: null,
isMultiplayer: false,
stats: { wins: 0, losses: 0, draws: 0, xp: 0, level: 1 },
username: 'NOVA',
avatar: '⚡',
soundEnabled: true,
musicEnabled: true,
language: 'en', // en / ar
timerEnabled: false,
timeLeft: 12,
timerInterval: null,
undoStack: [],
boardTheme: 'default',
dailyRewardClaimed: false,
achievements: ['first_win', 'tactician'],
lastMove: null
};
// Helper: i18n
const i18n = {
en: { sing: '1P vs AI', multi: '2P Local', online: 'Online', stats: 'Stats', settings: 'Settings', xp: 'XP', level: 'LV', win:'W', loss:'L', draw:'D', easy:'Easy', medium:'Medium', hard:'Hard', impossible:'Impossible', undo:'Undo', restart:'Restart', timer:'Timer Mode', board3:'3x3', board5:'5x5', board7:'7x7', turn:'Turn', winner:'Winner', drawMsg:'Draw!', daily:'Daily Reward', claim:'Claim', leaderboard:'Leaderboard' },
ar: { sing: 'لاعب واحد', multi: 'محلي 2 لاعب', online: 'أونلاين', stats: 'إحصائيات', settings: 'الإعدادات', xp: 'نقاط', level: 'مستوى', win:'فوز', loss:'خسارة', draw:'تعادل', easy:'سهل', medium:'متوسط', hard:'صعب', impossible:'مستحيل', undo:'تراجع', restart:'إعادة', timer:'مؤقت', board3:'3x3', board5:'5x5', board7:'7x7', turn:'دور', winner:'الفائز', drawMsg:'تعادل!', daily:'مكافأة يومية', claim:'استلام', leaderboard:'الترتيب' }
};
function t(key) { return i18n[appState.language][key] || i18n.en[key] || key; }
// sound simulation (web audio beep)
function playSound(type) { if(!appState.soundEnabled) return; const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const osc = audioCtx.createOscillator(); const gain = audioCtx.createGain(); osc.connect(gain); gain.connect(audioCtx.destination); osc.type = 'sine'; osc.frequency.value = type==='move'? 880: 540; gain.gain.value = 0.1; osc.start(); gain.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.3); }
// helper: init empty board
function initBoard(size) { return Array(size).fill().map(() => Array(size).fill(null)); }
// minimax (impossible AI)
function minimax(board, depth, isMax, playerSymbol, aiSymbol, humanSymbol) {
const winner = checkWinnerOnBoard(board, aiSymbol, humanSymbol);
if(winner === aiSymbol) return 10 - depth;
if(winner === humanSymbol) return depth - 10;
if(isBoardFull(board)) return 0;
if(isMax) {
let best = -Infinity;
for(let i=0;i<board.length;i++) for(let j=0;j<board.length;j++) if(!board[i][j]) {
board[i][j] = aiSymbol;
let score = minimax(board, depth+1, false, playerSymbol, aiSymbol, humanSymbol);
board[i][j] = null;
best = Math.max(score, best);
}
return best;
} else {
let best = Infinity;
for(let i=0;i<board.length;i++) for(let j=0;j<board.length;j++) if(!board[i][j]) {
board[i][j] = humanSymbol;
let score = minimax(board, depth+1, true, playerSymbol, aiSymbol, humanSymbol);
board[i][j] = null;
best = Math.min(score, best);
}
return best;
}
}
function bestAIMove(board, aiSymbol, humanSymbol) {
let bestScore = -Infinity, move = null;
for(let i=0;i<board.length;i++) for(let j=0;j<board.length;j++) if(!board[i][j]) {
board[i][j] = aiSymbol;
let score = minimax(board, 0, false, null, aiSymbol, humanSymbol);
board[i][j] = null;
if(score > bestScore) { bestScore = score; move = [i,j]; }
}
return move;
}
function checkWinnerOnBoard(board, aiSym, humSym) {
const n = board.length;
for(let i=0;i<n;i++) { if(board[i].every(c=>c===aiSym)) return aiSym; if(board[i].every(c=>c===humSym)) return humSym; }
for(let j=0;j<n;j++) { let colA=true, colH=true; for(let i=0;i<n;i++) { if(board[i][j]!==aiSym) colA=false; if(board[i][j]!==humSym) colH=false; } if(colA) return aiSym; if(colH) return humSym; }
let d1A=true,d1H=true,d2A=true,d2H=true;
for(let i=0;i<n;i++) { if(board[i][i]!==aiSym) d1A=false; if(board[i][i]!==humSym) d1H=false; if(board[i][n-1-i]!==aiSym) d2A=false; if(board[i][n-1-i]!==humSym) d2H=false; }
if(d1A || d2A) return aiSym; if(d1H || d2H) return humSym;
return null;
}
function isBoardFull(board) { return board.every(row=>row.every(cell=>cell!==null)); }
function getWinnerAndLine(board, size) {
let winner = null, line = null;
const checkLine = (cells) => { if(cells[0] && cells.every(c=>c===cells[0])) { winner = cells[0]; return cells; } return null; };
for(let i=0;i<size;i++) { let row = board[i]; let res = checkLine(row); if(res) { winner = row[0]; line = {type:'row', index:i}; break; } }
if(!winner) for(let j=0;j<size;j++) { let col = board.map(r=>r[j]); let res = checkLine(col); if(res) { winner = col[0]; line = {type:'col', index:j}; break; } }
if(!winner) { let diag1 = board.map((_,i)=>board[i][i]); let res = checkLine(diag1); if(res) { winner = diag1[0]; line = {type:'diag', dir:'main'}; } }
if(!winner) { let diag2 = board.map((_,i)=>board[i][size-1-i]); let res = checkLine(diag2); if(res) { winner = diag2[0]; line = {type:'diag', dir:'anti'}; } }
return {winner, line};
}
function aiMove() {
if(!appState.gameActive || appState.currentPlayer !== 'O' || appState.gameMode !== 'single') return;
let board = appState.board;
let size = appState.boardSize;
let move = null;
if(appState.difficulty === 'easy') {
let empty = []; for(let i=0;i<size;i++) for(let j=0;j<size;j++) if(!board[i][j]) empty.push([i,j]);
if(empty.length) move = empty[Math.floor(Math.random()*empty.length)];
} else if(appState.difficulty === 'medium') {
let humanBlocks = false;
for(let i=0;i<size;i++) for(let j=0;j<size;j++) if(!board[i][j]) { board[i][j] = 'X'; let w = checkWinnerOnBoard(board,'O','X'); board[i][j]=null; if(w==='X') { move=[i,j]; humanBlocks=true; break; } }
if(!humanBlocks) {
let empty = []; for(let i=0;i<size;i++) for(let j=0;j<size;j++) if(!board[i][j]) empty.push([i,j]);
if(empty.length) move = empty[Math.floor(Math.random()*empty.length)];
}
} else if(appState.difficulty === 'hard') {
// heuristic priority corners/center
const priorities = [[0,0],[0,size-1],[size-1,0],[size-1,size-1],[Math.floor(size/2),Math.floor(size/2)]];
for(let p of priorities) if(!board[p[0]][p[1]]) { move = p; break; }
if(!move) for(let i=0;i<size;i++) for(let j=0;j<size;j++) if(!board[i][j]) { move = [i,j]; break; }
} else { // impossible minimax
let best = bestAIMove(board, 'O', 'X');
if(best) move = best;
}
if(move) { makeMove(move[0], move[1]); }
}
function makeMove(row,col) {
if(!appState.gameActive) return false;
if(appState.board[row][col] !== null) return false;
if(appState.timerEnabled && appState.timeLeft <= 0 && appState.currentPlayer === 'X') return false;
// record undo
const snapshot = { board: JSON.parse(JSON.stringify(appState.board)), currentPlayer: appState.currentPlayer, gameActive: appState.gameActive };
appState.undoStack.push(snapshot);
appState.board[row][col] = appState.currentPlayer;
playSound('move');
const {winner, line} = getWinnerAndLine(appState.board, appState.boardSize);
if(winner) {
appState.gameActive = false; appState.winner = winner; appState.winningLine = line;
if(winner === 'X') { appState.stats.wins++; appState.stats.xp += 25; if(!appState.dailyRewardClaimed && false){} checkAchievements(); }
else if(winner === 'O' && appState.gameMode==='single') { appState.stats.losses++; appState.stats.xp += 10; }
else if(winner === 'O' && appState.gameMode==='multi') { appState.stats.wins++; }
if(winner) { triggerWinEffect(); }
updateStatsUI(); renderGameScreen(); return true;
}
const full = isBoardFull(appState.board);
if(full) { appState.gameActive = false; appState.winner = 'draw'; appState.stats.draws++; updateStatsUI(); renderGameScreen(); return true; }
appState.currentPlayer = appState.currentPlayer === 'X' ? 'O' : 'X';
if(appState.timerEnabled) { resetTimer(); }
renderGameScreen();
if(appState.gameMode === 'single' && appState.gameActive && appState.currentPlayer === 'O') { setTimeout(() => aiMove(), 50); }
return true;
}
function undoMove() { if(appState.undoStack.length && appState.gameActive) { let prev = appState.undoStack.pop(); appState.board = prev.board; appState.currentPlayer = prev.currentPlayer; appState.gameActive = prev.gameActive; appState.winner = null; renderGameScreen(); if(appState.timerEnabled) resetTimer(); } }
function resetGame() { clearTimer(); appState.board = initBoard(appState.boardSize); appState.currentPlayer = 'X'; appState.gameActive = true; appState.winner = null; appState.winningLine = null; appState.undoStack = []; if(appState.timerEnabled) { appState.timeLeft = 12; startTimer(); } renderGameScreen(); if(appState.gameMode === 'single' && appState.currentPlayer === 'O') setTimeout(aiMove, 80); }
function resetTimer() { if(!appState.timerEnabled) return; clearTimer(); appState.timeLeft = 12; startTimer(); }
function clearTimer() { if(appState.timerInterval) { clearInterval(appState.timerInterval); appState.timerInterval = null; } }
function startTimer() { if(!appState.timerEnabled || !appState.gameActive) return; clearTimer(); appState.timerInterval = setInterval(()=>{ if(appState.gameActive && appState.timerEnabled && appState.timeLeft > 0) { appState.timeLeft--; renderGameScreen(); if(appState.timeLeft===0 && appState.gameActive) { if(appState.currentPlayer === 'X') { appState.gameActive=false; appState.winner = 'O'; if(appState.gameMode==='single') appState.stats.losses++; updateStatsUI(); renderGameScreen(); clearTimer(); } else if(appState.gameMode==='single' && appState.currentPlayer==='O') { /*ai loses*/ appState.gameActive=false; appState.winner='X'; appState.stats.wins++; updateStatsUI(); renderGameScreen(); clearTimer(); } else if(appState.gameMode==='multi') { appState.gameActive=false; appState.winner = appState.currentPlayer==='X' ? 'O' : 'X'; renderGameScreen(); } } } }, 1000); }
function triggerWinEffect() { if(window.canvasConfetti) { canvasConfetti({ particleCount: 150, spread: 70, origin: { y: 0.6 }, colors: ['#3b82f6', '#a855f7'] }); } playSound('win'); }
function updateStatsUI() { localStorage.setItem('ttt_stats', JSON.stringify(appState.stats)); document.querySelectorAll('.stats-value').forEach(el => { if(el.id==='statWins') el.innerText = appState.stats.wins; if(el.id==='statLoss') el.innerText = appState.stats.losses; if(el.id==='statDraw') el.innerText = appState.stats.draws; if(el.id==='statXP') el.innerText = `${appState.stats.xp} XP`; }); }
function checkAchievements() { if(appState.stats.wins >=1 && !appState.achievements.includes('first_win')) { appState.achievements.push('first_win'); alert('🏆 Achievement: First Blood!'); } }
// RENDER ENGINE
function renderHome() {
const root = document.getElementById('root');
root.innerHTML = `
<div class="app-container">
<div class="glow-title">✦ TIC TAC TOE ARENA ✦</div>
<div class="profile-stats-bar">
<div class="avatar-name"><div class="avatar">${appState.avatar}</div><strong>${appState.username}</strong></div>
<div class="stats"><span><i class="fas fa-trophy"></i> <span id="statWins">${appState.stats.wins}</span></span><span><i class="fas fa-skull"></i> <span id="statLoss">${appState.stats.losses}</span></span><span><i class="fas fa-hand-peace"></i> <span id="statDraw">${appState.stats.draws}</span></span><span><i class="fas fa-star"></i> <span id="statXP">${appState.stats.xp} XP</span></span></div>
</div>
<div class="menu-grid">
<div class="premium-btn" data-mode="single"><i class="fas fa-microchip"></i> ${t('sing')}</div>
<div class="premium-btn" data-mode="multi"><i class="fas fa-users"></i> ${t('multi')}</div>
<div class="premium-btn" data-mode="online"><i class="fas fa-globe"></i> ${t('online')}</div>
<div class="premium-btn" id="settingsBtn"><i class="fas fa-sliders-h"></i> ${t('settings')}</div>
<div class="premium-btn" id="dailyBtn"><i class="fas fa-gift"></i> ${t('daily')}</div>
<div class="premium-btn" id="leaderboardBtn"><i class="fas fa-chart-line"></i> ${t('leaderboard')}</div>
</div>
<div style="display:flex; gap:8px; justify-content:center; margin-top:8px;"><div class="premium-btn" data-size="3">3x3</div><div class="premium-btn" data-size="5">5x5</div><div class="premium-btn" data-size="7">7x7</div><div class="premium-btn" id="timerToggle"><i class="fas fa-hourglass-half"></i> Timer OFF</div></div>
<div style="display:flex; gap:8px; justify-content:center; margin:10px 0;"><div class="premium-btn" data-diff="easy">${t('easy')}</div><div class="premium-btn" data-diff="medium">${t('medium')}</div><div class="premium-btn" data-diff="hard">${t('hard')}</div><div class="premium-btn" data-diff="impossible">${t('impossible')}</div></div>
</div>
<div id="settingsPanel" class="settings-drawer"><h3>⚙️ SETTINGS</h3><label><input type="checkbox" id="soundToggle" ${appState.soundEnabled ? 'checked' : ''}> Sound FX</label><label><input type="checkbox" id="musicToggle" ${appState.musicEnabled ? 'checked' : ''}> Music</label><label>Language: <select id="langSelect"><option value="en">English</option><option value="ar">العربية</option></select></label><label>Theme: <select id="themeSelect"><option>Neon Dark</option></select></label><button class="premium-btn" id="closeSettings" style="margin-top:16px;">Close</button></div>
<div id="overlay" class="overlay"></div>
`;
attachHomeEvents();
updateStatsUI();
}
function attachHomeEvents() {
document.querySelectorAll('[data-mode]').forEach(btn=>btn.onclick=()=>{ let mode=btn.dataset.mode; appState.gameMode = mode==='single'?'single':mode==='multi'?'multi':'single'; appState.isMultiplayer = (mode==='multi'); resetGame(); appState.currentScreen='game'; renderGameScreen(); });
document.querySelectorAll('[data-size]').forEach(btn=>btn.onclick=()=>{ appState.boardSize = parseInt(btn.dataset.size); resetGame(); renderHome(); });
document.querySelectorAll('[data-diff]').forEach(btn=>btn.onclick=()=>{ appState.difficulty = btn.dataset.diff; renderHome(); });
document.getElementById('settingsBtn')?.onclick=()=>{ document.getElementById('settingsPanel').classList.add('open'); document.getElementById('overlay').classList.add('active'); };
document.getElementById('closeSettings')?.onclick=()=>{ document.getElementById('settingsPanel').classList.remove('open'); document.getElementById('overlay').classList.remove('active'); };
document.getElementById('soundToggle')?.onchange=(e)=>appState.soundEnabled=e.target.checked;
document.getElementById('musicToggle')?.onchange=(e)=>appState.musicEnabled=e.target.checked;
document.getElementById('langSelect')?.onchange=(e)=>{ appState.language = e.target.value; renderHome(); };
document.getElementById('timerToggle')?.onclick=()=>{ appState.timerEnabled = !appState.timerEnabled; renderHome(); };
document.getElementById('dailyBtn')?.onclick=()=>{ alert('🎁 +50 XP daily claimed!'); appState.stats.xp+=50; updateStatsUI(); renderHome(); };
document.getElementById('leaderboardBtn')?.onclick=()=>alert('🏅 Leaderboard: 1. NOVA 2. PlayerX 3. AI_Master');
}
function renderGameScreen() {
const boardSize = appState.boardSize;
const board = appState.board;
const winner = appState.winner;
const active = appState.gameActive;
const current = appState.currentPlayer;
const timerHtml = appState.timerEnabled ? `<span class="timer-warning"><i class="fas fa-hourglass-start"></i> ${appState.timeLeft}s</span>` : '';
let statusText = winner ? (winner === 'draw' ? t('drawMsg') : `🏆 ${t('winner')}: ${winner}`) : `${t('turn')}: ${current}`;
let cellsHtml = '';
const cellSize = boardSize <=3 ? '80px' : boardSize <=5 ? '70px' : '55px';
for(let i=0;i<boardSize;i++){ for(let j=0;j<boardSize;j++){ let val = board[i][j] || ''; let winClass = '';
if(appState.winningLine && ((appState.winningLine.type==='row' && appState.winningLine.index===i) || (appState.winningLine.type==='col' && appState.winningLine.index===j) || (appState.winningLine.type==='diag' && ((appState.winningLine.dir==='main' && i===j) || (appState.winningLine.dir==='anti' && i===boardSize-1-j))))) winClass='win-highlight';
cellsHtml += `<div class="cell ${val} ${winClass}" style="width:${cellSize}; height:${cellSize}; font-size:${boardSize>3?'2rem':'3rem'}" data-row="${i}" data-col="${j}">${val === 'X' ? '✖' : val === 'O' ? '◯' : ''}</div>`;
}}
const root = document.getElementById('root');
root.innerHTML = `<div class="app-container"><div class="glow-title">⚡ XO ARENA ⚡</div><div class="game-panel"><div class="board" style="grid-template-columns: repeat(${boardSize}, 1fr); gap: 10px;">${cellsHtml}</div><div class="game-info"><div class="turn-indicator">${statusText}</div><div>${timerHtml}</div><div><button class="icon-btn" id="undoBtn"><i class="fas fa-undo-alt"></i></button><button class="icon-btn" id="restartBtn"><i class="fas fa-redo-alt"></i></button><button class="icon-btn" id="homeBtn"><i class="fas fa-home"></i></button></div></div></div></div>`;
document.querySelectorAll('.cell').forEach(el=>{ el.onclick=()=>{ if(!active) return; let r=parseInt(el.dataset.row), c=parseInt(el.dataset.col); if((appState.gameMode==='single' && appState.currentPlayer==='X') || appState.gameMode==='multi') makeMove(r,c); }; });
document.getElementById('undoBtn').onclick=()=>undoMove();
document.getElementById('restartBtn').onclick=()=>{ resetGame(); };
document.getElementById('homeBtn').onclick=()=>{ clearTimer(); appState.currentScreen='home'; renderHome(); };
if(appState.gameMode === 'single' && active && current === 'O') setTimeout(aiMove, 80);
if(appState.timerEnabled && active && !winner) startTimer(); else if(!active) clearTimer();
}
function bootstrap() {
let saved = localStorage.getItem('ttt_stats');
if(saved) appState.stats = JSON.parse(saved);
appState.board = initBoard(appState.boardSize);
renderHome();
}
bootstrap();
</script>
</body>
</html>Game Source: FUTURE TIC TAC TOE | Premium XO Arena
Creator: TurboDragon72
Libraries: none
Complexity: complex (477 lines, 26.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: future-tic-tac-toe-premium-xo-arena-turbodragon72" to link back to the original. Then publish at arcadelab.ai/publish.