<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover" />
<title>Touchscreen Snake Game with 3 Apples</title>
<style>
  /* Reset */
  *, *::before, *::after {
    box-sizing: border-box;
  }
  html, body {
    margin: 0; padding: 0;
    height: 100%;
    background: #111;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    overscroll-behavior: none;
    user-select: none;
  }
  body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
    height: 100vh;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    background: linear-gradient(45deg, #0f2027, #203a43, #2c5364);
  }
  #game {
    position: relative;
    width: 100vmin;
    height: 100vmin;
    max-width: 480px;
    max-height: 480px;
    border: 4px solid #00ff99;
    border-radius: 20px;
    background: #111;
    box-shadow: 0 0 20px #00ff99aa inset;
    touch-action: none;
  }
  canvas {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 20px;
    background: #111;
  }
  #scoreboard {
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    color: #00ff99;
    font-weight: 700;
    font-size: 1.5rem;
    text-shadow: 0 0 15px #00ff99;
    user-select: none;
    pointer-events: none;
    z-index: 10;
  }
  #scoreboard[aria-live] {
    outline: none;
  }
  #gameOverOverlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    color: #00ff99;
    font-weight: 700;
    font-size: 1.8rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    border-radius: 20px;
    z-index: 20;
    user-select: none;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  #gameOverOverlay.visible {
    visibility: visible;
    opacity: 1;
  }
  #restartBtn {
    margin-top: 20px;
    background: #00ff99;
    border: none;
    border-radius: 12px;
    padding: 14px 28px;
    color: #111;
    font-weight: 700;
    font-size: 1.4rem;
    box-shadow: 0 0 25px #00ff99;
    cursor: pointer;
    user-select: none;
    transition: background 0.2s ease, box-shadow 0.3s ease;
  }
  #restartBtn:active {
    background: #00cc77;
    box-shadow: 0 0 35px #00cc77 inset;
  }

  /* Controls container with 4 buttons spaced as requested */
  #controls {
    width: 100%;
    max-width: 480px;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    padding: 12px 10px;
    background: #0c1a20;
    border-top: 2px solid #00ff99;
    box-shadow: 0 -5px 15px #00ff9977;
    border-radius: 0 0 20px 20px;
    user-select: none;
    gap: 12px;
  }
  .btn {
    background: #00ff99;
    border: none;
    border-radius: 12px;
    padding: 18px 0;
    color: #111;
    font-weight: 700;
    font-size: 1.6rem;
    box-shadow: 0 0 20px #00ff99;
    cursor: pointer;
    user-select: none;
    transition: background 0.2s ease, box-shadow 0.3s ease;
    width: 100%;
    max-width: none;
    text-align: center;
  }
  .btn:active {
    background: #00cc77;
    box-shadow: 0 0 30px #00cc77 inset;
  }
  /* Position buttons as per requirement:
     Left button on far left (grid column 1)
     Up button second from left (grid column 2)
     Down button second from right (grid column 3)
     Right button far right (grid column 4)
  */
  button[data-dir="left"] {
    grid-column: 1 / 2;
  }
  button[data-dir="up"] {
    grid-column: 2 / 3;
  }
  button[data-dir="down"] {
    grid-column: 3 / 4;
  }
  button[data-dir="right"] {
    grid-column: 4 / 5;
  }

  @media (max-width: 400px) {
    .btn {
      font-size: 1.3rem;
      padding: 14px 0;
    }
  }
</style>
</head>
<body>

<div id="game" role="application" aria-label="Snake game" tabindex="0">
  <canvas id="canvas" width="480" height="480" aria-describedby="scoreboard"></canvas>
  <div id="scoreboard" aria-live="polite" aria-atomic="true">Score: 0</div>
  <div id="gameOverOverlay" role="alert" aria-live="assertive" aria-atomic="true">
    <div id="gameOverMessage"></div>
    <button id="restartBtn" aria-label="Restart game">Restart</button>
  </div>
</div>

<div id="controls" aria-label="Directional controls">
  <button class="btn" data-dir="left" aria-label="Move left" type="button">←</button>
  <button class="btn" data-dir="up" aria-label="Move up" type="button">↑</button>
  <button class="btn" data-dir="down" aria-label="Move down" type="button">↓</button>
  <button class="btn" data-dir="right" aria-label="Move right" type="button">→</button>
</div>

<script>
(() => {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  const scoreboard = document.getElementById('scoreboard');
  const controls = document.querySelectorAll('.btn');
  const gameOverOverlay = document.getElementById('gameOverOverlay');
  const gameOverMessage = document.getElementById('gameOverMessage');
  const restartBtn = document.getElementById('restartBtn');
  const gameContainer = document.getElementById('game');

  const gridSize = 20; // size of one square
  const tileCount = canvas.width / gridSize; // 24x24 grid

  // Game state variables
  let snake, velocity, nextVelocity, apples, score, gameOver;

  // Pre-create gradients once to improve performance
  let snakeGradient, appleGradient;

  function createGradients() {
    snakeGradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
    snakeGradient.addColorStop(0, "#00ff99");
    snakeGradient.addColorStop(1, "#00ffaa");

    appleGradient = ctx.createRadialGradient(0, 0, 5, 0, 0, 12);
    appleGradient.addColorStop(0, "#ff4444");
    appleGradient.addColorStop(1, "#990000");
  }

  // Draw rounded rect helper
  function roundedRect(x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h - r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
    ctx.fill();
  }

  // Spawn apples on empty tiles
  function spawnApples() {
    while (apples.length < 3) {
      let pos = {
        x: Math.floor(Math.random() * tileCount),
        y: Math.floor(Math.random() * tileCount)
      };
      if (isCellEmpty(pos.x, pos.y)) {
        apples.push(pos);
      }
    }
  }

  // Check if a tile is free (no snake, no apple)
  function isCellEmpty(x, y) {
    return !snake.some(s => s.x === x && s.y === y) &&
           !apples.some(a => a.x === x && a.y === y);
  }

  // Draw everything
  function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Background subtle grid lines
    ctx.strokeStyle = "#222";
    ctx.lineWidth = 1;
    for (let i = 0; i <= tileCount; i++) {
      ctx.beginPath();
      ctx.moveTo(i * gridSize, 0);
      ctx.lineTo(i * gridSize, canvas.height);
      ctx.stroke();
      ctx.beginPath();
      ctx.moveTo(0, i * gridSize);
      ctx.lineTo(canvas.width, i * gridSize);
      ctx.stroke();
    }

    // Draw apples with glow
    apples.forEach(({x, y}) => {
      const cx = x * gridSize + gridSize/2;
      const cy = y * gridSize + gridSize/2;

      ctx.save();
      ctx.translate(cx, cy);
      ctx.shadowColor = '#ff4444';
      ctx.shadowBlur = 18;
      ctx.fillStyle = appleGradient;
      ctx.beginPath();
      ctx.ellipse(0, 0, gridSize * 0.4, gridSize * 0.55, Math.PI / 6, 0, 2 * Math.PI);
      ctx.fill();

      // Apple leaf
      ctx.fillStyle = '#228822';
      ctx.beginPath();
      ctx.ellipse(0, -gridSize * 0.3, gridSize * 0.15, gridSize * 0.3, Math.PI / 4, 0, 2 * Math.PI);
      ctx.fill();

      ctx.restore();
    });

    // Draw snake with brighter glow
    ctx.fillStyle = snakeGradient;
    for (let i = 0; i < snake.length; i++) {
      const s = snake[i];
      const px = s.x * gridSize;
      const py = s.y * gridSize;

      ctx.save();
      ctx.shadowColor = '#00ffcc';
      ctx.shadowBlur = i === 0 ? 25 : 18; // brighter glow for head
      roundedRect(px + 2, py + 2, gridSize - 4, gridSize - 4, 8);
      ctx.restore();
    }
  }

  // Move snake and handle game logic
  function update() {
    if (gameOver) return;

    // Update velocity from buffered direction if valid
    if ((nextVelocity.x !== -velocity.x || nextVelocity.y !== -velocity.y) &&
        (nextVelocity.x !== velocity.x || nextVelocity.y !== velocity.y)) {
      velocity = nextVelocity;
    }

    // New head position
    const head = { x: snake[0].x + velocity.x, y: snake[0].y + velocity.y };

    // Wrap around screen edges
    if (head.x < 0) head.x = tileCount - 1;
    else if (head.x >= tileCount) head.x = 0;
    if (head.y < 0) head.y = tileCount - 1;
    else if (head.y >= tileCount) head.y = 0;

    // Check collision with self
    if (snake.some(part => part.x === head.x && part.y === head.y)) {
      endGame();
      return;
    }

    snake.unshift(head);

    // Check apple eat
    let ateApple = false;
    for (let i = 0; i < apples.length; i++) {
      if (apples[i].x === head.x && apples[i].y === head.y) {
        apples.splice(i, 1);
        score++;
        ateApple = true;
        break;
      }
    }

    if (!ateApple) {
      snake.pop();
    } else {
      spawnApples();
    }

    scoreboard.textContent = `Score: ${score}`;
  }

  // End game handler
  function endGame() {
    gameOver = true;
    gameOverMessage.textContent = `Game Over! Your score was ${score}.`;
    gameOverOverlay.classList.add('visible');
    gameContainer.focus();
  }

  // Restart game handler
  function restartGame() {
    snake = [
      { x: 12, y: 12 },
      { x: 11, y: 12 },
      { x: 10, y: 12 }
    ];
    velocity = { x: 1, y: 0 };
    nextVelocity = { x: 1, y: 0 };
    apples = [];
    score = 0;
    gameOver = false;
    scoreboard.textContent = `Score: ${score}`;
    gameOverOverlay.classList.remove('visible');
    spawnApples();
    draw();
  }

  // Touch controls: swipe detection
  let touchStartX = null;
  let touchStartY = null;
  const minSwipeDistance = 30;

  canvas.addEventListener('touchstart', e => {
    if (e.touches.length === 1) {
      const t = e.touches[0];
      touchStartX = t.clientX;
      touchStartY = t.clientY;
    }
  }, { passive: true });

  canvas.addEventListener('touchmove', e => {
    if (touchStartX === null || touchStartY === null) return;
    const t = e.touches[0];
    const dx = t.clientX - touchStartX;
    const dy = t.clientY - touchStartY;

    if (Math.abs(dx) > minSwipeDistance || Math.abs(dy) > minSwipeDistance) {
      if (Math.abs(dx) > Math.abs(dy)) {
        // Horizontal swipe
        if (dx > 0) tryChangeDirection('right');
        else tryChangeDirection('left');
      } else {
        // Vertical swipe
        if (dy > 0) tryChangeDirection('down');
        else tryChangeDirection('up');
      }
      touchStartX = null;
      touchStartY = null;
    }
  }, { passive: true });

  // Buttons for touch UI controls
  controls.forEach(btn => {
    btn.addEventListener('click', () => {
      tryChangeDirection(btn.dataset.dir);
    });
  });

  // Keyboard controls for accessibility
  gameContainer.addEventListener('keydown', e => {
    if (gameOver) return;
    switch(e.key) {
      case 'ArrowUp':
      case 'w':
      case 'W':
        tryChangeDirection('up');
        e.preventDefault();
        break;
      case 'ArrowDown':
      case 's':
      case 'S':
        tryChangeDirection('down');
        e.preventDefault();
        break;
      case 'ArrowLeft':
      case 'a':
      case 'A':
        tryChangeDirection('left');
        e.preventDefault();
        break;
      case 'ArrowRight':
      case 'd':
      case 'D':
        tryChangeDirection('right');
        e.preventDefault();
        break;
    }
  });

  // Change direction helper
  function tryChangeDirection(dir) {
    switch(dir) {
      case 'up':
        if (velocity.y === 0) nextVelocity = { x: 0, y: -1 };
        break;
      case 'down':
        if (velocity.y === 0) nextVelocity = { x: 0, y: 1 };
        break;
      case 'left':
        if (velocity.x === 0) nextVelocity = { x: -1, y: 0 };
        break;
      case 'right':
        if (velocity.x === 0) nextVelocity = { x: 1, y: 0 };
        break;
    }
  }

  // Game loop
  const fps = 10;
  let lastRender = 0;
  function gameLoop(time = 0) {
    if (!gameOver && (time - lastRender > 1000 / fps)) {
      lastRender = time;
      update();
      draw();
    }
    requestAnimationFrame(gameLoop);
  }

  // Restart button event
  restartBtn.addEventListener('click', () => {
    restartGame();
  });

  // Initialize game
  function init() {
    createGradients();
    restartGame();
    requestAnimationFrame(gameLoop);
  }

  init();
})();
</script>

</body>
</html>