Train snakeOne-line
GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 44.9 s
- Cost
- $0.0026
- Output tokens
- 5,158
- Spent thinking
- 0 tokens
- Speed
- 134 tok/s
- Console errors
- 0
- First token after
- 6.4 s
- First visible text after
- 6.4 s
- Prompt tokens
- 361
- Code size
- 15.4 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- 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 · 311 lines
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Platform Express</title>
<style>
:root{
--ink:#173248;--muted:#688095;--cream:#fff8e9;--grass:#b9dcae;
--rail:#718091;--red:#e66c59;--yellow:#f6c85f;--blue:#56a8ce;--green:#69b88d;
}
*{box-sizing:border-box}
body{
margin:0;min-height:100vh;display:grid;place-items:center;padding:22px;
background:#e8f0e8;color:var(--ink);
font-family:ui-rounded,"Trebuchet MS",system-ui,-apple-system,sans-serif;
}
.game{
width:min(100%,1000px);background:var(--cream);border:4px solid white;
border-radius:28px;overflow:hidden;box-shadow:0 20px 60px #24485322;
}
header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 26px 15px}
.brand{display:flex;align-items:center;gap:13px}
.logo{width:48px;height:48px;border-radius:16px;background:#f6c85f;display:grid;place-items:center;font-size:27px;box-shadow:inset 0 -4px #dda841}
h1{font-size:clamp(19px,3vw,27px);line-height:1;margin:0;letter-spacing:-.6px}
.subtitle{font-size:13px;color:var(--muted);margin-top:5px}
.stats{display:flex;gap:10px}
.stat{min-width:95px;padding:9px 14px;border-radius:15px;background:#edf2e8;text-align:center}
.stat strong{display:block;font-size:20px;line-height:1.05}
.stat span{font-size:10px;text-transform:uppercase;letter-spacing:1.2px;color:var(--muted);font-weight:800}
.track-wrap{padding:0 18px 16px}
.board{
position:relative;border-radius:20px;overflow:hidden;background:#dcedd5;
border:2px solid #d1e3c9;aspect-ratio: 1.92 / 1;min-height:300px;
}
canvas{display:block;width:100%;height:100%}
.overlay{
position:absolute;inset:0;display:grid;place-items:center;background:#17324826;
transition:opacity .25s;
}
.overlay.hidden{opacity:0;pointer-events:none}
.card{
text-align:center;background:#fff9eb;border:3px solid white;border-radius:22px;
padding:17px 25px;box-shadow:0 8px 24px #17324825;max-width:calc(100% - 30px);
}
.card .emoji{font-size:29px;line-height:1}
.card h2{font-size:20px;margin:7px 0 4px}
.card p{font-size:12px;color:var(--muted);margin:0}
.card kbd{display:inline-block;margin-top:10px;padding:5px 10px;border-radius:8px;background:#eaf0e8;font:700 11px inherit;color:var(--ink)}
footer{padding:0 24px 18px;display:flex;justify-content:space-between;align-items:center;gap:12px;color:var(--muted);font-size:12px}
.legend{display:flex;gap:14px;align-items:center}
.legend span{white-space:nowrap}
.dot{display:inline-block;width:9px;height:9px;margin-right:5px;border-radius:50%;vertical-align:middle}
.controls{font-weight:700;color:#527087}
.touch-controls{display:none;gap:7px;justify-content:center;padding:0 0 17px}
.touch-controls button{border:0;border-radius:12px;background:#edf2e8;color:var(--ink);font-size:18px;font-weight:800;width:44px;height:38px}
@media(max-width:650px){
body{padding:10px}
.game{border-radius:22px}
header{padding:15px 15px 12px;align-items:flex-start}
.logo{width:40px;height:40px;border-radius:13px;font-size:23px}
.subtitle{font-size:11px}
.stats{gap:6px}
.stat{min-width:62px;padding:8px 7px}
.stat strong{font-size:17px}
.stat span{font-size:8px;letter-spacing:.7px}
.track-wrap{padding:0 9px 11px}
.board{aspect-ratio:1 / 1.08;min-height:0}
footer{padding:0 14px 12px;font-size:10px;flex-wrap:wrap}
.legend{gap:8px}
.controls{display:none}
.touch-controls{display:flex}
}
@media(max-width:390px){.subtitle{max-width:155px}.legend{gap:6px}}
</style>
</head>
<body>
<main class="game">
<header>
<div class="brand">
<div class="logo">🚂</div>
<div><h1>Platform Express</h1><div class="subtitle">All aboard! Pick up passengers along the line.</div></div>
</div>
<div class="stats">
<div class="stat"><strong id="score">0</strong><span>Passengers</span></div>
<div class="stat"><strong id="best">0</strong><span>On board</span></div>
</div>
</header>
<div class="track-wrap">
<div class="board" id="board">
<canvas id="canvas" aria-label="Snake train game"></canvas>
<div class="overlay" id="overlay">
<div class="card">
<div class="emoji">🚉</div>
<h2>Ready to depart?</h2>
<p>Collect every waiting passenger to add a wagon.</p>
<kbd>Click or press a key to start</kbd>
</div>
</div>
</div>
</div>
<div class="touch-controls" aria-label="Directional controls">
<button data-dir="up" aria-label="Up">▲</button>
<button data-dir="left" aria-label="Left">◀</button>
<button data-dir="down" aria-label="Down">▼</button>
<button data-dir="right" aria-label="Right">▶</button>
</div>
<footer>
<div class="legend">
<span><i class="dot" style="background:#e66c59"></i>Passengers</span>
<span><i class="dot" style="background:#f6c85f"></i>Wagons</span>
<span><i class="dot" style="background:#56a8ce"></i>Track</span>
</div>
<div class="controls">Arrow keys / WASD to steer · Space to pause</div>
</footer>
</main>
<script>
(() => {
const canvas=document.getElementById('canvas'), ctx=canvas.getContext('2d');
const board=document.getElementById('board'), overlay=document.getElementById('overlay');
const scoreEl=document.getElementById('score'), bestEl=document.getElementById('best');
const dirs={up:{x:0,y:-1},down:{x:0,y:1},left:{x:-1,y:0},right:{x:1,y:0}};
const passengerColors=['#e66c59','#f3aa52','#8b83c9','#61a8c8','#e67ba0'];
let cols=24,rows=14,cell=30,train=[],dir='right',nextDir='right',passenger=null;
let score=0,started=false,paused=false,gameOver=false,lastStep=0,stepMs=220,autoTimer=0;
let anim=0, dust=[];
function resize(){
const rect=board.getBoundingClientRect(), dpr=Math.min(devicePixelRatio||1,2);
canvas.width=Math.round(rect.width*dpr);canvas.height=Math.round(rect.height*dpr);
ctx.setTransform(dpr,0,0,dpr,0,0);
const mobile=rect.width<650;
cols=mobile?16:24;
rows=mobile?Math.max(16,Math.round(cols*rect.height/rect.width)):14;
cell=Math.min(rect.width/cols,rect.height/rows);
cols=Math.floor(rect.width/cell);rows=Math.floor(rect.height/cell);
reset(false);draw();
}
function reset(show=true){
const cx=Math.floor(cols*.43), cy=Math.floor(rows/2);
train=[{x:cx,y:cy},{x:cx-1,y:cy},{x:cx-2,y:cy}];
dir=nextDir='right';score=0;gameOver=false;paused=false;stepMs=220;
scoreEl.textContent='0';bestEl.textContent='3';
placePassenger();
if(show){started=false;overlay.classList.remove('hidden')}
}
function placePassenger(){
let p,tries=0;
do{p={x:Math.floor(Math.random()*cols),y:Math.floor(Math.random()*rows),color:passengerColors[Math.floor(Math.random()*passengerColors.length)]};tries++}
while(train.some(t=>t.x===p.x&&t.y===p.y)&&tries<500);
passenger=p;
}
function start(){
if(gameOver) reset(false);
started=true;paused=false;overlay.classList.add('hidden');lastStep=performance.now();
}
function setDirection(d){
if(!dirs[d])return;
const v=dirs[d],cur=dirs[dir];
if(v.x===-cur.x&&v.y===-cur.y)return;
nextDir=d;
if(!started)start();
}
function key(e){
const k=e.key.toLowerCase();
const map={arrowup:'up',w:'up',arrowdown:'down',s:'down',arrowleft:'left',a:'left',arrowright:'right',d:'right'};
if(map[k]){e.preventDefault();setDirection(map[k]);return}
if(k===' '){e.preventDefault();if(!started){start()}else if(!gameOver){paused=!paused;if(paused)showCard('⏸️','Train paused','Press space or a direction to continue.');else overlay.classList.add('hidden')}}
else if(!started&&k!=='tab')start();
}
function showCard(icon,title,text){
overlay.innerHTML='<div class="card"><div class="emoji">'+icon+'</div><h2>'+title+'</h2><p>'+text+'</p></div>';
overlay.classList.remove('hidden');
}
function step(){
dir=nextDir;const v=dirs[dir],head={x:train[0].x+v.x,y:train[0].y+v.y};
const grow=passenger&&head.x===passenger.x&&head.y===passenger.y;
const check=train.slice(0,grow?train.length:train.length-1);
if(head.x<0||head.y<0||head.x>=cols||head.y>=rows||check.some(p=>p.x===head.x&&p.y===head.y)){
gameOver=true;started=false;
showCard('🚦','End of the line!','You picked up '+score+' passenger'+(score===1?'':'s')+'. Tap or press a key to ride again.');
return;
}
train.unshift(head);if(!grow)train.pop();
else{score++;scoreEl.textContent=score;bestEl.textContent=train.length;stepMs=Math.max(112,220-score*5);placePassenger()}
}
function roundedRect(x,y,w,h,r,fill,stroke){
ctx.beginPath();ctx.roundRect(x,y,w,h,r);ctx.fillStyle=fill;ctx.fill();
if(stroke){ctx.strokeStyle=stroke;ctx.lineWidth=1.2;ctx.stroke()}
}
function draw(){
const w=canvas.clientWidth,h=canvas.clientHeight;
ctx.clearRect(0,0,w,h);
ctx.fillStyle='#dcedd5';ctx.fillRect(0,0,w,h);
// Sunny, softly dotted countryside
for(let y=0;y<rows;y++)for(let x=0;x<cols;x++){
const px=x*cell,py=y*cell;
if((x*7+y*13)%17===0){ctx.fillStyle='#c5e2bb';ctx.beginPath();ctx.arc(px+cell*.22,py+cell*.3,Math.max(1,cell*.045),0,Math.PI*2);ctx.fill()}
}
// platform awning along the upper edge
const pad=cell*.25, platformH=cell*1.05;
roundedRect(pad,pad,w-pad*2,platformH,cell*.22,'#f7edcf');
ctx.fillStyle='#f3d987';ctx.fillRect(pad,pad,w-pad*2,cell*.14);
for(let x=pad+cell*.3;x<w-pad;x+=cell*1.65){
ctx.fillStyle='#f0bca0';ctx.fillRect(x,pad+cell*.15,cell*.08,platformH-cell*.22);
}
// railway sleepers and rails
const railY=h-cell*1.1, railH=cell*.36;
ctx.fillStyle='#9baf9b';
for(let x=cell*.18;x<w;x+=cell*.52)roundedRect(x,railY,cell*.28,railH,cell*.06,'#9ca99e');
roundedRect(0,railY+cell*.02,w,cell*.075,cell*.035,'#647a86');
roundedRect(0,railY+cell*.25,w,cell*.075,cell*.035,'#647a86');
// passenger waiting on platform
if(passenger){
const px=(passenger.x+.5)*cell,py=(passenger.y+.5)*cell;
ctx.save();ctx.translate(px,py);
const bob=Math.sin(anim/180)*cell*.035;ctx.translate(0,bob);
ctx.fillStyle='#17324824';ctx.beginPath();ctx.ellipse(0,cell*.24,cell*.22,cell*.07,0,0,Math.PI*2);ctx.fill();
// little suitcase
roundedRect(-cell*.25,cell*.02,cell*.18,cell*.2,cell*.04,'#e7ab55');
ctx.strokeStyle='#bd7e3b';ctx.lineWidth=1.5;ctx.beginPath();ctx.moveTo(-cell*.21,cell*.02);ctx.lineTo(-cell*.21,-cell*.025);ctx.lineTo(-cell*.1,-cell*.025);ctx.lineTo(-cell*.1,cell*.02);ctx.stroke();
// body + head
roundedRect(-cell*.12,-cell*.01,cell*.25,cell*.24,cell*.1,passenger.color);
ctx.fillStyle='#f3c49c';ctx.beginPath();ctx.arc(0,-cell*.13,cell*.13,0,Math.PI*2);ctx.fill();
ctx.fillStyle='#443b38';ctx.beginPath();ctx.arc(-cell*.04,-cell*.15,cell*.014,0,Math.PI*2);ctx.arc(cell*.045,-cell*.15,cell*.014,0,Math.PI*2);ctx.fill();
ctx.strokeStyle='#a85d54';ctx.lineWidth=1.2;ctx.beginPath();ctx.arc(0,-cell*.105,cell*.04,.1,Math.PI-.1);ctx.stroke();
ctx.restore();
}
// train wheels and wagons, draw tail to head
for(let i=train.length-1;i>=0;i--){
const p=train[i],cx=(p.x+.5)*cell,cy=(p.y+.5)*cell;
const isHead=i===0,bodyW=cell*.78,bodyH=cell*.67;
ctx.save();ctx.translate(cx,cy);
let angle=0;if(isHead)angle={right:0,down:Math.PI/2,left:Math.PI,up:-Math.PI/2}[dir];
else{
const prev=train[i-1],dx=prev.x-p.x,dy=prev.y-p.y;
angle=Math.atan2(dy,dx);
}
ctx.rotate(angle);
if(!isHead){
// connecting coupler
ctx.fillStyle='#475b62';ctx.fillRect(-cell*.54,-cell*.045,cell*.16,cell*.09);
// bright wagon
const color=['#f6c85f','#56a8ce','#e98165','#69b88d','#aa8bc8'][(i-1)%5];
roundedRect(-bodyW*.48,-bodyH*.39,bodyW*.88,bodyH*.78,cell*.15,color,'#ffffffaa');
// windows / seat
roundedRect(-bodyW*.25,-bodyH*.22,bodyW*.28,bodyH*.25,cell*.06,'#fff5d6');
roundedRect(bodyW*.08,-bodyH*.22,bodyW*.25,bodyH*.25,cell*.06,'#fff5d6');
ctx.fillStyle='#243d4b';
ctx.beginPath();ctx.arc(-bodyW*.23,bodyH*.42,cell*.105,0,Math.PI*2);ctx.arc(bodyW*.22,bodyH*.42,cell*.105,0,Math.PI*2);ctx.fill();
ctx.fillStyle='#e8eeee';ctx.beginPath();ctx.arc(-bodyW*.23,bodyH*.42,cell*.04,0,Math.PI*2);ctx.arc(bodyW*.22,bodyH*.42,cell*.04,0,Math.PI*2);ctx.fill();
// tiny waving passenger
if(i===1){
ctx.fillStyle='#f3c49c';ctx.beginPath();ctx.arc(0,-bodyH*.12,cell*.09,0,Math.PI*2);ctx.fill();
ctx.fillStyle='#273f4c';ctx.fillRect(-cell*.045,-bodyH*.08,cell*.09,cell*.18);
}
}else{
// locomotive cab and boiler, oriented nose to the right
ctx.fillStyle='#263d48';ctx.beginPath();ctx.arc(-cell*.2,cell*.31,cell*.13,0,Math.PI*2);ctx.arc(cell*.26,cell*.31,cell*.13,0,Math.PI*2);ctx.fill();
ctx.fillStyle='#e9eeee';ctx.beginPath();ctx.arc(-cell*.2,cell*.31,cell*.05,0,Math.PI*2);ctx.arc(cell*.26,cell*.31,cell*.05,0,Math.PI*2);ctx.fill();
roundedRect(-cell*.44,-cell*.19,cell*.75,cell*.48,cell*.14,'#e66856','#fff7df');
roundedRect(-cell*.31,-cell*.5,cell*.43,cell*.38,cell*.1,'#d9574c','#fff7df');
roundedRect(-cell*.23,-cell*.42,cell*.26,cell*.21,cell*.05,'#bde7ef');
roundedRect(cell*.22,-cell*.1,cell*.31,cell*.2,cell*.06,'#f6c85f');
roundedRect(cell*.38,-cell*.34,cell*.14,cell*.18,cell*.04,'#445966');
ctx.fillStyle='#f6c85f';ctx.beginPath();ctx.arc(cell*.43,0,cell*.105,0,Math.PI*2);ctx.fill();ctx.strokeStyle='#fff3c8';ctx.lineWidth=2;ctx.stroke();
// little steam puffs
const puff=Math.sin(anim/200)*cell*.03;
ctx.fillStyle='#ffffffaa';ctx.beginPath();ctx.arc(-cell*.14,-cell*.58-puff,cell*.09,0,Math.PI*2);ctx.arc(-cell*.29,-cell*.67-puff,cell*.065,0,Math.PI*2);ctx.fill();
}
ctx.restore();
}
// trackside flowers
for(let x=cell*.5;x<w;x+=cell*2.7){
const y=h-cell*.34;
ctx.strokeStyle='#76a66f';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(x,y+cell*.15);ctx.lineTo(x,y-cell*.04);ctx.stroke();
ctx.fillStyle=['#f7b86a','#e98ca1','#fff2c2'][Math.floor(x/cell)%3];
ctx.beginPath();ctx.arc(x,y-cell*.07,cell*.07,0,Math.PI*2);ctx.fill();
}
// pause hint / small route marker
}
function loop(now){
anim=now;
if(started&&!paused&&now-lastStep>stepMs){step();lastStep=now}
draw();requestAnimationFrame(loop);
}
window.addEventListener('keydown',key);
board.addEventListener('pointerdown',()=>{if(!started||paused||gameOver)start()});
document.querySelectorAll('.touch-controls button').forEach(btn=>btn.addEventListener('click',()=>setDirection(btn.dataset.dir)));
window.addEventListener('resize',resize);
resize();
// self-playing demo: after the ready screen, the train cruises and seeks each passenger.
function autopilot(){
if(!started){autoTimer=setTimeout(autopilot,1100);return}
if(!paused&&!gameOver&&passenger){
const head=train[0], options=['right','down','left','up'];
const candidates=options.map(d=>({d,x:head.x+dirs[d].x,y:head.y+dirs[d].y,dist:0}))
.filter(a=>!(a.x<0||a.y<0||a.x>=cols||a.y>=rows))
.filter(a=>!train.slice(0,-1).some(t=>t.x===a.x&&t.y===a.y))
.filter(a=>{const c=dirs[dir];return !(dirs[a.d].x===-c.x&&dirs[a.d].y===-c.y)});
candidates.forEach(a=>a.dist=Math.abs(a.x-passenger.x)+Math.abs(a.y-passenger.y));
candidates.sort((a,b)=>a.dist-b.dist);
if(candidates.length)nextDir=candidates[0].d;
}
autoTimer=setTimeout(autopilot,240);
}
autoTimer=setTimeout(autopilot,1300);
requestAnimationFrame(loop);
})();
</script>
</body>
</html>