Models 44
Train snakeOne-line

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
46.8 s
Cost
$0.0011
Output tokens
5,817
Spent thinking
0 tokens
Speed
125.9 tok/s
Console errors
0
First token after
560 ms
First visible text after
560 ms
Prompt tokens
361
Code size
19.4 KB
Finish reason
stop
Served by
DeepInfra
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
“make me a snake game where the snake is a little train with a locomotive and colorful wagons, picking up passengers waiting on the platform”
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 · 643 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>🚂 Train Snake – passengers & wagons</title>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      background: linear-gradient(145deg, #1b3b4f 0%, #2a5f7a 100%);
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: system-ui, 'Segoe UI', Roboto, sans-serif;
      margin: 0;
      padding: 16px;
    }
    .game-wrapper {
      background: #203d4e;
      padding: 18px 20px 20px 20px;
      border-radius: 40px 40px 30px 30px;
      box-shadow: 0 12px 24px rgba(0,0,0,0.6), inset 0 2px 4px rgba(255,255,255,0.1);
      border: 1px solid #4b7b97;
    }
    canvas {
      display: block;
      margin: 0 auto;
      background: #b7d9e7;
      background-image: 
        linear-gradient(rgba(140, 180, 200, 0.3) 1px, transparent 1px),
        linear-gradient(90deg, rgba(140, 180, 200, 0.3) 1px, transparent 1px);
      background-size: 32px 32px;
      border-radius: 20px;
      box-shadow: inset 0 0 0 2px #6d9cb0, 0 8px 16px rgba(0,0,0,0.5);
      width: 100%;
      height: auto;
      aspect-ratio: 1 / 1;
      max-width: 600px;
      image-rendering: pixelated;
      touch-action: none;
    }
    .info-panel {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 16px;
      padding: 0 8px 0 12px;
      color: #ebf5fa;
      text-shadow: 0 2px 2px #0f1e27;
      font-weight: bold;
      font-size: 1.1rem;
      gap: 16px;
      flex-wrap: wrap;
    }
    .info-panel span {
      background: #14303e;
      padding: 8px 18px;
      border-radius: 40px;
      box-shadow: inset 0 2px 5px #0b1f2a, 0 3px 0 #07161f;
      display: inline-flex;
      align-items: center;
      gap: 8px;
    }
    .info-panel span i {
      font-size: 1.2rem;
      filter: drop-shadow(0 1px 1px black);
    }
    .score-block {
      background: #152e3b;
      letter-spacing: 0.5px;
    }
    .restart-btn {
      background: #f3c35b;
      border: none;
      color: #1a2c38;
      font-weight: bold;
      font-size: 1rem;
      padding: 8px 24px;
      border-radius: 40px;
      box-shadow: 0 4px 0 #9f782e, 0 6px 12px rgba(0,0,0,0.4);
      cursor: pointer;
      transition: 0.06s linear;
      letter-spacing: 0.3px;
    }
    .restart-btn:active {
      transform: translateY(5px);
      box-shadow: 0 1px 0 #9f782e;
    }
    .msg-overlay {
      position: absolute;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(9, 25, 34, 0.65);
      backdrop-filter: blur(3px);
      border-radius: 20px;
      color: white;
      font-weight: bold;
      font-size: 1.8rem;
      text-shadow: 0 4px 8px black;
      pointer-events: none;
      transition: opacity 0.3s;
      padding: 20px;
      text-align: center;
      line-height: 1.4;
    }
    .canvas-wrapper {
      position: relative;
      border-radius: 20px;
    }
    .small-note {
      font-size: 0.9rem;
      opacity: 0.8;
      font-weight: normal;
    }
  </style>
</head>
<body>
<div class="game-wrapper">
  <div class="canvas-wrapper">
    <canvas id="gameCanvas" width="600" height="600"></canvas>
    <div id="overlayMessage" class="msg-overlay">🚂 Click or press a key to start</div>
  </div>
  <div class="info-panel">
    <span class="score-block"><span id="passengerCount">0</span> 🧳 riders</span>
    <span><span id="wagonCount">1</span> 🚃 wagons</span>
    <button class="restart-btn" id="restartButton">↺ New train</button>
  </div>
</div>
<script>
  (function() {
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    const overlay = document.getElementById('overlayMessage');
    const passengerSpan = document.getElementById('passengerCount');
    const wagonSpan = document.getElementById('wagonCount');

    // grid: 15x15 (cells 40px)
    const GRID_SIZE = 15;
    const CELL = canvas.width / GRID_SIZE; // 40

    // direction vectors
    const DIRS = {
      'UP': { x: 0, y: -1 },
      'DOWN': { x: 0, y: 1 },
      'LEFT': { x: -1, y: 0 },
      'RIGHT': { x: 1, y: 0 }
    };

    // ------ game state ------
    let snake = [];          // array of {x, y}
    let direction = { x: 1, y: 0 };  // start moving right
    let nextDirection = { x: 1, y: 0 };
    let food = { x: 7, y: 7 };
    let passengers = [];     // array of {x, y} (max 2)
    let score = 0;          // passengers collected
    let gameActive = false;
    let gameOver = false;   // wall/self collision
    let intervalId = null;
    let aiDemoInterval = null;
    let aiMode = false;     // ai plays when gameActive && !gameOver

    // colors per wagon index (locomotive index 0)
    const WAGON_COLORS = [
      '#c0392b', // locomotive red
      '#e67e22', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6',
      '#e84393', '#00b894', '#fdcb6e', '#e17055', '#6c5ce7'
    ];

    // ------ helpers ------
    function randomCell() {
      return {
        x: Math.floor(Math.random() * GRID_SIZE),
        y: Math.floor(Math.random() * GRID_SIZE)
      };
    }

    function isOnSnake(cell, includeHead = true) {
      return snake.some((s, idx) => {
        if (!includeHead && idx === 0) return false;
        return s.x === cell.x && s.y === cell.y;
      });
    }

    function isOnPassengers(cell) {
      return passengers.some(p => p.x === cell.x && p.y === cell.y);
    }

    function isOnFood(cell) {
      return food.x === cell.x && food.y === cell.y;
    }

    // spawn food at empty cell (not snake, not passenger)
    function spawnFood() {
      const free = [];
      for (let x = 0; x < GRID_SIZE; x++) {
        for (let y = 0; y < GRID_SIZE; y++) {
          if (!isOnSnake({x, y}) && !isOnPassengers({x, y})) {
            free.push({x, y});
          }
        }
      }
      if (free.length === 0) return false;
      const pos = free[Math.floor(Math.random() * free.length)];
      food = pos;
      return true;
    }

    // spawn 1 or 2 passengers (max 6)
    function spawnPassengers(forceCount = 2) {
      // remove existing passengers that are on snake
      passengers = passengers.filter(p => !isOnSnake(p));

      const maxPassengers = 6;
      const target = Math.min(forceCount, maxPassengers - passengers.length);
      for (let i = 0; i < target; i++) {
        const free = [];
        for (let x = 0; x < GRID_SIZE; x++) {
          for (let y = 0; y < GRID_SIZE; y++) {
            if (!isOnSnake({x, y}) && !isOnPassengers({x, y}) && !(food.x === x && food.y === y)) {
              free.push({x, y});
            }
          }
        }
        if (free.length === 0) break;
        const pos = free[Math.floor(Math.random() * free.length)];
        passengers.push(pos);
      }
    }

    // reset full game
    function resetGame() {
      // kill any intervals
      if (intervalId) { clearInterval(intervalId); intervalId = null; }
      if (aiDemoInterval) { clearInterval(aiDemoInterval); aiDemoInterval = null; }

      // initial snake: 3 cells, head at (7,7)
      snake = [
        { x: 7, y: 7 },
        { x: 6, y: 7 },
        { x: 5, y: 7 }
      ];
      direction = { x: 1, y: 0 };
      nextDirection = { x: 1, y: 0 };
      score = 0;
      gameOver = false;
      gameActive = false;
      aiMode = false;
      passengers = [];
      // place food away from snake
      while (true) {
        const f = randomCell();
        if (!isOnSnake(f)) {
          food = f;
          break;
        }
      }
      spawnPassengers(2);
      updateUI();
      draw();
      overlay.style.opacity = '1';
      overlay.textContent = '🚂 Click or press a key to start';
    }

    // game tick (move snake)
    function tick() {
      if (!gameActive || gameOver) return;
      if (snake.length === 0) { gameActive = false; return; }

      // apply direction (no reverse)
      if ((nextDirection.x !== 0 || nextDirection.y !== 0) &&
          (direction.x !== -nextDirection.x || direction.y !== -nextDirection.y)) {
        direction = { x: nextDirection.x, y: nextDirection.y };
      }

      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_SIZE || newHead.y < 0 || newHead.y >= GRID_SIZE) {
        gameOver = true;
        gameActive = false;
        stopAI();
        overlay.style.opacity = '1';
        overlay.textContent = '💥 Train crashed! Click to restart';
        draw();
        return;
      }

      // self collision (skip tail if no eat? we remove tail after, but check after)
      // we remove tail AFTER checking, but we need to check body (except current tail if not eating)
      // classic snake: if newHead collides with snake body (except the last segment if we will remove it)
      const willEat = (food.x === newHead.x && food.y === newHead.y);
      const willPickup = passengers.some(p => p.x === newHead.x && p.y === newHead.y);
      let snakeWithoutTail = snake;
      if (!willEat && !willPickup) {
        // tail will be removed
        snakeWithoutTail = snake.slice(0, -1);
      }
      if (snakeWithoutTail.some(s => s.x === newHead.x && s.y === newHead.y)) {
        gameOver = true;
        gameActive = false;
        stopAI();
        overlay.style.opacity = '1';
        overlay.textContent = '💥 Train derailed! Click to restart';
        draw();
        return;
      }

      // move: add new head
      snake.unshift(newHead);

      // check food
      if (willEat) {
        // grow (keep tail)
        score += 1;
        if (!spawnFood()) {
          // no free cells -> win
          gameOver = false;
          gameActive = false;
          stopAI();
          overlay.style.opacity = '1';
          overlay.textContent = '🎉 All aboard! You win!';
          draw();
          updateUI();
          return;
        }
        // spawn extra passenger sometimes
        if (Math.random() < 0.6) spawnPassengers(1);
      } else {
        // remove tail
        snake.pop();
      }

      // check passenger pickup
      const pickedUp = passengers.filter(p => p.x === newHead.x && p.y === newHead.y);
      if (pickedUp.length > 0) {
        passengers = passengers.filter(p => !(p.x === newHead.x && p.y === newHead.y));
        score += pickedUp.length;
        // grow: add extra segments (one per passenger)
        for (let i = 0; i < pickedUp.length; i++) {
          // duplicate tail (grow)
          const tail = snake[snake.length - 1];
          snake.push({ x: tail.x, y: tail.y });
        }
        // refill passengers up to ~4
        if (passengers.length < 3) spawnPassengers(2);
      }

      // if too few passengers, spawn more
      if (passengers.length < 2 && !gameOver) spawnPassengers(1);

      updateUI();
      draw();
    }

    // demo AI: random direction changes (avoid walls a bit)
    function aiStep() {
      if (!gameActive || gameOver || !aiMode) return;

      const head = snake[0];
      const possible = [];
      for (const dir of ['UP', 'DOWN', 'LEFT', 'RIGHT']) {
        const d = DIRS[dir];
        const nx = head.x + d.x;
        const ny = head.y + d.y;
        // avoid reverse
        if (direction.x === -d.x && direction.y === -d.y) continue;
        // wall check
        if (nx < 0 || nx >= GRID_SIZE || ny < 0 || ny >= GRID_SIZE) continue;
        // body check (excluding tail if will move)
        const willEat = (food.x === nx && food.y === ny);
        const willPickup = passengers.some(p => p.x === nx && p.y === ny);
        let bodyCheck = snake;
        if (!willEat && !willPickup) bodyCheck = snake.slice(0, -1);
        if (bodyCheck.some(s => s.x === nx && s.y === ny)) continue;
        possible.push(d);
      }

      if (possible.length > 0) {
        // prefer direction towards food with 40% chance, else random
        let chosen = null;
        if (Math.random() < 0.5) {
          // find direction that minimizes distance to food
          let bestDist = Infinity;
          for (const d of possible) {
            const nx = head.x + d.x;
            const ny = head.y + d.y;
            const dist = Math.abs(nx - food.x) + Math.abs(ny - food.y);
            if (dist < bestDist) {
              bestDist = dist;
              chosen = d;
            }
          }
        }
        if (!chosen) chosen = possible[Math.floor(Math.random() * possible.length)];
        nextDirection = chosen;
      }
    }

    function stopAI() {
      if (aiDemoInterval) {
        clearInterval(aiDemoInterval);
        aiDemoInterval = null;
      }
      aiMode = false;
    }

    function startGame() {
      if (gameActive) return;
      if (gameOver) {
        resetGame();
        // will show overlay again, but start after click
        overlay.style.opacity = '1';
        overlay.textContent = '🚂 Click or press a key to start';
        return;
      }
      // start fresh if not active
      resetGame();
      gameActive = true;
      aiMode = true;
      overlay.style.opacity = '0';
      // start tick interval
      if (intervalId) clearInterval(intervalId);
      intervalId = setInterval(tick, 180);
      // start AI demo
      if (aiDemoInterval) clearInterval(aiDemoInterval);
      aiDemoInterval = setInterval(aiStep, 200);
      draw();
    }

    // draw everything
    function draw() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // grid lines (already drawn as background via CSS, but we draw tracks)
      for (let i = 0; i <= GRID_SIZE; i++) {
        ctx.strokeStyle = '#8bb5c7';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(i * CELL, 0);
        ctx.lineTo(i * CELL, canvas.height);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(0, i * CELL);
        ctx.lineTo(canvas.width, i * CELL);
        ctx.stroke();
      }
      // draw rails
      for (let i = 0; i < GRID_SIZE; i++) {
        for (let j = 0; j < GRID_SIZE; j++) {
          ctx.fillStyle = '#94bdd0';
          ctx.fillRect(i * CELL + 2, j * CELL + 2, 2, 2);
        }
      }

      // draw passengers (waiting on platform)
      for (const p of passengers) {
        const x = p.x * CELL, y = p.y * CELL;
        ctx.fillStyle = '#f5e56b';
        ctx.shadowColor = '#00000040';
        ctx.shadowBlur = 6;
        ctx.beginPath();
        ctx.arc(x + CELL/2, y + CELL/2, 12, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;
        ctx.fillStyle = '#2f2f2f';
        ctx.font = 'bold 16px sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('🚶', x + CELL/2, y + CELL/2 - 1);
      }

      // draw food (ticket / coal?)
      ctx.fillStyle = '#d35400';
      ctx.shadowBlur = 8;
      ctx.shadowColor = '#b85f1a';
      ctx.beginPath();
      ctx.arc(food.x * CELL + CELL/2, food.y * CELL + CELL/2, 10, 0, 2 * Math.PI);
      ctx.fill();
      ctx.fillStyle = '#f39c12';
      ctx.font = 'bold 18px sans-serif';
      ctx.shadowBlur = 0;
      ctx.fillText('⚡', food.x * CELL + CELL/2, food.y * CELL + CELL/2 + 2);

      // draw snake (train) with wagons
      for (let i = 0; i < snake.length; i++) {
        const seg = snake[i];
        const x = seg.x * CELL + 2, y = seg.y * CELL + 2;
        const w = CELL - 4, h = CELL - 4;
        const colorIdx = Math.min(i, WAGON_COLORS.length - 1);
        const isLocomotive = i === 0;

        // wagon body
        ctx.fillStyle = WAGON_COLORS[colorIdx];
        ctx.shadowBlur = 6;
        ctx.shadowColor = '#1f3a47';
        ctx.beginPath();
        ctx.roundRect(x, y, w, h, 6);
        ctx.fill();

        // locomotive chimney / cabin
        if (isLocomotive) {
          ctx.fillStyle = '#2c3e50';
          ctx.beginPath();
          ctx.arc(x + w/2, y - 2, 8, 0, Math.PI, true);
          ctx.fill();
          ctx.fillStyle = '#f1c40f';
          ctx.beginPath();
          ctx.arc(x + w/2, y - 2, 4, 0, Math.PI, true);
          ctx.fill();
          // windows
          ctx.fillStyle = '#b0d0e0';
          ctx.shadowBlur = 0;
          ctx.fillRect(x + 4, y + 6, 8, 8);
          ctx.fillRect(x + w - 12, y + 6, 8, 8);
        } else {
          // windows on wagons
          ctx.fillStyle = '#d4e6f1';
          ctx.shadowBlur = 0;
          ctx.fillRect(x + 6, y + 6, 10, 10);
          ctx.fillRect(x + w - 16, y + 6, 10, 10);
          // connector lines
          ctx.fillStyle = '#4a5b66';
          ctx.fillRect(x - 2, y + h/2 - 2, 6, 4);
        }
        // wheels
        ctx.fillStyle = '#1f1f1f';
        ctx.shadowBlur = 0;
        ctx.beginPath();
        ctx.arc(x + 7, y + h - 4, 5, 0, 2 * Math.PI);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(x + w - 7, y + h - 4, 5, 0, 2 * Math.PI);
        ctx.fill();
      }

      // game over overlay visual
      if (gameOver) {
        ctx.fillStyle = '#00000060';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
    }

    // roundRect utility
    CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
      if (w < 2 * r) r = w / 2;
      if (h < 2 * r) 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);
      this.closePath();
      return this;
    };

    function updateUI() {
      passengerSpan.textContent = score;
      const wagons = snake.length;
      wagonSpan.textContent = wagons;
    }

    // keyboard
    function handleKey(e) {
      const key = e.key;
      if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)) {
        e.preventDefault();
        if (!gameActive && !gameOver) {
          startGame();
          return;
        }
        if (!gameActive) return;
        const map = {
          'ArrowUp': 'UP',
          'ArrowDown': 'DOWN',
          'ArrowLeft': 'LEFT',
          'ArrowRight': 'RIGHT'
        };
        const nd = DIRS[map[key]];
        if (nd && (direction.x !== -nd.x || direction.y !== -nd.y)) {
          nextDirection = nd;
          // disable ai mode when player moves
          if (aiMode) {
            aiMode = false;
            if (aiDemoInterval) {
              clearInterval(aiDemoInterval);
              aiDemoInterval = null;
            }
          }
        }
      } else if (key === ' ' || key === 'Enter') {
        e.preventDefault();
        if (!gameActive && !gameOver) startGame();
        else if (gameOver) resetGame();
      }
    }

    // ----- events -----
    document.addEventListener('keydown', handleKey);

    canvas.addEventListener('click', () => {
      if (!gameActive && !gameOver) {
        startGame();
      } else if (gameOver) {
        resetGame();
        // after reset, show overlay
        overlay.style.opacity = '1';
        overlay.textContent = '🚂 Click or press a key to start';
      }
    });

    document.getElementById('restartButton').addEventListener('click', (e) => {
      e.stopPropagation();
      resetGame();
      overlay.style.opacity = '1';
      overlay.textContent = '🚂 Click or press a key to start';
      if (intervalId) { clearInterval(intervalId); intervalId = null; }
      if (aiDemoInterval) { clearInterval(aiDemoInterval); aiDemoInterval = null; }
      aiMode = false;
      gameActive = false;
      draw();
    });

    // init
    resetGame();
    draw();
  })();
</script>
</body>
</html>