Models 44

DeepSeek V4 Flash 0423

by DeepSeekreasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
29.6 s
Cost
$0.0010
Output tokens
5,268
Spent thinking
0 tokens
Speed
181.9 tok/s
Console errors
0
First token after
641 ms
First visible text after
641 ms
Prompt tokens
492
Code size
16.1 KB
Finish reason
stop
Served by
Wafer
Model id
deepseek/deepseek-v4-flash
Reasoning sent
model default
Max output
393,216 tokens
Generated
Nov 13, 2026

What it was asked

The prompt
Build a polished Snake game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A grid of at least 20×20 cells, centered on the page. - Arrow keys and WASD control the snake. Quick successive key presses must both be honored, but the snake can never reverse onto itself. - Eating food adds 10 points and grows the snake by one cell; new food never spawns on the snake. - Game over when the snake hits a wall or itself, with an overlay showing the score; Space or Enter restarts, P pauses. - Visual polish: smooth movement, a snake with a distinct head and eyes facing its direction, and a small particle burst when eating. - Display the current score and the best score of the session.
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task.

Output rules:
- Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block.
- Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task.
- Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw.
- The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar.
- Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction.
- Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session.
- Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing.
- The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message.
- The code must run with zero errors in the console of the latest Chrome.

Code run-1.html · 543 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>Snake · polished</title>
  <style>
    * {
      box-sizing: border-box;
      user-select: none;
    }
    body {
      margin: 0;
      min-height: 100vh;
      background: linear-gradient(145deg, #111418, #1f2a30);
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    }
    .game-wrapper {
      background: #1e2a2f;
      padding: 1.6rem 1.6rem 1.8rem;
      border-radius: 3.5rem;
      box-shadow: 0 20px 30px rgba(0,0,0,0.7), inset 0 1px 3px rgba(255,255,255,0.08);
      backdrop-filter: blur(2px);
    }
    .header {
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      padding: 0 0.5rem 0.8rem 0.5rem;
      color: #c8ddd0;
      font-weight: 500;
      letter-spacing: 0.3px;
      text-shadow: 0 2px 3px #0b1113;
    }
    .score-span {
      background: #0e1a1e;
      padding: 0.3rem 1.2rem;
      border-radius: 40px;
      box-shadow: inset 0 3px 6px rgba(0,0,0,0.6), 0 1px 0 #497b6b;
      font-size: 1.2rem;
      display: inline-block;
    }
    .score-label {
      opacity: 0.7;
      font-size: 0.85rem;
      margin-right: 6px;
    }
    canvas {
      display: block;
      margin: 0 auto;
      width: 560px;
      height: 560px;
      border-radius: 28px;
      background: #1d2b30;
      box-shadow: inset 0 0 0 2px #2d4148, 0 12px 20px -8px black;
      cursor: pointer;
      touch-action: none;
    }
    .status-bar {
      display: flex;
      justify-content: space-between;
      padding: 0.8rem 0.5rem 0 0.5rem;
      color: #9fb7af;
      font-size: 0.9rem;
      font-weight: 400;
    }
    .best-score {
      background: #0f1b1f;
      padding: 0.2rem 1rem;
      border-radius: 30px;
      box-shadow: inset 0 2px 5px #0a1215;
    }
    .controls-hint {
      opacity: 0.6;
      letter-spacing: 0.5px;
    }
    @media (max-width: 600px) {
      .game-wrapper { padding: 1rem; border-radius: 2rem; }
      canvas { width: 92vw; height: 92vw; max-width: 500px; max-height: 500px; }
      .header { flex-wrap: wrap; gap: 0.3rem; }
    }
  </style>
</head>
<body>
<div class="game-wrapper">
  <div class="header">
    <div class="score-span"><span class="score-label">🍎</span><span id="scoreDisplay">0</span></div>
    <div class="score-span"><span class="score-label">🏆</span><span id="bestDisplay">0</span></div>
  </div>
  <canvas id="gameCanvas" width="560" height="560"></canvas>
  <div class="status-bar">
    <span class="controls-hint">←↑↓→ / WASD</span>
    <span id="pauseIndicator">▶</span>
  </div>
</div>

<script>
  (function(){
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    const scoreSpan = document.getElementById('scoreDisplay');
    const bestSpan = document.getElementById('bestDisplay');
    const pauseIndicator = document.getElementById('pauseIndicator');

    // ---------- constants ----------
    const GRID = 20;           // 20x20
    const CELL = 28;           // pixel size (560/20)
    const TICK_BASE = 150;     // ms per frame

    // ---------- state ----------
    let snake = [];            // [{x,y}] head first
    let food = { x: 8, y: 8 };
    let direction = { x: 1, y: 0 };   // current direction
    let nextDirection = { x: 1, y: 0 };
    let score = 0;
    let bestScore = 0;
    let gameActive = false;    // true = playing, false = game over / waiting
    let gameOverFlag = false;
    let winFlag = false;       // (not used, but for safety)
    let paused = false;

    // particles
    let particles = [];

    // timing
    let lastMove = 0;
    let animationFrame = null;
    let startScreen = true;    // show "click to start"

    // key buffer: allow up to 2 buffered inputs (prevents reverse)
    let keyBuffer = [];

    // ---------- helpers ----------
    function randomInt(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function getFreeCell() {
      const occupied = new Set(snake.map(s => `${s.x},${s.y}`));
      const free = [];
      for (let i = 0; i < GRID; i++) {
        for (let j = 0; j < GRID; j++) {
          if (!occupied.has(`${i},${j}`)) free.push({ x: i, y: j });
        }
      }
      if (free.length === 0) return null;
      return free[Math.floor(Math.random() * free.length)];
    }

    function spawnFood() {
      const cell = getFreeCell();
      if (!cell) {
        // you win – but we treat as game over (full map)
        gameOverFlag = true;
        gameActive = false;
        return false;
      }
      food = cell;
      return true;
    }

    // ---------- reset / init ----------
    function resetGame() {
      snake = [
        { x: 9, y: 10 },
        { x: 8, y: 10 },
        { x: 7, y: 10 }
      ];
      direction = { x: 1, y: 0 };
      nextDirection = { x: 1, y: 0 };
      score = 0;
      gameOverFlag = false;
      gameActive = true;
      paused = false;
      startScreen = false;
      keyBuffer = [];
      particles = [];
      if (!spawnFood()) {
        // map full → rare, but reset again
        snake = [{ x: 5, y: 5 }, { x: 4, y: 5 }, { x: 3, y: 5 }];
        spawnFood();
      }
      updateScoreUI();
      pauseIndicator.textContent = '▶';
    }

    function gameOver() {
      gameActive = false;
      gameOverFlag = true;
      if (score > bestScore) {
        bestScore = score;
        bestSpan.textContent = bestScore;
      }
      pauseIndicator.textContent = '⏹';
    }

    // ---------- movement & collision ----------
    function moveSnake() {
      if (!gameActive || paused || gameOverFlag) return false;

      // consume buffered direction (at least one)
      if (keyBuffer.length > 0) {
        const newDir = keyBuffer.shift();
        // prevent reversal
        if (!(newDir.x === -direction.x && newDir.y === -direction.y)) {
          direction = { ...newDir };
        }
      }

      const head = snake[0];
      const newHead = {
        x: head.x + direction.x,
        y: head.y + direction.y
      };

      // wall collision
      if (newHead.x < 0 || newHead.x >= GRID || newHead.y < 0 || newHead.y >= GRID) {
        gameOver();
        return false;
      }

      // self collision (check against current snake body, excluding tail if not growing)
      const willEat = (newHead.x === food.x && newHead.y === food.y);
      // if will eat, tail stays; else tail will be removed – so collision check against body without last element
      const compareBody = willEat ? snake : snake.slice(0, -1);
      for (let seg of compareBody) {
        if (seg.x === newHead.x && seg.y === newHead.y) {
          gameOver();
          return false;
        }
      }

      // perform move
      snake.unshift(newHead);
      if (willEat) {
        // eat: grow (keep tail)
        score += 10;
        updateScoreUI();
        spawnParticles(food.x * CELL + CELL/2, food.y * CELL + CELL/2);
        if (!spawnFood()) {
          // board full – win condition rare; set gameover but show win style
          gameOverFlag = true;
          gameActive = false;
          return true;
        }
      } else {
        // no food: pop tail
        snake.pop();
      }

      return true;
    }

    // ---------- particles ----------
    function spawnParticles(cx, cy) {
      for (let i = 0; i < 18; i++) {
        const angle = Math.random() * 2 * Math.PI;
        const speed = 1.8 + Math.random() * 3.5;
        particles.push({
          x: cx,
          y: cy,
          vx: Math.cos(angle) * speed,
          vy: Math.sin(angle) * speed,
          life: 1.0,
          size: 4 + Math.random() * 7,
          color: `hsl(${30 + Math.random() * 30}, 85%, ${60 + 30 * Math.random()}%)`
        });
      }
    }

    function updateParticles() {
      for (let i = particles.length - 1; i >= 0; i--) {
        const p = particles[i];
        p.x += p.vx;
        p.y += p.vy;
        p.vy += 0.08;
        p.life -= 0.025;
        if (p.life <= 0 || p.x < 0 || p.x > 560 || p.y < 0 || p.y > 560) {
          particles.splice(i, 1);
        }
      }
    }

    // ---------- drawing ----------
    function draw() {
      ctx.clearRect(0, 0, 560, 560);

      // draw subtle grid
      ctx.strokeStyle = '#2c3f47';
      ctx.lineWidth = 0.7;
      for (let i = 0; i <= GRID; i++) {
        ctx.beginPath();
        ctx.moveTo(i * CELL, 0);
        ctx.lineTo(i * CELL, 560);
        ctx.strokeStyle = '#304a52';
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(0, i * CELL);
        ctx.lineTo(560, i * CELL);
        ctx.strokeStyle = '#304a52';
        ctx.stroke();
      }

      // draw food (glowing)
      ctx.shadowColor = '#ffb07c';
      ctx.shadowBlur = 14;
      ctx.fillStyle = '#ff6f3c';
      ctx.beginPath();
      ctx.arc(food.x * CELL + CELL/2, food.y * CELL + CELL/2, CELL/2 - 2, 0, 2*Math.PI);
      ctx.fill();
      ctx.shadowBlur = 0;
      // inner shine
      ctx.fillStyle = '#ff9a5e';
      ctx.beginPath();
      ctx.arc(food.x * CELL + CELL/2 - 2, food.y * CELL + CELL/2 - 2, 5, 0, 2*Math.PI);
      ctx.fill();

      // draw snake
      for (let i = 0; i < snake.length; i++) {
        const seg = snake[i];
        const x = seg.x * CELL, y = seg.y * CELL;
        const radius = CELL/2 - 1.5;

        // body gradient
        const gradient = ctx.createRadialGradient(x+6, y+6, 2, x+10, y+10, 16);
        if (i === 0) {
          gradient.addColorStop(0, '#78c2a4');
          gradient.addColorStop(0.7, '#2c7a5c');
          gradient.addColorStop(1, '#1e5f46');
        } else {
          gradient.addColorStop(0, '#5ca88c');
          gradient.addColorStop(0.8, '#2a6f56');
        }
        ctx.fillStyle = gradient;
        ctx.shadowColor = '#1a2b28';
        ctx.shadowBlur = 10;
        ctx.beginPath();
        ctx.roundRect(x+1.5, y+1.5, CELL-3, CELL-3, 8);
        ctx.fill();

        // eyes on head
        if (i === 0) {
          ctx.shadowBlur = 0;
          const eyeOff = 5;
          let ex1, ey1, ex2, ey2;
          if (direction.x === 1) { // right
            ex1 = x + 18; ey1 = y + 7;
            ex2 = x + 18; ey2 = y + 16;
          } else if (direction.x === -1) { // left
            ex1 = x + 9; ey1 = y + 7;
            ex2 = x + 9; ey2 = y + 16;
          } else if (direction.y === -1) { // up
            ex1 = x + 7; ey1 = y + 9;
            ex2 = x + 16; ey2 = y + 9;
          } else { // down
            ex1 = x + 7; ey1 = y + 18;
            ex2 = x + 16; ey2 = y + 18;
          }
          ctx.fillStyle = '#fffaec';
          ctx.beginPath();
          ctx.arc(ex1, ey1, 3.5, 0, 2*Math.PI);
          ctx.fill();
          ctx.beginPath();
          ctx.arc(ex2, ey2, 3.5, 0, 2*Math.PI);
          ctx.fill();
          ctx.fillStyle = '#0f1a1a';
          ctx.beginPath();
          ctx.arc(ex1 + (direction.x===1?1: direction.x===-1?-1:0), ey1 + (direction.y===1?1: direction.y===-1?-1:0), 1.8, 0, 2*Math.PI);
          ctx.fill();
          ctx.beginPath();
          ctx.arc(ex2 + (direction.x===1?1: direction.x===-1?-1:0), ey2 + (direction.y===1?1: direction.y===-1?-1:0), 1.8, 0, 2*Math.PI);
          ctx.fill();
        }
      }
      ctx.shadowBlur = 0;

      // particles
      for (let p of particles) {
        ctx.globalAlpha = p.life;
        ctx.fillStyle = p.color;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.size * p.life * 0.7, 0, 2*Math.PI);
        ctx.fill();
      }
      ctx.globalAlpha = 1.0;

      // overlay game over / start
      if (!gameActive && !startScreen) {
        ctx.fillStyle = 'rgba(8, 16, 18, 0.75)';
        ctx.fillRect(0, 0, 560, 560);
        ctx.fillStyle = '#ddeee0';
        ctx.font = 'bold 28px "Segoe UI", system-ui, sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('💀 GAME OVER', 280, 250);
        ctx.font = '18px sans-serif';
        ctx.fillStyle = '#b3d3c5';
        ctx.fillText(`score: ${score}  ·  press SPACE / ENTER`, 280, 320);
      } else if (startScreen) {
        ctx.fillStyle = 'rgba(10, 20, 22, 0.7)';
        ctx.fillRect(0, 0, 560, 560);
        ctx.fillStyle = '#f5fcf5';
        ctx.font = 'bold 26px "Segoe UI", sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('🐍 SNAKE', 280, 220);
        ctx.font = '20px sans-serif';
        ctx.fillStyle = '#b7d9cf';
        ctx.fillText('click or press any key', 280, 300);
        ctx.font = '16px sans-serif';
        ctx.fillStyle = '#88ada1';
        ctx.fillText('arrows / WASD  ·  P pause', 280, 360);
      } else if (paused && gameActive) {
        ctx.fillStyle = 'rgba(0,0,0,0.3)';
        ctx.fillRect(0, 0, 560, 560);
        ctx.fillStyle = '#deefe8';
        ctx.font = 'bold 30px sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('⏸ PAUSED', 280, 280);
      }
    }

    // roundRect polyfill for canvas
    if (!CanvasRenderingContext2D.prototype.roundRect) {
      CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
        if (r > w/2) r = w/2;
        if (r > h/2) r = h/2;
        this.moveTo(x + r, y);
        this.lineTo(x + w - r, y);
        this.quadraticCurveTo(x + w, y, x + w, y + r);
        this.lineTo(x + w, y + h - r);
        this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
        this.lineTo(x + r, y + h);
        this.quadraticCurveTo(x, y + h, x, y + h - r);
        this.lineTo(x, y + r);
        this.quadraticCurveTo(x, y, x + r, y);
        return this;
      };
    }

    // ---------- UI ----------
    function updateScoreUI() {
      scoreSpan.textContent = score;
      if (score > bestScore) bestScore = score;
      bestSpan.textContent = bestScore;
    }

    // ---------- game loop ----------
    function tick(now) {
      animationFrame = requestAnimationFrame(tick);

      if (gameActive && !paused && !gameOverFlag) {
        if (now - lastMove > TICK_BASE) {
          lastMove = now;
          moveSnake();
          updateParticles();
          updateScoreUI();
        } else {
          updateParticles();
        }
      } else {
        updateParticles();
      }
      draw();
    }

    // ---------- input ----------
    function handleKey(e) {
      const key = e.key;
      // restart / start
      if (key === ' ' || key === 'Space' || key === 'Enter') {
        e.preventDefault();
        if (startScreen || gameOverFlag) {
          resetGame();
          return;
        }
        if (!gameActive && gameOverFlag) resetGame();
        return;
      }
      if (key === 'p' || key === 'P') {
        if (gameActive && !gameOverFlag) {
          paused = !paused;
          pauseIndicator.textContent = paused ? '⏸' : '▶';
        }
        e.preventDefault();
        return;
      }

      // start game on any key if startScreen
      if (startScreen) {
        resetGame();
        return;
      }

      // directional keys
      let dir = null;
      if (key === 'ArrowUp' || key === 'w' || key === 'W') dir = { x: 0, y: -1 };
      else if (key === 'ArrowDown' || key === 's' || key === 'S') dir = { x: 0, y: 1 };
      else if (key === 'ArrowLeft' || key === 'a' || key === 'A') dir = { x: -1, y: 0 };
      else if (key === 'ArrowRight' || key === 'd' || key === 'D') dir = { x: 1, y: 0 };

      if (dir) {
        e.preventDefault();
        if (!gameActive || paused || gameOverFlag) return;
        // buffer at most 2 inputs
        if (keyBuffer.length < 2) {
          const last = keyBuffer.length > 0 ? keyBuffer[keyBuffer.length-1] : direction;
          if (!(dir.x === -last.x && dir.y === -last.y)) {
            keyBuffer.push(dir);
          }
        }
      }
    }

    function handleClick(e) {
      if (startScreen || gameOverFlag) {
        resetGame();
      }
    }

    // ---------- init ----------
    window.addEventListener('keydown', handleKey);
    canvas.addEventListener('click', handleClick);
    canvas.addEventListener('touchstart', (e) => {
      e.preventDefault();
      handleClick(e);
    });

    // initial best from storage? session-level, but simple: 0
    bestScore = 0;
    resetGame();
    startScreen = true;
    gameActive = false;
    lastMove = performance.now();
    animationFrame = requestAnimationFrame(tick);
  })();
</script>
</body>
</html>