Models 44
Train snakeOne-line

Muse Ember

Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
13.3 s
Cost
$0.0039
Output tokens
7,762
Spent thinking
1,351 tokens
Speed
705.5 tok/s
Console errors
0
First token after
2.3 s
First visible text after
4.9 s
Prompt tokens
380
Code size
18.1 KB
Finish reason
stop
Served by
Meta
Model id
meta/muse-ember
Reasoning sent
{"effort":"low"}
Max output
943,718 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 · 329 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sunny Station — Train Snake</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  body{
    font-family:ui-rounded,system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
    background:linear-gradient(180deg,#7ec8f7 0%,#aee3ff 38%,#fff3c4 100%);
    min-height:100vh;color:#19324d;overflow-x:hidden;
    display:flex;flex-direction:column;align-items:center;
  }
  header{width:min(960px,96vw);padding:14px 6px 6px;display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
  .title{display:flex;align-items:center;gap:10px}
  .logo{width:52px;height:52px;background:#e63946;border-radius:14px;display:grid;place-items:center;font-size:30px;box-shadow:0 6px 0 #b81f2d,0 10px 18px rgba(0,0,0,.18);border:2px solid #fff}
  h1{font-size:clamp(22px,4vw,34px);line-height:.95;letter-spacing:-.5px}
  h1 span{color:#e63946}
  .sub{font-size:13px;font-weight:700;opacity:.75;margin-top:3px}
  .hud{display:flex;gap:8px;flex-wrap:wrap}
  .pill{background:#fff;border:2px solid #19324d;border-radius:999px;padding:7px 12px;font-weight:900;box-shadow:0 3px 0 #19324d;font-size:14px;white-space:nowrap}
  .pill b{color:#e63946}
  .pill.green b{color:#1a9e4b}
  main{width:min(960px,96vw);display:flex;flex-direction:column;align-items:center;gap:10px;padding-bottom:16px}
  .frame{
    width:100%;background:#fff;border:3px solid #19324d;border-radius:20px;padding:8px;
    box-shadow:0 10px 0 #19324d,0 22px 36px rgba(0,0,0,.22);position:relative;overflow:hidden
  }
  canvas{display:block;width:100%;height:auto;border-radius:12px;background:#79c94f;touch-action:none}
  .banner{
    position:absolute;left:50%;top:16px;transform:translateX(-50%);z-index:5;
    background:rgba(255,255,255,.96);border:2px solid #19324d;border-radius:999px;
    padding:8px 14px;font-weight:900;font-size:13px;box-shadow:0 4px 0 #19324d;
    display:flex;gap:8px;align-items:center;max-width:92%;text-align:center;transition:.25s;
  }
  .banner.hide{opacity:0;pointer-events:none;transform:translateX(-50%) translateY(-10px)}
  .dot{width:9px;height:9px;background:#1a9e4b;border-radius:50%;box-shadow:0 0 0 4px #d9f7e4;animation:pulse 1s infinite}
  @keyframes pulse{50%{transform:scale(1.4)}}
  .overlay{
    position:absolute;inset:8px;border-radius:12px;background:rgba(20,40,60,.72);backdrop-filter:blur(3px);
    display:none;place-items:center;z-index:6;text-align:center;color:#fff;padding:20px
  }
  .overlay.show{display:grid}
  .card{background:#fff;color:#19324d;border:3px solid #19324d;border-radius:18px;padding:20px;box-shadow:0 8px 0 #19324d;max-width:340px}
  .card h2{font-size:28px;margin-bottom:6px}
  .card p{font-weight:700;opacity:.8;margin-bottom:12px}
  button{font-family:inherit;cursor:pointer;border:2px solid #19324d;font-weight:900;border-radius:999px;box-shadow:0 3px 0 #19324d;transition:.12s}
  button:active{transform:translateY(3px);box-shadow:0 0 0 #19324d}
  .btn{background:#ffd23f;padding:9px 16px;font-size:14px}
  .btn.red{background:#e63946;color:#fff}
  .btn.white{background:#fff}
  .controls{width:100%;display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}
  .hint{background:rgba(255,255,255,.9);border:2px solid #19324d;border-radius:12px;padding:8px 11px;font-weight:800;font-size:13px}
  .hint kbd{background:#19324d;color:#fff;padding:2px 7px;border-radius:6px;font-family:inherit}
  .dpad{display:none;grid-template-columns:repeat(3,48px);grid-template-rows:repeat(3,42px);gap:5px}
  .dpad button{background:#fff;font-size:18px;border-radius:12px;padding:0}
  .dpad .up{grid-column:2;grid-row:1}.dpad .left{grid-column:1;grid-row:2}.dpad .down{grid-column:2;grid-row:2}.dpad .right{grid-column:3;grid-row:2}
  .actions{display:flex;gap:8px;flex-wrap:wrap}
  @media (max-width:600px){.dpad{display:grid}.hint{display:none}header{justify-content:center}.hud{justify-content:center}}
</style>
</head>
<body>
<header>
  <div class="title">
    <div class="logo">🚂</div>
    <div><h1>SUNNY <span>STATION</span></h1><div class="sub">Pick up passengers • grow your train!</div></div>
  </div>
  <div class="hud">
    <div class="pill">🧑 Passengers <b id="score">0</b></div>
    <div class="pill green">🚃 Wagons <b id="len">3</b></div>
    <div class="pill">⭐ Bonus <b id="bonus">0</b></div>
    <div class="pill">🏆 Best <b id="best">0</b></div>
  </div>
</header>
<main>
  <div class="frame" id="frame">
    <div class="banner" id="banner"><span class="dot"></span><span id="bannerText">🤖 Demo train is playing — click / press a key to drive!</span></div>
    <canvas id="game" width="800" height="560"></canvas>
    <div class="overlay" id="overlay">
      <div class="card">
        <div style="font-size:44px">💥🚂</div>
        <h2>Derailment!</h2>
        <p id="overText">You picked up 0 passengers.</p>
        <button class="btn red" id="againBtn">↻ Play again</button>
      </div>
    </div>
  </div>
  <div class="controls">
    <div class="hint"><kbd>←</kbd><kbd>↑</kbd><kbd>↓</kbd><kbd>→</kbd> or <kbd>WASD</kbd> to steer &nbsp;•&nbsp; <kbd>Space</kbd> pause &nbsp;•&nbsp; Avoid walls &amp; your own wagons!</div>
    <div class="actions">
      <button class="btn white" id="pauseBtn">⏸ Pause</button>
      <button class="btn" id="demoBtn">🤖 Auto-drive: ON</button>
      <button class="btn white" id="resetBtn">↻ Restart</button>
    </div>
    <div class="dpad">
      <button class="up" data-d="up">▲</button><button class="left" data-d="left">◀</button><button class="down" data-d="down">▼</button><button class="right" data-d="right">▶</button>
    </div>
  </div>
</main>
<script>
const COLS=20, ROWS=14, CELL=40, W=COLS*CELL, H=ROWS*CELL;
const cvs=document.getElementById('game'), ctx=cvs.getContext('2d');
const scoreEl=document.getElementById('score'), lenEl=document.getElementById('len'), bonusEl=document.getElementById('bonus'), bestEl=document.getElementById('best');
const banner=document.getElementById('banner'), bannerText=document.getElementById('bannerText'), overlay=document.getElementById('overlay'), overText=document.getElementById('overText');
const demoBtn=document.getElementById('demoBtn');

const WAGON_COLORS=['#e63946','#2a9d8f','#f4a261','#4361ee','#9b5de5','#06d6a0','#ff8fa3','#ffd23f','#4cc9f0','#f3722c'];
const SHIRTS=['#e63946','#4361ee','#ffd23f','#2a9d8f','#9b5de5','#ff8fa3','#06d6a0','#f3722c'];

let snake, prevSnake, dir, dirQueue, passenger, bonusItem, score, bonusScore, best=0, speed, lastStep, paused=false, manual=false, gameOver=false, smoke=[], trees=[], flowers=[];
let passengerAnim=0;

function randCell(){ return {x:1+Math.floor(Math.random()*(COLS-2)), y:1+Math.floor(Math.random()*(ROWS-2))}; }
function occupied(p){ return snake.some(s=>s.x===p.x&&s.y===p.y) || (passenger&&passenger.x===p.x&&passenger.y===p.y) || (bonusItem&&bonusItem.x===p.x&&bonusItem.y===p.y); }
function spawnPassenger(){ let p; do{p=randCell()}while(occupied(p)); passenger=p; }
function spawnBonus(){ if(bonusItem) return; let p; do{p=randCell()}while(occupied(p)); bonusItem={...p, t:Date.now()}; }

function reset(){
  snake=[{x:9,y:7},{x:8,y:7},{x:7,y:7}];
  prevSnake=snake.map(s=>({...s}));
  dir={x:1,y:0}; dirQueue=[]; score=0; bonusScore=0; speed=170; lastStep=performance.now(); paused=false; gameOver=false; bonusItem=null; smoke=[];
  overlay.classList.remove('show'); spawnPassenger();
  if(Math.random()<0.6) spawnBonus();
  updateHUD();
}
function updateHUD(){ scoreEl.textContent=score; lenEl.textContent=snake?snake.length:3; bonusEl.textContent=bonusScore; bestEl.textContent=best; }

function setDir(x,y){
  const last = dirQueue.length?dirQueue[dirQueue.length-1]:dir;
  if(last.x===-x && last.y===-y) return;
  if(last.x===x && last.y===y) return;
  if(dirQueue.length<3) dirQueue.push({x,y});
}
function takeControl(){
  if(!manual){ manual=true; bannerText.textContent='🎮 You are driving! Use arrows / WASD — good luck, conductor!'; setTimeout(()=>banner.classList.add('hide'),1400); demoBtn.textContent='🤖 Auto-drive: OFF'; }
}

function step(){
  if(gameOver||paused) return;
  prevSnake=snake.map(s=>({...s}));
  if(dirQueue.length) dir=dirQueue.shift();

  // AI
  if(!manual){
    const nd=aiMove();
    if(nd) dir=nd;
  }

  const head={x:snake[0].x+dir.x, y:snake[0].y+dir.y};

  if(head.x<0||head.x>=COLS||head.y<0||head.y>=ROWS||snake.some(s=>s.x===head.x&&s.y===head.y)){
    gameOver=true; best=Math.max(best,score); updateHUD();
    if(manual){ overText.textContent=`You picked up ${score} passengers and ${bonusScore} bonus stars.`; overlay.classList.add('show'); }
    else { setTimeout(reset,900); }
    return;
  }

  snake.unshift(head);
  let ate=false;
  if(passenger && head.x===passenger.x && head.y===passenger.y){ ate=true; score++; speed=Math.max(85,speed-3); spawnPassenger(); burst(head.x,head.y,'#ffd23f'); }
  if(bonusItem && head.x===bonusItem.x && head.y===bonusItem.y){ ate=true; bonusScore++; score+=2; bonusItem=null; burst(head.x,head.y,'#ffe066'); setTimeout(spawnBonus,4000); }

  if(!ate) snake.pop();
  else prevSnake.push({...snake[snake.length-1]}); // grow smoothly
  updateHUD();
}

function aiMove(){
  // BFS to passenger
  const target=passenger; if(!target) return null;
  const key=(x,y)=>x+','+y;
  const blocked=new Set(snake.slice(0,-1).map(s=>key(s.x,s.y)));
  const q=[{...snake[0], first:null}]; const seen=new Set([key(snake[0].x,snake[0].y)]);
  const dirs=[{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}];
  while(q.length){
    const cur=q.shift();
    if(cur.x===target.x&&cur.y===target.y) return cur.first;
    for(const d of dirs){
      const nx=cur.x+d.x, ny=cur.y+d.y;
      if(nx<0||ny<0||nx>=COLS||ny>=ROWS) continue;
      if(blocked.has(key(nx,ny))||seen.has(key(nx,ny))) continue;
      // avoid reversing into neck unless it's first step logic already blocked
      if(cur===snake[0] && snake[1] && nx===snake[1].x && ny===snake[1].y) continue;
      seen.add(key(nx,ny));
      q.push({x:nx,y:ny,first:cur.first||d});
    }
  }
  // survival: any safe move
  const safe=dirs.filter(d=>{
    const nx=snake[0].x+d.x, ny=snake[0].y+d.y;
    if(nx<0||ny<0||nx>=COLS||ny>=ROWS) return false;
    if(snake.slice(0,-1).some(s=>s.x===nx&&s.y===ny)) return false;
    if(d.x===-dir.x&&d.y===-dir.y) return false;
    return true;
  });
  return safe.length?safe[0]:dir;
}

function burst(cx,cy,color){
  for(let i=0;i<10;i++) smoke.push({x:cx*CELL+20,y:cy*CELL+20,vx:(Math.random()-.5)*3,vy:-Math.random()*3-1,life:30,color,r:3+Math.random()*4});
}

// scenery generation
trees=[]; for(let i=0;i<14;i++) trees.push({x:Math.random()*W,y:Math.random()*H,s:.7+Math.random()*.6});
flowers=[]; for(let i=0;i<36;i++) flowers.push({x:Math.random()*W,y:Math.random()*H,c:['#fff','#ffd23f','#ff8fa3','#c77dff'][i%4]});

function draw(now){
  passengerAnim=now/250;
  const t=Math.max(0,Math.min(1,(now-lastStep)/speed));
  // grass
  for(let y=0;y<ROWS;y++) for(let x=0;x<COLS;x++){ ctx.fillStyle=(x+y)%2? '#7cc94f':'#72c147'; ctx.fillRect(x*CELL,y*CELL,CELL,CELL); }
  // tracks - two horizontal lines
  ctx.strokeStyle='rgba(90,60,30,.35)'; ctx.lineWidth=3;
  [3.5,10.5].forEach(r=>{ ctx.beginPath(); ctx.moveTo(0,r*CELL); ctx.lineTo(W,r*CELL); ctx.stroke(); });
  ctx.strokeStyle='#5b4636'; ctx.lineWidth=2;
  [3.5,10.5].forEach(r=>{ ctx.beginPath(); ctx.moveTo(0,r*CELL-5); ctx.lineTo(W,r*CELL-5); ctx.moveTo(0,r*CELL+5); ctx.lineTo(W,r*CELL+5); ctx.stroke(); });
  ctx.lineWidth=2; ctx.strokeStyle='rgba(90,60,30,.5)';
  for(let x=10;x<W;x+=28){ ctx.beginPath(); ctx.moveTo(x,3.5*CELL-6); ctx.lineTo(x,3.5*CELL+6); ctx.moveTo(x,10.5*CELL-6); ctx.lineTo(x,10.5*CELL+6); ctx.stroke(); }

  // flowers
  flowers.forEach(f=>{ ctx.fillStyle=f.c; ctx.beginPath(); ctx.arc(f.x,f.y,3,0,7); ctx.fill(); });
  // trees (behind train, clipped at edges ok)
  trees.forEach(tr=>{
    ctx.fillStyle='#8b5a2b'; ctx.fillRect(tr.x-3,tr.y+6*tr.s,6,12*tr.s);
    ctx.fillStyle='#2f9e44'; ctx.beginPath(); ctx.arc(tr.x,tr.y,13*tr.s,0,7); ctx.arc(tr.x-9*tr.s,tr.y+6*tr.s,9*tr.s,0,7); ctx.arc(tr.x+9*tr.s,tr.y+6*tr.s,9*tr.s,0,7); ctx.fill();
  });

  // border fence / station edge
  ctx.strokeStyle='#19324d'; ctx.lineWidth=6; ctx.strokeRect(3,3,W-6,H-6);

  // passenger platform
  if(passenger){
    const px=passenger.x*CELL, py=passenger.y*CELL;
    ctx.fillStyle='#f2d492'; roundRect(px+2,py+16,CELL-4,22,6); ctx.fill();
    ctx.strokeStyle='#b8893a'; ctx.lineWidth=2; roundRect(px+2,py+16,CELL-4,22,6); ctx.stroke();
    // sign
    ctx.fillStyle='#fff'; ctx.fillRect(px+13,py+3,14,13); ctx.strokeStyle='#19324d'; ctx.lineWidth=1.5; ctx.strokeRect(px+13,py+3,14,13);
    ctx.fillStyle='#e63946'; ctx.font='bold 10px sans-serif'; ctx.textAlign='center'; ctx.fillText('PLATFORM',px+20,py+12);
    // person
    const bob=Math.sin(passengerAnim)*2, cx=px+20, cy=py+18+bob;
    ctx.fillStyle='#ffdbac'; ctx.beginPath(); ctx.arc(cx,cy-6,5,0,7); ctx.fill();
    ctx.fillStyle=SHIRTS[score%SHIRTS.length]; roundRect(cx-5,cy-1,10,11,3); ctx.fill();
    ctx.strokeStyle='#ffdbac'; ctx.lineWidth=3; ctx.beginPath(); ctx.moveTo(cx+5,cy+2); ctx.lineTo(cx+9,cy-3+Math.sin(passengerAnim*2)*2); ctx.stroke();
    ctx.fillStyle='#19324d'; ctx.fillRect(cx-4,cy+10,3,6); ctx.fillRect(cx+1,cy+10,3,6);
    // speech bubble
    ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(cx+11,cy-14,7,0,7); ctx.fill(); ctx.fillStyle='#e63946'; ctx.font='bold 11px sans-serif'; ctx.fillText('!',cx+11,cy-10);
  }

  // bonus star
  if(bonusItem){
    const cx=bonusItem.x*CELL+20, cy=bonusItem.y*CELL+20+Math.sin(now/200)*3;
    ctx.fillStyle='#ffd23f'; ctx.strokeStyle='#e6a800'; ctx.lineWidth=2;
    ctx.beginPath();
    for(let i=0;i<10;i++){ const r=i%2?6:13, a=-Math.PI/2+i*Math.PI/5; ctx[i?'lineTo':'moveTo'](cx+Math.cos(a)*r,cy+Math.sin(a)*r); }
    ctx.closePath(); ctx.fill(); ctx.stroke();
  }

  // smoke particles
  smoke=smoke.filter(p=>p.life>0);
  smoke.forEach(p=>{ p.x+=p.vx; p.y+=p.vy; p.vy-=.02; p.life--; ctx.fillStyle=p.color; ctx.globalAlpha=Math.max(0,p.life/30)*.8; ctx.beginPath(); ctx.arc(p.x,p.y,p.r,0,7); ctx.fill(); ctx.globalAlpha=1; });

  // train - draw wagons first (tail to head)
  const interp=snake.map((s,i)=>{
    const pr=prevSnake[Math.min(i,prevSnake.length-1)]||s;
    return {x:(pr.x+(s.x-pr.x)*t)*CELL, y:(pr.y+(s.y-pr.y)*t)*CELL};
  });
  for(let i=interp.length-1;i>=1;i--){
    const p=interp[i], col=WAGON_COLORS[(i-1)%WAGON_COLORS.length];
    ctx.fillStyle='rgba(0,0,0,.18)'; roundRect(p.x+4,p.y+24,32,9,4); ctx.fill();
    ctx.fillStyle=col; roundRect(p.x+3,p.y+9,34,22,5); ctx.fill();
    ctx.strokeStyle='#19324d'; ctx.lineWidth=2; roundRect(p.x+3,p.y+9,34,22,5); ctx.stroke();
    ctx.fillStyle='#fff'; ctx.fillRect(p.x+7,p.y+13,7,8); ctx.fillRect(p.x+17,p.y+13,7,8); ctx.fillRect(p.x+27,p.y+13,7,8);
    ctx.fillStyle='#19324d'; ctx.beginPath(); ctx.arc(p.x+10,p.y+33,4,0,7); ctx.arc(p.x+30,p.y+33,4,0,7); ctx.fill();
    // tiny passenger face in some wagons
    if(i%3===0){ ctx.fillStyle='#ffdbac'; ctx.beginPath(); ctx.arc(p.x+20,p.y+17,3,0,7); ctx.fill(); }
  }
  // locomotive
  const h=interp[0];
  const horiz=dir.x!==0;
  ctx.fillStyle='rgba(0,0,0,.2)'; roundRect(h.x+2,h.y+26,36,9,4); ctx.fill();
  ctx.fillStyle='#c1121f'; roundRect(h.x+2,h.y+8,36,24,6); ctx.fill();
  ctx.strokeStyle='#19324d'; ctx.lineWidth=2; roundRect(h.x+2,h.y+8,36,24,6); ctx.stroke();
  ctx.fillStyle='#ffd23f'; ctx.fillRect(h.x+5,h.y+11,7,7);
  ctx.fillStyle='#19324d'; ctx.fillRect(h.x+26,h.y+10,8,12); // cabin
  ctx.fillStyle='#a8dadc'; ctx.fillRect(h.x+28,h.y+12,4,5);
  ctx.fillStyle='#555'; ctx.fillRect(h.x+14,h.y+2,7,8); ctx.fillStyle='#333'; ctx.fillRect(h.x+12,h.y,11,4); // chimney
  if(Math.random()<.18) smoke.push({x:h.x+17,y:h.y+2,vx:.3,vy:-1.2,life:36,color:'#eaeaea',r:4+Math.random()*3});
  ctx.fillStyle='#19324d'; ctx.beginPath(); ctx.arc(h.x+10,h.y+34,5,0,7); ctx.arc(h.x+20,h.y+34,5,0,7); ctx.arc(h.x+30,h.y+34,5,0,7); ctx.fill();
  ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(h.x+10,h.y+34,2,0,7); ctx.arc(h.x+20,h.y+34,2,0,7); ctx.arc(h.x+30,h.y+34,2,0,7); ctx.fill();
  // face on loco
  ctx.fillStyle='#ffdbac'; ctx.beginPath(); ctx.arc(h.x+13,h.y+18,4.5,0,7); ctx.fill();
  ctx.fillStyle='#19324d'; ctx.beginPath(); ctx.arc(h.x+11.5,h.y+17,1,0,7); ctx.arc(h.x+14.5,h.y+17,1,0,7); ctx.fill();

  // vignette text
  ctx.fillStyle='rgba(255,255,255,.92)'; roundRect(10,10,196,30,8); ctx.fill(); ctx.strokeStyle='#19324d'; ctx.lineWidth=2; roundRect(10,10,196,30,8); ctx.stroke();
  ctx.fillStyle='#19324d'; ctx.font='900 13px sans-serif'; ctx.textAlign='left'; ctx.fillText(manual?'🎮 MANUAL DRIVING':'🤖 AUTO DEMO  •  SUNNY STATION',18,30);

  requestAnimationFrame(draw);
}
function roundRect(x,y,w,h,r){ ctx.beginPath(); ctx.moveTo(x+r,y); ctx.arcTo(x+w,y,x+w,y+h,r); ctx.arcTo(x+w,y+h,x,y+h,r); ctx.arcTo(x,y+h,x,y,r); ctx.arcTo(x,y,x+w,y,r); ctx.closePath(); }

setInterval(()=>{ if(!paused && !gameOver && performance.now()-lastStep>=speed){ step(); lastStep=performance.now(); } },30);

const DIRS={ArrowUp:[0,-1],KeyW:[0,-1],ArrowDown:[0,1],KeyS:[0,1],ArrowLeft:[-1,0],KeyA:[-1,0],ArrowRight:[1,0],KeyD:[1,0]};
window.addEventListener('keydown',e=>{
  if(e.code==='Space'){ e.preventDefault(); togglePause(); return; }
  if(DIRS[e.code]){ e.preventDefault(); takeControl(); setDir(...DIRS[e.code]); }
});
cvs.addEventListener('pointerdown',()=>{ takeControl(); });
document.getElementById('frame').addEventListener('pointerdown',()=>takeControl());
document.querySelectorAll('.dpad button').forEach(b=>b.addEventListener('click',()=>{ takeControl(); const d=b.dataset.d; const map={up:[0,-1],down:[0,1],left:[-1,0],right:[1,0]}; setDir(...map[d]); }));
function togglePause(){ if(gameOver) return; paused=!paused; lastStep=performance.now(); document.getElementById('pauseBtn').textContent=paused?'▶ Resume':'⏸ Pause'; }
document.getElementById('pauseBtn').addEventListener('click',togglePause);
document.getElementById('resetBtn').addEventListener('click',reset);
document.getElementById('againBtn').addEventListener('click',reset);
demoBtn.addEventListener('click',()=>{
  manual=!manual;
  demoBtn.textContent=manual?'🤖 Auto-drive: OFF':'🤖 Auto-drive: ON';
  bannerText.textContent=manual?'🎮 You are driving!':'🤖 Demo train is playing — click / press a key to drive!';
  banner.classList.toggle('hide',manual);
  if(!manual) banner.classList.remove('hide');
});

reset();
requestAnimationFrame(draw);
</script>
</body>
</html>
Reasoning trace (1,351 tokens)
Designing a single-file canvas-based snake game with a train and wagons theme, 1280x800 responsive layout, and self-playing AI demo mode.Adding passenger pickup visuals, track and platform layout, and BFS-based AI with manual control fallback.