Train snakeOne-line
Gemini 3.8 Flash
by GoogleReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 36.7 s
- Cost
- $0.027
- Output tokens
- 7,007
- Spent thinking
- 0 tokens
- Speed
- 197.3 tok/s
- Console errors
- 0
- First token after
- 1.2 s
- First visible text after
- 1.2 s
- Prompt tokens
- 367
- Code size
- 17.2 KB
- Finish reason
- stop
- Served by
- Model id
google/gemini-3.8-flash- Reasoning sent
{"effort":"low"}- Max output
- 65,536 tokens
- Generated
- Nov 12, 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 · 703 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Charming Train Snake</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
}
body {
background: radial-gradient(circle at center, #2e4438 0%, #17261e 100%);
color: #fff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 20px 50px rgba(0,0,0,0.6);
border-radius: 16px;
background: #3c5a48;
padding: 12px;
border: 4px solid #5a3d28;
}
header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 16px 12px 16px;
font-weight: bold;
font-size: 1.1rem;
text-shadow: 1px 1px 3px rgba(0,0,0,0.5);
}
.stat-badge {
background: rgba(0,0,0,0.3);
padding: 6px 14px;
border-radius: 20px;
border: 1px solid rgba(255,255,255,0.15);
display: flex;
align-items: center;
gap: 8px;
}
canvas {
background-color: #72a34b;
border-radius: 8px;
box-shadow: inset 0 0 20px rgba(0,0,0,0.25);
display: block;
cursor: pointer;
}
#overlay {
position: absolute;
top: 60px;
left: 12px;
right: 12px;
bottom: 12px;
background: rgba(18, 30, 24, 0.75);
backdrop-filter: blur(4px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 8px;
pointer-events: none;
transition: opacity 0.3s;
}
.banner {
background: #e67e22;
color: #fff;
padding: 10px 24px;
border-radius: 30px;
font-size: 1.25rem;
font-weight: 800;
letter-spacing: 1px;
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
text-transform: uppercase;
animation: bounce 1.2s infinite alternate;
}
.sub-banner {
margin-top: 10px;
font-size: 0.95rem;
color: #f1c40f;
font-weight: 600;
}
@keyframes bounce {
0% { transform: translateY(0) scale(1); }
100% { transform: translateY(-8px) scale(1.03); }
}
.hidden {
opacity: 0 !important;
}
#controls-hint {
margin-top: 10px;
font-size: 0.85rem;
color: #a3c2a0;
display: flex;
gap: 15px;
}
.key {
background: rgba(255,255,255,0.15);
padding: 2px 6px;
border-radius: 4px;
border: 1px solid rgba(255,255,255,0.3);
}
</style>
</head>
<body>
<div id="game-container">
<header>
<div class="stat-badge">🚂 Passengers: <span id="score-val" style="color:#f1c40f;">0</span></div>
<div style="font-size:1.3rem; letter-spacing: 2px; color: #ffeb3b; text-shadow: 2px 2px #d35400;">VALLEY EXPRESS</div>
<div class="stat-badge">⏱️ Status: <span id="status-val" style="color:#2ecc71;">AUTOPILOT</span></div>
</header>
<canvas id="gameCanvas" width="800" height="520"></canvas>
<div id="overlay">
<div class="banner" id="banner-text">AUTOPILOT DEMO</div>
<div class="sub-banner">CLICK CANVAS OR PRESS ANY ARROW KEY TO DRIVE</div>
</div>
<div id="controls-hint">
<span>Steer: <span class="key">▲</span> <span class="key">▼</span> <span class="key">◀</span> <span class="key">▶</span> or <span class="key">W</span><span class="key">A</span><span class="key">S</span><span class="key">D</span></span>
<span>•</span>
<span>Pick up waiting travelers at stations!</span>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score-val');
const statusEl = document.getElementById('status-val');
const overlayEl = document.getElementById('overlay');
const bannerText = document.getElementById('banner-text');
const GRID_SIZE = 40;
const COLS = canvas.width / GRID_SIZE; // 20
const ROWS = canvas.height / GRID_SIZE; // 13
// Wagon colors cycle
const WAGON_COLORS = ['#e74c3c', '#3498db', '#f1c40f', '#9b59b6', '#e67e22', '#1abc9c', '#e84393'];
// State
let train = [];
let dir = { x: 1, y: 0 };
let nextDir = { x: 1, y: 0 };
let passenger = null;
let station = null;
let trees = [];
let particles = [];
let score = 0;
let isDemo = true;
let gameOver = false;
let lastTick = 0;
const TICK_INTERVAL = 145; // ms per grid move
// Generate static decorative scenery (trees, bushes)
function generateScenery() {
trees = [];
for (let i = 0; i < 22; i++) {
trees.push({
x: Math.random() * (canvas.width - 40) + 20,
y: Math.random() * (canvas.height - 40) + 20,
size: 10 + Math.random() * 8,
type: Math.random() > 0.4 ? 'pine' : 'oak',
shade: Math.random() > 0.5 ? '#24592b' : '#1e4823'
});
}
}
function spawnPassenger() {
let valid = false;
let pX, pY;
while (!valid) {
pX = Math.floor(Math.random() * (COLS - 2)) + 1;
pY = Math.floor(Math.random() * (ROWS - 2)) + 1;
valid = !train.some(seg => seg.x === pX && seg.y === pY);
}
// Passenger character palette
const shirtColors = ['#e74c3c', '#3498db', '#f39c12', '#9b59b6', '#1abc9c', '#d35400'];
const hairColors = ['#f1c40f', '#8e44ad', '#2c3e50', '#d35400', '#7f8c8d'];
passenger = {
x: pX,
y: pY,
shirt: shirtColors[Math.floor(Math.random() * shirtColors.length)],
hair: hairColors[Math.floor(Math.random() * hairColors.length)],
bobble: 0
};
station = {
x: pX,
y: pY
};
}
function resetGame() {
train = [
{ x: 5, y: 6 },
{ x: 4, y: 6 },
{ x: 3, y: 6 }
];
dir = { x: 1, y: 0 };
nextDir = { x: 1, y: 0 };
score = 0;
scoreEl.textContent = score;
gameOver = false;
spawnPassenger();
}
// Basic Pathfinding / Autopilot for Demo Mode
function autoPilotMove() {
const head = train[0];
const target = passenger;
const choices = [
{ x: 0, y: -1 }, // Up
{ x: 0, y: 1 }, // Down
{ x: -1, y: 0 }, // Left
{ x: 1, y: 0 } // Right
];
// Disallow 180 flip
const validChoices = choices.filter(c => !(c.x === -dir.x && c.y === -dir.y));
// Score candidate moves (safe from wall and self)
let best = null;
let minDistance = Infinity;
for (const c of validChoices) {
const nx = head.x + c.x;
const ny = head.y + c.y;
// Check bounds
if (nx < 0 || nx >= COLS || ny < 0 || ny >= ROWS) continue;
// Check self-collision (exclude tail segment since it will advance)
let collision = false;
for (let i = 0; i < train.length - 1; i++) {
if (train[i].x === nx && train[i].y === ny) {
collision = true;
break;
}
}
if (collision) continue;
// Manhattan distance
const dist = Math.abs(nx - target.x) + Math.abs(ny - target.y);
if (dist < minDistance) {
minDistance = dist;
best = c;
}
}
if (best) {
nextDir = best;
} else {
// If trapped, pick any legal step
for (const c of validChoices) {
const nx = head.x + c.x;
const ny = head.y + c.y;
if (nx >= 0 && nx < COLS && ny >= 0 && ny < ROWS) {
nextDir = c;
break;
}
}
}
}
function update() {
if (gameOver) return;
if (isDemo) {
autoPilotMove();
}
dir = nextDir;
const head = { x: train[0].x + dir.x, y: train[0].y + dir.y };
// Wall collisions
if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) {
triggerGameOver();
return;
}
// Self collisions
for (let i = 0; i < train.length; i++) {
if (train[i].x === head.x && train[i].y === head.y) {
triggerGameOver();
return;
}
}
train.unshift(head);
// Smoke puffed by locomotive
createSmoke(head.x * GRID_SIZE + GRID_SIZE/2, head.y * GRID_SIZE + GRID_SIZE/2);
// Passenger picked up
if (head.x === passenger.x && head.y === passenger.y) {
score++;
scoreEl.textContent = score;
createConfetti(passenger.x * GRID_SIZE + GRID_SIZE/2, passenger.y * GRID_SIZE + GRID_SIZE/2);
spawnPassenger();
} else {
train.pop();
}
}
function triggerGameOver() {
gameOver = true;
if (isDemo) {
resetGame();
} else {
bannerText.textContent = "GAME OVER";
overlayEl.querySelector('.sub-banner').textContent = "PRESS ANY ARROW OR CLICK TO RESTART";
overlayEl.classList.remove('hidden');
statusEl.textContent = "DERAILED";
statusEl.style.color = "#e74c3c";
}
}
function createSmoke(x, y) {
for (let i = 0; i < 2; i++) {
particles.push({
type: 'smoke',
x: x + (Math.random() - 0.5) * 8,
y: y + (Math.random() - 0.5) * 8,
vx: (Math.random() - 0.5) * 0.8 - dir.x * 1.2,
vy: (Math.random() - 0.5) * 0.8 - dir.y * 1.2 - 0.5,
size: 5 + Math.random() * 4,
alpha: 0.7,
growth: 0.15
});
}
}
function createConfetti(x, y) {
const colors = ['#f1c40f', '#e74c3c', '#3498db', '#2ecc71', '#e67e22'];
for (let i = 0; i < 20; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 1.5 + Math.random() * 3.5;
particles.push({
type: 'confetti',
x: x,
y: y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: 4 + Math.random() * 3,
color: colors[Math.floor(Math.random() * colors.length)],
alpha: 1,
life: 25 + Math.random() * 10
});
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
if (p.type === 'smoke') {
p.size += p.growth;
p.alpha -= 0.025;
if (p.alpha <= 0) particles.splice(i, 1);
} else if (p.type === 'confetti') {
p.vy += 0.08; // gravity
p.alpha -= 0.03;
if (p.alpha <= 0) particles.splice(i, 1);
}
}
}
// Draw Railway Track Tiles
function drawTracks() {
ctx.strokeStyle = '#5a7a3b';
ctx.lineWidth = 1;
// Subtle grid tracks
for (let x = 0; x < canvas.width; x += GRID_SIZE) {
for (let y = 0; y < canvas.height; y += GRID_SIZE) {
// Grass patch dots
ctx.fillStyle = '#689643';
ctx.fillRect(x + 19, y + 19, 2, 2);
}
}
}
// Draw Scenery (trees)
function drawScenery() {
for (const tree of trees) {
ctx.save();
ctx.fillStyle = 'rgba(0,0,0,0.15)';
ctx.beginPath();
ctx.arc(tree.x + 3, tree.y + 4, tree.size, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = tree.shade;
ctx.beginPath();
ctx.arc(tree.x, tree.y, tree.size, 0, Math.PI * 2);
ctx.fill();
// Leaf highlight
ctx.fillStyle = '#488c3a';
ctx.beginPath();
ctx.arc(tree.x - tree.size * 0.3, tree.y - tree.size * 0.3, tree.size * 0.45, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// Draw Platform & Passenger
function drawStationAndPassenger(time) {
if (!passenger) return;
const px = passenger.x * GRID_SIZE;
const py = passenger.y * GRID_SIZE;
// Wooden Station Platform
ctx.save();
ctx.fillStyle = 'rgba(0,0,0,0.2)';
ctx.fillRect(px + 2, py + 2, GRID_SIZE - 2, GRID_SIZE - 2);
ctx.fillStyle = '#c2a649';
ctx.fillRect(px + 4, py + 4, GRID_SIZE - 8, GRID_SIZE - 8);
// Platform planks
ctx.strokeStyle = '#8d7328';
ctx.lineWidth = 1.5;
for (let i = 8; i < GRID_SIZE - 4; i += 7) {
ctx.beginPath();
ctx.moveTo(px + 4, py + i);
ctx.lineTo(px + GRID_SIZE - 4, py + i);
ctx.stroke();
}
// Station roof sign / Lantern post
ctx.fillStyle = '#e67e22';
ctx.fillRect(px + 5, py + 2, 8, 4);
// Passenger Bobbing
const bob = Math.sin(time * 0.008) * 2;
const cx = px + GRID_SIZE / 2;
const cy = py + GRID_SIZE / 2 + bob;
// Luggage
ctx.fillStyle = '#8e44ad';
ctx.fillRect(cx + 6, cy + 2, 7, 6);
ctx.fillStyle = '#f39c12';
ctx.fillRect(cx + 8, cy + 1, 3, 2);
// Shadow
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.beginPath();
ctx.ellipse(cx, py + GRID_SIZE - 8, 6, 3, 0, 0, Math.PI*2);
ctx.fill();
// Torso / Shirt
ctx.fillStyle = passenger.shirt;
ctx.beginPath();
ctx.arc(cx, cy + 2, 5, 0, Math.PI * 2);
ctx.fill();
// Head
ctx.fillStyle = '#ffd1a4';
ctx.beginPath();
ctx.arc(cx, cy - 4, 4.5, 0, Math.PI * 2);
ctx.fill();
// Hair
ctx.fillStyle = passenger.hair;
ctx.beginPath();
ctx.arc(cx, cy - 6, 4.2, Math.PI, Math.PI * 2);
ctx.fill();
// Waiting waving arm
ctx.strokeStyle = '#ffd1a4';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cx - 4, cy);
ctx.lineTo(cx - 7, cy - 5 + Math.sin(time * 0.015) * 3);
ctx.stroke();
ctx.restore();
}
// Draw Realistic Charming Train
function drawTrain() {
for (let i = train.length - 1; i >= 0; i--) {
const seg = train[i];
const x = seg.x * GRID_SIZE;
const y = seg.y * GRID_SIZE;
const cx = x + GRID_SIZE / 2;
const cy = y + GRID_SIZE / 2;
// Draw rails underneath the train
ctx.save();
ctx.strokeStyle = '#4a3b32';
ctx.lineWidth = 2;
ctx.strokeRect(x + 4, y + 4, GRID_SIZE - 8, GRID_SIZE - 8);
ctx.restore();
if (i === 0) {
// LOCOMOTIVE
ctx.save();
ctx.translate(cx, cy);
// Angle based on dir
let angle = 0;
if (dir.x === 1) angle = 0;
else if (dir.x === -1) angle = Math.PI;
else if (dir.y === 1) angle = Math.PI / 2;
else if (dir.y === -1) angle = -Math.PI / 2;
ctx.rotate(angle);
// Shadow
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.fillRect(-14, -10, 32, 24);
// Train Body
ctx.fillStyle = '#2c3e50'; // Dark iron engine
ctx.beginPath();
ctx.roundRect(-16, -12, 32, 24, [4, 8, 8, 4]);
ctx.fill();
// Boiler colored plate
ctx.fillStyle = '#c0392b';
ctx.fillRect(-6, -10, 18, 20);
// Cab window
ctx.fillStyle = '#81ecec';
ctx.fillRect(-14, -8, 6, 16);
// Golden Headlight
ctx.fillStyle = '#f1c40f';
ctx.beginPath();
ctx.arc(16, 0, 4, -Math.PI / 2, Math.PI / 2);
ctx.fill();
// Funnel / Chimney
ctx.fillStyle = '#111';
ctx.beginPath();
ctx.arc(6, 0, 4.5, 0, Math.PI * 2);
ctx.fill();
// Golden Rim
ctx.strokeStyle = '#f39c12';
ctx.lineWidth = 1.5;
ctx.strokeRect(-6, -10, 18, 20);
ctx.restore();
} else {
// WAGONS
ctx.save();
ctx.translate(cx, cy);
// Orientation relative to predecessor segment
const prev = train[i - 1];
const dx = prev.x - seg.x;
const dy = prev.y - seg.y;
let angle = Math.atan2(dy, dx);
ctx.rotate(angle);
// Coupler link
ctx.strokeStyle = '#2d3436';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(12, 0);
ctx.lineTo(20, 0);
ctx.stroke();
// Shadow
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(-12, -9, 26, 22);
// Wagon Body
const wagonColor = WAGON_COLORS[(i - 1) % WAGON_COLORS.length];
ctx.fillStyle = wagonColor;
ctx.beginPath();
ctx.roundRect(-13, -11, 26, 22, 4);
ctx.fill();
// Roof panel
ctx.fillStyle = 'rgba(255,255,255,0.25)';
ctx.fillRect(-8, -8, 16, 16);
// Passenger silhouettes inside windows
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
ctx.beginPath();
ctx.arc(-2, -4, 2.5, 0, Math.PI * 2);
ctx.arc(4, -4, 2.5, 0, Math.PI * 2);
ctx.arc(-2, 4, 2.5, 0, Math.PI * 2);
ctx.arc(4, 4, 2.5, 0, Math.PI * 2);
ctx.fill();
// Wheels
ctx.fillStyle = '#1e272c';
ctx.fillRect(-11, -12, 5, 2);
ctx.fillRect(6, -12, 5, 2);
ctx.fillRect(-11, 10, 5, 2);
ctx.fillRect(6, 10, 5, 2);
ctx.restore();
}
}
}
function drawParticles() {
for (const p of particles) {
ctx.save();
if (p.type === 'smoke') {
ctx.fillStyle = `rgba(240, 240, 240, ${p.alpha})`;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
} else if (p.type === 'confetti') {
ctx.fillStyle = p.color;
ctx.globalAlpha = p.alpha;
ctx.fillRect(p.x, p.y, p.size, p.size);
}
ctx.restore();
}
}
function gameLoop(time) {
requestAnimationFrame(gameLoop);
if (time - lastTick >= TICK_INTERVAL) {
update();
lastTick = time;
}
updateParticles();
// CLEAR
ctx.clearRect(0, 0, canvas.width, canvas.height);
// RENDER LAYERS
drawTracks();
drawScenery();
drawStationAndPassenger(time);
drawTrain();
drawParticles();
}
// User Interaction to take over controls
function takeControl() {
if (isDemo || gameOver) {
isDemo = false;
statusEl.textContent = "PLAYER IN CAB";
statusEl.style.color = "#f1c40f";
overlayEl.classList.add('hidden');
resetGame();
}
}
window.addEventListener('keydown', (e) => {
const k = e.key;
if (['ArrowUp', 'KeyW', 'ArrowDown', 'KeyS', 'ArrowLeft', 'KeyA', 'ArrowRight', 'KeyD', ' '].includes(k) || e.code.startsWith('Arrow')) {
e.preventDefault();
}
if (isDemo || gameOver) {
takeControl();
}
if (k === 'ArrowUp' || k === 'w' || k === 'W') {
if (dir.y !== 1) nextDir = { x: 0, y: -1 };
} else if (k === 'ArrowDown' || k === 's' || k === 'S') {
if (dir.y !== -1) nextDir = { x: 0, y: 1 };
} else if (k === 'ArrowLeft' || k === 'a' || k === 'A') {
if (dir.x !== 1) nextDir = { x: -1, y: 0 };
} else if (k === 'ArrowRight' || k === 'd' || k === 'D') {
if (dir.x !== -1) nextDir = { x: 1, y: 0 };
}
});
canvas.addEventListener('mousedown', () => {
takeControl();
});
// START
generateScenery();
resetGame();
requestAnimationFrame(gameLoop);
</script>
</body>
</html>