🎮ArcadeLab

Neon Tic-Tac-Toe

by AtomicRaven42
275 lines7.1 KB
▶ Play
<!--ARCADELAB title: Neon Tic-Tac-Toe description: A polished Tic-Tac-Toe game with a glowing neon theme, featuring local 2-player mode and an AI opponent. libraries: none emoji: ❌ color: purple -->
<!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>
  <style>
    :root {
      --bg-color: #0d1117;
      --panel-bg: #161b22;
      --text-color: #c9d1d9;
      --x-color: #ff4a5a;
      --o-color: #38bdf8;
      --accent-color: #8b5cf6;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-color);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      min-height: 100vh;
      overflow: hidden;
    }

    .container {
      text-align: center;
      background: var(--panel-bg);
      padding: 30px;
      border-radius: 16px;
      box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
      border: 1px solid #30363d;
      max-width: 400px;
      width: 100%;
    }

    h1 {
      font-size: 2rem;
      margin-bottom: 20px;
      background: linear-gradient(45deg, var(--x-color), var(--o-color));
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .controls {
      margin-bottom: 20px;
      display: flex;
      justify-content: center;
      gap: 10px;
    }

    .btn {
      background: #21262d;
      color: var(--text-color);
      border: 1px solid #30363d;
      padding: 8px 16px;
      border-radius: 6px;
      cursor: pointer;
      font-weight: 600;
      transition: all 0.2s;
    }

    .btn:hover {
      background: #30363d;
      border-color: #8b5cf6;
    }

    .btn.active {
      background: var(--accent-color);
      color: white;
      border-color: var(--accent-color);
    }

    .status {
      font-size: 1.2rem;
      margin-bottom: 20px;
      height: 30px;
      font-weight: 500;
    }

    .board {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-gap: 10px;
      margin-bottom: 20px;
    }

    .cell {
      aspect-ratio: 1;
      background: #21262d;
      border: 1px solid #30363d;
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 2.5rem;
      font-weight: bold;
      cursor: pointer;
      transition: background 0.2s, transform 0.1s;
    }

    .cell:hover {
      background: #30363d;
    }

    .cell:active {
      transform: scale(0.95);
    }

    .cell.X {
      color: var(--x-color);
      text-shadow: 0 0 10px rgba(255, 74, 90, 0.5);
    }

    .cell.O {
      color: var(--o-color);
      text-shadow: 0 0 10px rgba(56, 189, 248, 0.5);
    }

    .cell.win {
      background: #238636;
      color: white;
    }
  </style>
</head>
<body>

  <div class="container">
    <h1>Tic-Tac-Toe</h1>
    
    <div class="controls">
      <button id="pvpBtn" class="btn active" onclick="setMode('pvp')">2 Player</button>
      <button id="aiBtn" class="btn" onclick="setMode('ai')">vs AI</button>
    </div>

    <div class="status" id="statusText">X's Turn</div>

    <div class="board" id="board">
      <div class="cell" onclick="makeMove(this, 0)"></div>
      <div class="cell" onclick="makeMove(this, 1)"></div>
      <div class="cell" onclick="makeMove(this, 2)"></div>
      <div class="cell" onclick="makeMove(this, 3)"></div>
      <div class="cell" onclick="makeMove(this, 4)"></div>
      <div class="cell" onclick="makeMove(this, 5)"></div>
      <div class="cell" onclick="makeMove(this, 6)"></div>
      <div class="cell" onclick="makeMove(this, 7)"></div>
      <div class="cell" onclick="makeMove(this, 8)"></div>
    </div>

    <button class="btn" style="width: 100%; padding: 12px;" onclick="resetGame()">Reset Game</button>
  </div>

  <script>
    let board = ['', '', '', '', '', '', '', '', ''];
    let currentPlayer = 'X';
    let gameActive = true;
    let gameMode = 'pvp'; // 'pvp' or 'ai'

    const statusText = document.getElementById('statusText');
    const cells = document.querySelectorAll('.cell');

    const winPatterns = [, [3, 4, 5], [6, 7, 8], // Rows, [1, 4, 7], [2, 5, 8], // Columns, [2, 4, 6]             // Diagonals
    ];

    function setMode(mode) {
      gameMode = mode;
      document.getElementById('pvpBtn').classList.toggle('active', mode === 'pvp');
      document.getElementById('aiBtn').classList.toggle('active', mode === 'ai');
      resetGame();
    }

    function makeMove(cell, index) {
      if (board[index] !== '' || !gameActive) return;

      board[index] = currentPlayer;
      cell.innerText = currentPlayer;
      cell.classList.add(currentPlayer);

      checkResult();

      if (gameActive && gameMode === 'ai' && currentPlayer === 'O') {
        gameActive = false; // Temporarily disable player inputs during AI turn
        setTimeout(aiMove, 400);
      }
    }

    function aiMove() {
      // 1. Try to win or block
      let move = findBestMove('O') ?? findBestMove('X');

      // 2. Take center if available
      if (move === null && board[4] === '') move = 4;

      // 3. Take random empty spot
      if (move === null) {
        const emptyCells = board.map((val, idx) => val === '' ? idx : null).filter(val => val !== null);
        move = emptyCells[Math.floor(Math.random() * emptyCells.length)];
      }

      if (move !== null) {
        board[move] = 'O';
        cells[move].innerText = 'O';
        cells[move].classList.add('O');
        gameActive = true;
        checkResult();
      }
    }

    function findBestMove(player) {
      for (let pattern of winPatterns) {
        const values = pattern.map(idx => board[idx]);
        const playerCount = values.filter(v => v === player).length;
        const emptyCount = values.filter(v => v === '').length;

        if (playerCount === 2 && emptyCount === 1) {
          return pattern[values.indexOf('')];
        }
      }
      return null;
    }

    function checkResult() {
      let roundWon = false;
      let winningPattern = [];

      for (let pattern of winPatterns) {
        const [a, b, c] = pattern;
        if (board[a] && board[a] === board[b] && board[a] === board[c]) {
          roundWon = true;
          winningPattern = pattern;
          break;
        }
      }

      if (roundWon) {
        statusText.innerText = `${currentPlayer} Wins! 🎉`;
        winningPattern.forEach(idx => cells[idx].classList.add('win'));
        gameActive = false;
        return;
      }

      if (!board.includes('')) {
        statusText.innerText = "It's a Draw! 🤝";
        gameActive = false;
        return;
      }

      currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
      statusText.innerText = `${currentPlayer}'s Turn`;
    }

    function resetGame() {
      board = ['', '', '', '', '', '', '', '', ''];
      currentPlayer = 'X';
      gameActive = true;
      statusText.innerText = "X's Turn";
      cells.forEach(cell => {
        cell.innerText = '';
        cell.className = 'cell';
      });
    }
  </script>
</body>
</html>

Game Source: Neon Tic-Tac-Toe

Creator: AtomicRaven42

Libraries: none

Complexity: complex (275 lines, 7.1 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: neon-tic-tac-toe-atomicraven42" to link back to the original. Then publish at arcadelab.ai/publish.