Train snakeOne-line
GPT-5 Nano
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 25.8 s
- Cost
- $0.0016
- Output tokens
- 3,835
- Spent thinking
- 0 tokens
- Speed
- 153.6 tok/s
- Console errors
- 0
- First token after
- 855 ms
- First visible text after
- 855 ms
- Prompt tokens
- 361
- Code size
- 11.7 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"minimal"}- Max output
- 128,000 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 · 410 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Train Snake - Passenger Hop</title>
<style>
:root{
--bg: #0b1020;
--track: #2b2b2b;
--grid: rgba(255,255,255,0.05);
--locomotive: #ff4d4d;
--wagon1: #4db6ff;
--wagon2: #7cffb5;
--wagon3: #ffd166;
--passenger: #fff;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
background: radial-gradient(circle at 30% -10%, rgba(255,255,255,0.15) 0 20%, transparent 40%),
radial-gradient(circle at 90% 0%, rgba(0,0,0,0.25) 0 30%, transparent 40%),
var(--bg);
color:#eaf2ff;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial;
}
.wrap{
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
min-height:100vh;
padding:20px;
}
h1{margin:0 0 6px 0;font-size:1.1rem;letter-spacing:0.3px;color:#e8f0ff}
canvas{background: #0a0f1a; border-radius:12px; box-shadow:0 8px 30px rgba(0,0,0,.4); display:block}
.hint{
margin-top:8px;
font-size:0.9rem;
color:#cbd7ff;
opacity:.9;
}
.startOverlay{
position:absolute; inset:0;
display:flex; align-items:center; justify-content:center;
background: rgba(0,0,0,.5);
color:#eaf2ff;
font-family: inherit;
font-size:1.05rem;
text-align:center;
padding:20px;
}
@media (max-width: 700px){
canvas{width:95vw; height:auto}
}
</style>
</head>
<body>
<div class="wrap" aria-label="Train Snake game container">
<h1>Train Snake: Passengers on the Platform</h1>
<canvas id="game" width="960" height="540" aria-label="Train Snake game canvas"></canvas>
<div class="hint" id="hint">Click the canvas or press any key to start. Control the train with Arrow Keys or WASD.</div>
<div class="startOverlay" id="startOverlay" role="dialog" aria-label="Start screen" style="display:flex">
<div>
<div style="font-weight:700;font-size:1.1rem;margin-bottom:6px">Click or press a key to start</div>
<div>Guide the locomotive to pick up waiting passengers on the platform. The train grows by adding colorful wagons. Avoid colliding with yourself.</div>
</div>
</div>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('startOverlay');
const hint = document.getElementById('hint');
// Game settings
const COLS = 48; // grid columns
const ROWS = 28; // grid rows
const CELL = 20; // px per cell (base, computed)
// Adjust canvas scale to fit viewport
function fitCanvas(){
const w = Math.min(window.innerWidth * 0.95, 1100);
const h = Math.max(420, Math.min(window.innerHeight * 0.85, 600));
// compute cell size to fit cols/rows
const cw = Math.floor(w / COLS);
const ch = Math.floor(h / ROWS);
const s = Math.max(6, Math.min(cw, ch)); // cell size
canvas.width = COLS * s;
canvas.height = ROWS * s;
window.__cell = s;
}
fitCanvas();
window.addEventListener('resize', fitCanvas);
// Colors
const LOC = '#ff4d4d';
const WAGON_COLORS = ['#4db6ff','#7cffb5','#ffd166','#9b87ff'];
const BG_GRID = '#1a1f2b';
const PASSENGER = '#fff';
// Train snake (train)
let dir = {x:1, y:0};
let nextDir = {x:1, y:0};
let head = {x:5, y:5};
let body = [{x: head.x, y: head.y}]; // locomotive at head
let wagons = []; // array of wagons segments after locomotive
let pendingGrowth = 0;
// Passenger on platform to pick up
let passenger = null; // position {x,y}
let score = 0;
let gameOver = false;
let started = false;
function randInt(min, max){
return Math.floor(Math.random()*(max-min+1))+min;
}
function placePassenger(){
// place on grid not colliding with train
let tries = 0;
while(tries<100){
const p = {x: randInt(0, COLS-1), y: randInt(0, ROWS-1)};
const collision = [head, ...body, ...wagons].some(seg => seg.x===p.x && seg.y===p.y);
if(!collision){
passenger = p;
return;
}
tries++;
}
passenger = null;
}
function init(){
dir = {x:1, y:0};
nextDir = {x:1, y:0};
head = {x:5, y:5};
body = [{x: head.x, y: head.y}];
wagons = [];
pendingGrowth = 0;
score = 0;
gameOver = false;
started = false;
placePassenger();
render(); // first paint
}
// AI autoplay: simple path follower toward next target (passenger). Keeps moving forward with basic avoidance.
function aiDecide(){
// If has no growth, keep moving toward passenger when possible
if(!passenger) return;
// simple heuristic: try to move towards passenger without immediate collision with self
const desired = {
x: passenger.x - head.x,
y: passenger.y - head.y
};
// prefer horizontal if abs dx > dy
const tries = [
{x: Math.sign(desired.x) || 0, y: 0},
{x: 0, y: Math.sign(desired.y) || 0},
{x: Math.sign(desired.x) || 0, y: Math.sign(desired.y) || 0},
{x: -Math.sign(desired.x) || 0, y: 0},
{x: 0, y: -Math.sign(desired.y) || 0}
];
for(let t of tries){
const cand = {x: head.x + t.x, y: head.y + t.y};
if(cand.x<0||cand.y<0||cand.x>=COLS||cand.y>=ROWS) continue;
// collision check
if(isCollisionPoint(cand.x, cand.y)) continue;
nextDir = t;
return;
}
// else no safe move, random safe
const dirs = [{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}];
for(let d of dirs){
const cand = {x: head.x + d.x, y: head.y + d.y};
if(cand.x<0||cand.y<0||cand.x>=COLS||cand.y>=ROWS) continue;
if(!isCollisionPoint(cand.x, cand.y)){
nextDir = d;
return;
}
}
}
function isCollisionPoint(x,y){
// collision with self (excluding tail which moves)
for(let i=0;i<headIndex();i++){
const s = i===0 ? head : body[i];
if(s.x===x && s.y===y) return true;
}
// wagons
for(let w of wagons){
if(w.x===x && w.y===y) return true;
}
return false;
}
function headIndex(){ return 0; }
// Move
function step(){
if(gameOver) return;
// AI planning
aiDecide();
// Apply direction
dir = {x: nextDir.x, y: nextDir.y};
const nx = head.x + dir.x;
const ny = head.y + dir.y;
// wrap? We'll keep walls as borders (game over on mistake)
if(nx<0 || ny<0 || nx>=COLS || ny>=ROWS){
gameOver = true;
render();
return;
}
// check collision with body or wagons
if(isCollisionPoint(nx, ny)){
gameOver = true;
render();
return;
}
// move
body.unshift({x: head.x, y: head.y});
head = {x: nx, y: ny};
// check passenger pickup
if(passenger && head.x===passenger.x && head.y===passenger.y){
score += 1;
pendingGrowth += 3; // add 3 wagons
placePassenger();
}
// handle growth
if(pendingGrowth>0){
wagons.unshift({x: body[0].x, y: body[0].y});
pendingGrowth--;
} else if(wagons.length>0){
// move wagons: each wagon takes behind previous
wagons.unshift({x: body[0].x, y: body[0].y});
wagons.pop();
}
render();
}
// Rendering
function render(){
const s = window.__cell || 20;
// clear
ctx.clearRect(0,0,canvas.width, canvas.height);
// background grid
ctx.fillStyle = BG_GRID;
ctx.fillRect(0,0,canvas.width, canvas.height);
// subtle grid lines
ctx.strokeStyle = 'rgba(255,255,255,0.04)';
ctx.lineWidth = 1;
for(let i=0;i<=COLS;i++){
ctx.beginPath();
ctx.moveTo(i*s,0);
ctx.lineTo(i*s, ROWS*s);
ctx.stroke();
}
for(let j=0;j<=ROWS;j++){
ctx.beginPath();
ctx.moveTo(0, j*s);
ctx.lineTo(COLS*s, j*s);
ctx.stroke();
}
// platform line (bottom)
ctx.fillStyle = '#2a2a2a';
ctx.fillRect(0, ROWS*s - s/2, COLS*s, s/2);
// draw passenger if waiting
if(passenger){
ctx.fillStyle = PASSENGER;
ctx.fillRect(passenger.x*s + s*0.2, passenger.y*s + s*0.2, s*0.6, s*0.6);
// little seat icon
ctx.fillStyle = 'rgba(0,0,0,0.15)';
ctx.fillRect(passenger.x*s + s*0.25, passenger.y*s + s*0.25, s*0.3, s*0.3);
}
// draw wagons first so locomotive on top
for(let i=wagons.length-1; i>=0; i--){
const w = wagons[i];
drawCoach(w.x, w.y, i+1);
}
// locomotive
drawLocomotive(head.x, head.y);
// score
ctx.fillStyle = '#e9f2ff';
ctx.font = `${Math.max(12, Math.floor(s*0.9))}px system-ui, Arial`;
ctx.textAlign = 'left';
ctx.fillText('Passengers picked: ' + score, 10, 20);
if(gameOver){
ctx.fillStyle = 'rgba(0,0,0,0.6)';
ctx.fillRect(0, canvas.height/2 - 40, canvas.width, 80);
ctx.fillStyle = '#ffdddd';
ctx.textAlign = 'center';
ctx.font = `${Math.max(14, Math.floor(s*0.9))}px system-ui, Arial`;
ctx.fillText('Game over! Refresh to try again.', canvas.width/2, canvas.height/2);
} else if(!started){
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(0,0,canvas.width,canvas.height);
}
}
function drawLocomotive(x,y){
const s = window.__cell;
// locomotive body
const px = x*s, py = y*s;
// engine
ctx.fillStyle = LOC;
ctx.fillRect(px+2, py+4, s-4, s-6);
// cabin
ctx.fillStyle = '#ff7f50';
ctx.fillRect(px+4, py+2, s-8, s-9);
// chimney
ctx.fillStyle = '#333';
ctx.fillRect(px + s*0.65, py+0.5, s*0.25, s*0.5);
// wheels
ctx.fillStyle = '#111';
ctx.fillRect(px+6, py+s-4, s*0.8, 4);
// windows
ctx.fillStyle = '#9bd4ff';
ctx.fillRect(px+6, py+6, s-12, s-8);
}
function drawCoach(x,y,idx){
const s = window.__cell;
const px = x*s, py = y*s;
const color = WAGON_COLORS[(idx-1) % WAGON_COLORS.length];
// wagon body
ctx.fillStyle = color;
ctx.fillRect(px+2, py+4, s-4, s-6);
// wagon roof
ctx.fillStyle = shade(color, -20);
ctx.fillRect(px+4, py+2, s-8, 6);
// wheels
ctx.fillStyle = '#111';
ctx.fillRect(px+6, py+s-3, s-12, 3);
// windows
ctx.fillStyle = '#d9f0ff';
for(let wx=6; wx<=s-12; wx+=s/2){
ctx.fillRect(px+wx, py+6, s/4, s-12);
}
}
// simple color shade helper
function shade(hex, percent){
// hex like #rrggbb
const f = parseInt(hex.slice(1),16);
const r = Math.max(0, Math.min(255, (f>>16) + Math.round( (percent/100)*255 )));
const g = Math.max(0, Math.min(255, ((f>>8)&0xff) + Math.round( (percent/100)*255 )));
const b = Math.max(0, Math.min(255, (f&0xff) + Math.round( (percent/100)*255 )));
return `rgb(${r},${g},${b})`;
}
// Input handling
window.addEventListener('keydown', (e) => {
if(!started){
started = true;
overlay.style.display = 'none';
hint.textContent = 'Use arrow keys or WASD to steer. Train grows by picking up passengers.';
}
if(gameOver) return;
const k = e.key.toLowerCase();
if(['arrowleft','a'].includes(e.key)){
nextDir = {x:-1, y:0};
} else if(['arrowright','d'].includes(e.key)){
nextDir = {x:1, y:0};
} else if(['arrowup','w'].includes(e.key)){
nextDir = {x:0, y:-1};
} else if(['arrowdown','s'].includes(e.key)){
nextDir = {x:0, y:1};
}
});
canvas.addEventListener('mousedown', () => {
if(!started){
started = true;
overlay.style.display = 'none';
hint.textContent = 'Use arrow keys or WASD to steer. Train grows by picking up passengers.';
}
});
// Loop
let last = performance.now();
const INTERVAL = 120; // ms per step for smooth AI movement
function loop(now){
const dt = now - last;
if(dt > INTERVAL){
step();
last = now;
}
requestAnimationFrame(loop);
}
// Init and start
init();
// start in auto-run mode: overlay shown until click/keypress
requestAnimationFrame(loop);
})();
</script>
</body>
</html>