Train snakeOne-line
DeepSeek V4.1 Flash 1017
by DeepSeekReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 22.6 s
- Cost
- $0.0037
- Output tokens
- 6,771
- Spent thinking
- 0 tokens
- Speed
- 308.1 tok/s
- Console errors
- 0
- First token after
- 580 ms
- First visible text after
- 580 ms
- Prompt tokens
- 362
- Code size
- 22.5 KB
- Finish reason
- stop
- Served by
- StepFun
- Model id
deepseek/deepseek-v4.1-flash-1017- Reasoning sent
{"effort":"none"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 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 · 678 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Train — Passenger Express</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
height: 100vh;
margin: 0;
background: linear-gradient(145deg, #1b2a1f 0%, #2c3e2e 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 12px;
gap: 10px;
max-width: 100%;
}
canvas {
display: block;
width: min(85vmin, 640px);
height: min(85vmin, 640px);
border-radius: 28px;
box-shadow: 0 18px 30px rgba(0, 0, 0, 0.7), 0 0 0 4px #3a2e1f, 0 0 0 8px #d9b382;
background-color: #1f2c1b;
cursor: pointer;
transition: filter 0.2s;
}
canvas:active {
filter: brightness(0.95);
}
.status-bar {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 20px 32px;
color: #f7e9c3;
text-shadow: 0 2px 4px #00000080;
font-weight: bold;
font-size: clamp(1rem, 3.5vw, 1.6rem);
letter-spacing: 0.5px;
background: #2c3e2e;
padding: 10px 28px;
border-radius: 60px;
box-shadow: inset 0 4px 8px #00000040, 0 8px 12px #00000060;
border: 1px solid #6b8f6b;
}
.status-bar span {
display: flex;
align-items: center;
gap: 8px;
}
.badge {
background: #d9b382;
color: #1e2b1e;
padding: 4px 16px;
border-radius: 30px;
font-size: 0.9em;
box-shadow: inset 0 -3px 0 #9c7e58;
text-shadow: none;
}
.overlay-message {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
z-index: 10;
}
.overlay-message div {
background: #1f2c1bdd;
backdrop-filter: blur(4px);
color: #f7e9c3;
font-size: clamp(1.4rem, 6vw, 2.6rem);
font-weight: 800;
padding: 24px 42px;
border-radius: 60px;
border: 3px solid #d9b382;
box-shadow: 0 18px 30px black;
text-align: center;
animation: pulse 1.6s infinite ease-in-out;
pointer-events: none;
white-space: nowrap;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 0.95; }
50% { transform: scale(1.04); opacity: 1; }
100% { transform: scale(1); opacity: 0.95; }
}
@media (max-width: 500px) {
.status-bar { padding: 8px 16px; gap: 12px; }
.overlay-message div { padding: 16px 24px; white-space: normal; }
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="gameCanvas" width="640" height="640"></canvas>
<div class="status-bar">
<span>🚂 <span class="badge" id="scoreDisplay">0</span></span>
<span>👥 <span class="badge" id="passengerDisplay">0</span></span>
</div>
</div>
<div class="overlay-message" id="overlay">
<div>CLICK or PRESS ANY KEY<br>TO START THE TRAIN</div>
</div>
<script>
(function() {
// ----- CONFIG -----
const GRID_SIZE = 16; // 16x16 grid
const CELL_SIZE = 640 / GRID_SIZE; // 40px
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const scoreDisplay = document.getElementById('scoreDisplay');
const passengerDisplay = document.getElementById('passengerDisplay');
// ----- GAME STATE -----
let snake = []; // array of {x, y}, head first
let direction = { dx: 1, dy: 0 }; // moving right initially
let nextDirection = { dx: 1, dy: 0 };
let passengers = []; // array of {x, y}
let score = 0;
let passengersCollected = 0;
let gameState = 'demo'; // 'demo', 'playing', 'gameover'
let gameLoopId = null;
let moveInterval = 180; // ms per move (demo & human)
let lastMoveTime = 0;
let animationFrame = null;
// ----- DEMO AI -----
let aiEnabled = true;
// ----- INIT / RESET -----
function resetGame() {
// start with a small train: 3 segments, positioned near center-left
snake = [
{ x: 5, y: 8 }, // head (locomotive)
{ x: 4, y: 8 }, // wagon 1
{ x: 3, y: 8 } // wagon 2
];
direction = { dx: 1, dy: 0 };
nextDirection = { dx: 1, dy: 0 };
score = 0;
passengersCollected = 0;
passengers = [];
spawnPassenger();
spawnPassenger(); // two passengers for better visuals
updateScoreUI();
// Ensure no immediate game over
gameState = 'demo';
aiEnabled = true;
overlay.style.display = 'flex';
}
// spawn passenger at random free cell
function spawnPassenger() {
const maxAttempts = 500;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const x = Math.floor(Math.random() * GRID_SIZE);
const y = Math.floor(Math.random() * GRID_SIZE);
if (!snake.some(seg => seg.x === x && seg.y === y) &&
!passengers.some(p => p.x === x && p.y === y)) {
passengers.push({ x, y });
return true;
}
}
return false;
}
// update score and passenger count
function updateScoreUI() {
scoreDisplay.textContent = score;
passengerDisplay.textContent = passengersCollected;
}
// ----- GAME ACTIONS -----
function moveSnake() {
if (gameState === 'gameover') return;
// apply next direction (prevent reversing)
if ((direction.dx !== -nextDirection.dx || direction.dy !== -nextDirection.dy) &&
(nextDirection.dx !== 0 || nextDirection.dy !== 0)) {
direction = { ...nextDirection };
}
const head = snake[0];
const newHead = {
x: head.x + direction.dx,
y: head.y + direction.dy
};
// wall collision
if (newHead.x < 0 || newHead.x >= GRID_SIZE || newHead.y < 0 || newHead.y >= GRID_SIZE) {
gameOver();
return;
}
// self collision (check all body segments)
if (snake.some(seg => seg.x === newHead.x && seg.y === newHead.y)) {
gameOver();
return;
}
// move: add new head
snake.unshift(newHead);
// check passenger pickup
const passengerIndex = passengers.findIndex(p => p.x === newHead.x && p.y === newHead.y);
let pickedUp = false;
if (passengerIndex !== -1) {
// remove passenger, add score, increase length (already added)
passengers.splice(passengerIndex, 1);
score += 10;
passengersCollected++;
updateScoreUI();
pickedUp = true;
// spawn a new passenger (so at least one is visible)
spawnPassenger();
// also spawn extra passenger occasionally to keep it lively
if (Math.random() < 0.5 && passengers.length < 4) {
spawnPassenger();
}
// make the train a little faster as it grows
moveInterval = Math.max(110, 190 - Math.floor(score / 20) * 8);
}
// remove tail if no pickup
if (!pickedUp) {
snake.pop();
} else {
// when picking up, we keep the tail (grow). But we also want to remove tail?
// Actually we added head, but we need to keep old tail for growth.
// So we do NOT pop. That's correct.
}
// ensure at least one passenger on field
if (passengers.length === 0) {
spawnPassenger();
}
}
function gameOver() {
if (gameState === 'gameover') return;
gameState = 'gameover';
overlay.style.display = 'flex';
overlay.querySelector('div').innerHTML = '💥 TRAIN CRASHED! 💥<br>CLICK or PRESS KEY TO RESTART';
if (gameLoopId) {
clearInterval(gameLoopId);
gameLoopId = null;
}
}
// ----- DEMO AI (simple pathfinding) -----
function updateDemoAI() {
if (gameState !== 'demo' || !aiEnabled || passengers.length === 0) return;
const head = snake[0];
// choose the closest passenger (manhattan)
let target = passengers[0];
let minDist = Infinity;
for (let p of passengers) {
const dist = Math.abs(p.x - head.x) + Math.abs(p.y - head.y);
if (dist < minDist) {
minDist = dist;
target = p;
}
}
const dx = target.x - head.x;
const dy = target.y - head.y;
// preferred directions: try to move along the axis with larger distance
let moves = [];
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0) moves.push({ dx: 1, dy: 0 });
if (dx < 0) moves.push({ dx: -1, dy: 0 });
if (dy > 0) moves.push({ dx: 0, dy: 1 });
if (dy < 0) moves.push({ dx: 0, dy: -1 });
} else {
if (dy > 0) moves.push({ dx: 0, dy: 1 });
if (dy < 0) moves.push({ dx: 0, dy: -1 });
if (dx > 0) moves.push({ dx: 1, dy: 0 });
if (dx < 0) moves.push({ dx: -1, dy: 0 });
}
// add remaining axes as fallback
const allDirs = [
{ dx: 1, dy: 0 }, { dx: -1, dy: 0 },
{ dx: 0, dy: 1 }, { dx: 0, dy: -1 }
];
for (let d of allDirs) {
if (!moves.some(m => m.dx === d.dx && m.dy === d.dy)) {
moves.push(d);
}
}
// avoid reverse direction
const safeMoves = moves.filter(m => !(m.dx === -direction.dx && m.dy === -direction.dy));
if (safeMoves.length === 0) return;
// pick first safe move that doesn't immediately crash
for (let m of safeMoves) {
const nx = head.x + m.dx;
const ny = head.y + m.dy;
if (nx < 0 || nx >= GRID_SIZE || ny < 0 || ny >= GRID_SIZE) continue;
if (snake.some(seg => seg.x === nx && seg.y === ny)) continue;
if (passengers.some(p => p.x === nx && p.y === ny)) {
// prioritize passenger move
nextDirection = m;
return;
}
}
// if no passenger directly reachable, take any not crashing move
for (let m of safeMoves) {
const nx = head.x + m.dx;
const ny = head.y + m.dy;
if (nx < 0 || nx >= GRID_SIZE || ny < 0 || ny >= GRID_SIZE) continue;
if (snake.some(seg => seg.x === nx && seg.y === ny)) continue;
nextDirection = m;
return;
}
// last resort: any direction not reversing (may crash)
nextDirection = safeMoves[0] || direction;
}
// ----- DRAWING -----
function draw() {
ctx.clearRect(0, 0, 640, 640);
// background grass / ground
ctx.fillStyle = '#2e4a2a';
ctx.fillRect(0, 0, 640, 640);
// subtle grid lines
ctx.strokeStyle = '#3f613b';
ctx.lineWidth = 1.5;
for (let i = 0; i <= GRID_SIZE; i++) {
const pos = i * CELL_SIZE;
ctx.beginPath();
ctx.moveTo(pos, 0);
ctx.lineTo(pos, 640);
ctx.strokeStyle = '#3f613b';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, pos);
ctx.lineTo(640, pos);
ctx.stroke();
}
// draw passengers (waiting on platform - represented as tiny people)
passengers.forEach(p => {
const cx = p.x * CELL_SIZE + CELL_SIZE / 2;
const cy = p.y * CELL_SIZE + CELL_SIZE / 2;
drawPassenger(cx, cy, CELL_SIZE * 0.32);
});
// draw snake (train) from tail to head for correct layering
for (let i = snake.length - 1; i >= 0; i--) {
const seg = snake[i];
const isHead = (i === 0);
drawTrainSegment(seg.x, seg.y, i, isHead);
}
// draw headlight glow on locomotive if head exists
if (snake.length > 0) {
const head = snake[0];
const hx = head.x * CELL_SIZE + CELL_SIZE / 2;
const hy = head.y * CELL_SIZE + CELL_SIZE / 2;
ctx.shadowColor = '#ffdd88';
ctx.shadowBlur = 20;
ctx.beginPath();
ctx.arc(hx, hy, CELL_SIZE * 0.15, 0, 2 * Math.PI);
ctx.fillStyle = '#fff9e6';
ctx.fill();
ctx.shadowBlur = 0;
ctx.shadowColor = 'transparent';
}
}
function drawPassenger(cx, cy, radius) {
// little person with suitcase
// body
ctx.fillStyle = '#f7d9b3';
ctx.beginPath();
ctx.ellipse(cx - radius * 0.2, cy + radius * 0.2, radius * 0.5, radius * 0.6, 0, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#3b5e8b';
ctx.beginPath();
ctx.ellipse(cx - radius * 0.2, cy + radius * 0.55, radius * 0.55, radius * 0.4, 0, 0, Math.PI * 2);
ctx.fill();
// head
ctx.fillStyle = '#f7d9b3';
ctx.beginPath();
ctx.arc(cx - radius * 0.2, cy - radius * 0.1, radius * 0.38, 0, 2 * Math.PI);
ctx.fill();
// hat
ctx.fillStyle = '#b84a4a';
ctx.beginPath();
ctx.ellipse(cx - radius * 0.2, cy - radius * 0.3, radius * 0.4, radius * 0.18, 0, 0, Math.PI * 2);
ctx.fill();
// suitcase
ctx.fillStyle = '#8b5a2b';
ctx.fillRect(cx + radius * 0.2, cy + radius * 0.1, radius * 0.5, radius * 0.4);
ctx.fillStyle = '#c49a6c';
ctx.fillRect(cx + radius * 0.3, cy + radius * 0.2, radius * 0.1, radius * 0.15);
// handle
ctx.strokeStyle = '#3d2b1a';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cx + radius * 0.3, cy + radius * 0.1);
ctx.lineTo(cx + radius * 0.3, cy - radius * 0.1);
ctx.stroke();
}
function drawTrainSegment(gridX, gridY, index, isHead) {
const x = gridX * CELL_SIZE;
const y = gridY * CELL_SIZE;
const w = CELL_SIZE;
const h = CELL_SIZE;
const padding = 3;
const radius = 8;
// colors: locomotive is dark red/black, wagons use cycle of vibrant colors
const wagonColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6', '#e67e22'];
let baseColor, topColor, windowColor, wheelColor;
if (isHead) {
baseColor = '#b33e2b';
topColor = '#7a2a1c';
windowColor = '#f9e79f';
wheelColor = '#2d1e0e';
} else {
const colIndex = (index - 1) % wagonColors.length;
baseColor = wagonColors[colIndex];
topColor = shadeColor(baseColor, -30);
windowColor = '#fef9e7';
wheelColor = '#2d1e0e';
}
// draw body with slight rounding
ctx.shadowColor = '#00000060';
ctx.shadowBlur = 8;
ctx.fillStyle = baseColor;
ctx.beginPath();
ctx.roundRect(x + padding, y + padding, w - 2 * padding, h - 2 * padding - 4, radius);
ctx.fill();
// roof line / accent
ctx.shadowBlur = 4;
ctx.fillStyle = topColor;
ctx.beginPath();
ctx.roundRect(x + padding, y + padding, w - 2 * padding, 8, radius);
ctx.fill();
// windows (two small rectangles)
ctx.shadowBlur = 0;
ctx.fillStyle = windowColor;
// window 1
ctx.beginPath();
ctx.roundRect(x + w * 0.2, y + h * 0.35, w * 0.25, h * 0.25, 4);
ctx.fill();
// window 2
ctx.beginPath();
ctx.roundRect(x + w * 0.55, y + h * 0.35, w * 0.25, h * 0.25, 4);
ctx.fill();
// wheels (two circles)
ctx.fillStyle = wheelColor;
ctx.beginPath();
ctx.arc(x + w * 0.3, y + h - 8, 5, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(x + w * 0.7, y + h - 8, 5, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = '#aaa';
ctx.beginPath();
ctx.arc(x + w * 0.3, y + h - 8, 2, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(x + w * 0.7, y + h - 8, 2, 0, 2 * Math.PI);
ctx.fill();
// locomotive extra: smokestack and headlight
if (isHead) {
ctx.fillStyle = '#3d2b1a';
ctx.beginPath();
ctx.roundRect(x + w * 0.15, y + 4, w * 0.2, 10, 3);
ctx.fill();
// smoke puff
ctx.fillStyle = '#ffffffb0';
ctx.beginPath();
ctx.arc(x + w * 0.25, y + 2, 6, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = '#ffffff60';
ctx.beginPath();
ctx.arc(x + w * 0.3, y - 4, 5, 0, 2 * Math.PI);
ctx.fill();
}
// reset shadow
ctx.shadowBlur = 0;
ctx.shadowColor = 'transparent';
}
// helper to darken/lighten
function shadeColor(color, percent) {
const num = parseInt(color.replace('#', ''), 16);
const r = Math.min(255, Math.max(0, (num >> 16) + percent));
const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + percent));
const b = Math.min(255, Math.max(0, (num & 0x0000FF) + percent));
return `rgb(${r}, ${g}, ${b})`;
}
// Canvas roundRect polyfill
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);
return this;
};
// ----- GAME LOOP (time-based) -----
function gameTick(now) {
if (!lastMoveTime) lastMoveTime = now;
const elapsed = now - lastMoveTime;
if (gameState === 'demo' || gameState === 'playing') {
if (elapsed >= moveInterval) {
lastMoveTime = now;
if (gameState === 'demo') {
updateDemoAI();
}
moveSnake();
}
}
draw();
if (gameState !== 'gameover') {
animationFrame = requestAnimationFrame(gameTick);
}
}
// ----- START / STOP / RESTART -----
function startGame() {
if (gameState === 'gameover') {
resetGame();
gameState = 'demo'; // start in demo again for safety
overlay.querySelector('div').innerHTML = 'CLICK or PRESS ANY KEY<br>TO START THE TRAIN';
}
// from demo -> playing
if (gameState === 'demo') {
gameState = 'playing';
aiEnabled = false;
overlay.style.display = 'none';
}
// reset movement timer
lastMoveTime = performance.now();
if (gameLoopId) clearInterval(gameLoopId);
if (animationFrame) cancelAnimationFrame(animationFrame);
// start loop
animationFrame = requestAnimationFrame(gameTick);
}
// restart from user interaction after game over
function handleStartInteraction() {
if (gameState === 'gameover') {
resetGame();
gameState = 'demo';
overlay.querySelector('div').innerHTML = 'CLICK or PRESS ANY KEY<br>TO START THE TRAIN';
overlay.style.display = 'flex';
lastMoveTime = 0;
if (animationFrame) cancelAnimationFrame(animationFrame);
animationFrame = requestAnimationFrame(gameTick);
} else if (gameState === 'demo') {
startGame();
}
}
// ----- EVENT LISTENERS -----
function handleKeydown(e) {
// start or restart from overlay
if (gameState === 'demo' || gameState === 'gameover') {
handleStartInteraction();
// prevent default for arrow keys to avoid scroll
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
e.preventDefault();
}
return;
}
// playing: control direction
if (gameState === 'playing') {
const key = e.key;
let newDx = 0, newDy = 0;
if (key === 'ArrowUp' || key === 'w' || key === 'W') { newDx = 0; newDy = -1; }
else if (key === 'ArrowDown' || key === 's' || key === 'S') { newDx = 0; newDy = 1; }
else if (key === 'ArrowLeft' || key === 'a' || key === 'A') { newDx = -1; newDy = 0; }
else if (key === 'ArrowRight' || key === 'd' || key === 'D') { newDx = 1; newDy = 0; }
if (newDx !== 0 || newDy !== 0) {
e.preventDefault();
// prevent reversing
if (!(direction.dx === -newDx && direction.dy === -newDy)) {
nextDirection = { dx: newDx, dy: newDy };
}
}
}
}
function handleCanvasClick() {
if (gameState === 'demo' || gameState === 'gameover') {
handleStartInteraction();
}
}
// ----- INITIALIZE -----
function init() {
resetGame();
// start demo loop immediately
lastMoveTime = performance.now();
animationFrame = requestAnimationFrame(gameTick);
// attach listeners
window.addEventListener('keydown', handleKeydown);
canvas.addEventListener('click', handleCanvasClick);
overlay.addEventListener('click', handleStartInteraction);
// Allow overlay pointer events? It's set to none but on the div? we add click on overlay itself (pointer-events none)
// So better to listen on document or make overlay pointer-events auto
// Actually the overlay has pointer-events: none, so we need to rely on canvas click or keypress.
// But the overlay is on top; we can set pointer-events: auto on the inner div?
// Instead, let's attach click on canvas and also on a full-screen listener if needed.
// We'll add a global click listener that checks if overlay is visible.
window.addEventListener('click', function(e) {
if (gameState === 'demo' || gameState === 'gameover') {
// but avoid double-trigger with canvas click
if (e.target === canvas || e.target === overlay || overlay.contains(e.target)) {
// handled by canvas/overlay
} else {
handleStartInteraction();
}
}
});
}
// start everything
init();
})();
</script>
</body>
</html>