Train snakeOne-line
DeepSeek V4 Flash 0423
by DeepSeekreasoning defaultClean
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>