🎮ArcadeLab

FUTURE TIC TAC TOE | Premium XO Arena

by TurboDragon72
477 lines26.4 KB
▶ Play
<!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.