Train snakeOne-line
GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 2 min 33 s
- Cost
- $0.068
- Output tokens
- 13,542
- Spent thinking
- 4,076 tokens
- Speed
- 96.1 tok/s
- Console errors
- 0
- First token after
- 12.4 s
- First visible text after
- 1 min 14 s
- Prompt tokens
- 361
- Code size
- 26.7 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"high"}- 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 · 573 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Little Train</title>
<style>
:root {
--ink: #263d34;
--muted: #77867a;
--cream: #f7f5eb;
--line: #e3e5d8;
--orange: #dc7955;
--green: #587b60;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: #f2f1e6;
color: var(--ink);
font-family: Arial, Helvetica, sans-serif;
background-image: radial-gradient(#d9ddce 0.7px, transparent 0.7px);
background-size: 17px 17px;
}
button { font: inherit; }
.page { max-width: 1190px; margin: 0 auto; padding: 24px 18px 30px; }
.top {
display: flex; align-items: center; justify-content: space-between;
gap: 20px; margin-bottom: 22px;
}
.brand { display: flex; align-items: center; gap: 14px; }
.brand-icon {
width: 51px; height: 51px; flex: none; border-radius: 15px;
background: #d77d5c; display: grid; place-items: center;
box-shadow: inset 0 -3px 0 #bd684c;
}
.eyebrow {
font-size: 9px; font-weight: 800; letter-spacing: 2px;
text-transform: uppercase; color: #829083; margin-bottom: 4px;
}
h1 {
font-family: Georgia, 'Times New Roman', serif; font-size: 34px;
line-height: 1; letter-spacing: -1.3px; margin: 0; font-weight: 700;
}
.top-note {
display: flex; align-items: center; gap: 12px;
color: #829083; font-size: 10px; font-weight: 800; letter-spacing: 1.5px;
text-transform: uppercase; text-align: right;
}
.top-note .sun {
display: grid; place-items: center; width: 38px; height: 38px;
border: 1px solid #d9dfd0; border-radius: 50%; color: #d59860; font-size: 20px;
}
.layout { display: grid; grid-template-columns: minmax(0, 1fr) 275px; gap: 20px; align-items: stretch; }
.game-card {
min-width: 0; background: #fffef8; border: 1px solid #e2e5d9;
border-radius: 18px; padding: 0 9px 9px;
box-shadow: 0 6px 20px rgba(68, 84, 63, .07);
}
.game-top {
height: 67px; display: flex; align-items: center; justify-content: space-between;
gap: 12px; padding: 0 12px;
}
.route { display: flex; align-items: center; gap: 10px; }
.route-icon {
width: 32px; height: 32px; border-radius: 10px; display: grid;
place-items: center; background: #e8efe3; color: #64836b;
}
.route-title { font-size: 11px; letter-spacing: 1.2px; font-weight: 800; }
.route-sub { margin-top: 4px; font-size: 11px; color: #9aa59a; }
.stats { display: flex; align-items: center; gap: 18px; }
.stat { display: flex; align-items: center; gap: 8px; }
.stat + .stat { border-left: 1px solid #e9ebe2; padding-left: 18px; }
.stat-label { color: #9aa69a; font-size: 9px; line-height: 1.3; font-weight: 800; letter-spacing: 1px; }
.stat-value { font-family: Georgia, serif; font-weight: bold; font-size: 24px; line-height: 1; color: #405c47; }
.playfield {
position: relative; overflow: hidden; border-radius: 12px;
background: #d7e5c7; line-height: 0;
}
canvas { width: 100%; height: auto; display: block; cursor: pointer; touch-action: manipulation; }
.start-hint {
position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
display: flex; align-items: center; gap: 11px; white-space: nowrap;
background: #fffef6; border: 1px solid rgba(75, 105, 77, .12);
border-radius: 12px; padding: 10px 15px; line-height: 1.2;
box-shadow: 0 5px 16px rgba(48, 79, 49, .16); cursor: pointer;
}
.hint-icon {
width: 30px; height: 30px; display: grid; place-items: center; flex: none;
border-radius: 9px; background: #e7efe1; color: #668967; font-size: 14px;
}
.hint-small { color: #d18462; font-size: 9px; font-weight: 800; letter-spacing: 1.1px; margin-bottom: 3px; }
.hint-main { color: #405746; font-size: 12px; font-weight: 700; }
.end-overlay {
position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
background: rgba(37, 60, 43, .34); line-height: normal; padding: 20px;
}
.end-overlay.show { display: flex; }
.end-card {
background: #fffef6; border-radius: 18px; padding: 25px 32px;
text-align: center; box-shadow: 0 12px 40px #38593c44;
}
.end-card h2 { font: bold 27px Georgia, serif; margin: 5px 0 8px; }
.end-card p { font-size: 13px; color: #829083; margin: 0 0 16px; }
.primary {
border: 0; border-radius: 9px; background: #d77c5a; color: white;
padding: 10px 18px; cursor: pointer; font-size: 12px; font-weight: bold;
}
.sidebar {
background: #fffef8; border: 1px solid #e2e5d9; border-radius: 18px;
padding: 22px; box-shadow: 0 6px 20px rgba(68, 84, 63, .07);
display: flex; flex-direction: column;
}
.side-kicker { color: #d38463; font-weight: 800; font-size: 9px; letter-spacing: 1.6px; }
.sidebar h2 { font: bold 25px/1.1 Georgia, serif; letter-spacing: -.6px; margin: 8px 0 9px; }
.intro { color: #849286; font-size: 12px; line-height: 1.55; margin: 0; }
.ticket {
margin: 16px 0 18px; background: #f0f3e7; border-radius: 11px;
padding: 11px 12px; display: flex; align-items: center; justify-content: space-between;
position: relative; overflow: hidden;
}
.ticket:after {
content: ""; position: absolute; right: 70px; top: 0; bottom: 0;
border-left: 1px dashed #ccd8c6;
}
.ticket-label { color: #91a08f; font-size: 8px; letter-spacing: 1.2px; font-weight: 800; }
.ticket-title { margin-top: 4px; color: #5d7860; font: bold 15px Georgia, serif; }
.faces { display: flex; padding-right: 2px; }
.face {
width: 25px; height: 25px; border-radius: 50%; border: 2px solid #f0f3e7;
margin-left: -6px; display: grid; place-items: center;
}
.face svg { width: 17px; height: 17px; }
.f1 { background: #e5ad8a; } .f2 { background: #e5c56f; } .f3 { background: #9dbdc3; }
.section-title {
font-size: 9px; letter-spacing: 1.5px; color: #9aa69a; font-weight: 800;
border-top: 1px solid #edf0e7; padding-top: 16px; margin-bottom: 13px;
}
.instruction { display: flex; gap: 11px; margin-bottom: 13px; align-items: flex-start; }
.number {
width: 23px; height: 23px; border-radius: 7px; background: #f2f4ec;
color: #98a895; display: grid; place-items: center; flex: none;
font-size: 9px; font-weight: bold;
}
.instruction strong { display: block; font-size: 11px; margin: 1px 0 3px; }
.instruction span { display: block; font-size: 11px; color: #98a397; line-height: 1.35; }
.control-area {
border-top: 1px solid #edf0e7; margin-top: auto; padding-top: 15px;
display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
.control-copy { font-size: 9px; line-height: 1.6; font-weight: 800; color: #9aa69a; letter-spacing: 1px; }
.control-copy b { display: block; color: #657c69; font-size: 11px; letter-spacing: .2px; }
.dpad { display: grid; grid-template-columns: repeat(3, 28px); grid-template-rows: repeat(2, 26px); gap: 3px; }
.dpad button {
border: 1px solid #e4e9df; background: #f7f8f2; border-radius: 6px;
color: #617b66; font-size: 14px; font-weight: bold; cursor: pointer;
padding: 0; touch-action: manipulation;
}
.dpad button:hover, .dpad button:active { background: #e2ecdd; }
.dpad .up { grid-column: 2; }
.dpad .left { grid-column: 1; grid-row: 2; }
.dpad .down { grid-column: 2; grid-row: 2; }
.dpad .right { grid-column: 3; grid-row: 2; }
.side-bottom {
display: flex; justify-content: space-between; align-items: center;
margin-top: 15px; padding-top: 13px; border-top: 1px solid #edf0e7;
}
.status { color: #7d9780; font-size: 9px; font-weight: 800; letter-spacing: 1px; }
.status:before {
content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 6px;
border-radius: 50%; background: #8db993;
}
.pause {
border: 0; background: none; padding: 2px 0; color: #9da99c;
font-size: 10px; font-weight: bold; cursor: pointer;
}
.footer {
display: flex; justify-content: space-between; margin: 12px 4px 0;
font-size: 9px; font-weight: bold; color: #a2afa1; letter-spacing: 1.1px;
text-transform: uppercase;
}
@media (max-width: 820px) {
.page { max-width: 650px; padding: 18px 14px 25px; }
.layout { grid-template-columns: 1fr; }
.sidebar { min-height: 0; }
.control-area { margin-top: 4px; }
.top-note { font-size: 9px; }
}
@media (max-width: 470px) {
h1 { font-size: 28px; }
.brand-icon { width: 43px; height: 43px; border-radius: 12px; }
.top-note { display: none; }
.top { margin-bottom: 16px; }
.game-top { height: 58px; padding: 0 5px; }
.route-icon { display: none; }
.route-title { font-size: 9px; }
.route-sub { font-size: 9px; }
.stats { gap: 8px; }
.stat + .stat { padding-left: 8px; }
.stat-label { font-size: 7px; }
.stat-value { font-size: 19px; }
.start-hint { bottom: 8px; padding: 6px 9px; gap: 7px; }
.hint-icon { width: 25px; height: 25px; }
.hint-small { font-size: 8px; }
.hint-main { font-size: 10px; }
.sidebar { padding: 19px; }
}
</style>
</head>
<body>
<div class="page">
<header class="top">
<div class="brand">
<div class="brand-icon" aria-hidden="true">
<svg width="31" height="31" viewBox="0 0 32 32" fill="none">
<path d="M5 22h22v3H5zM8 20V9h8v11M16 13h7l3 7H16z" fill="#fff9e9"/>
<path d="M10 11h4v4h-4z" fill="#D77D5C"/><path d="M9 6h6v3H9zM19 9h3v4h-3z" fill="#fff9e9"/>
<circle cx="10" cy="25" r="2.5" fill="#fff9e9"/><circle cx="22" cy="25" r="2.5" fill="#fff9e9"/>
</svg>
</div>
<div><div class="eyebrow">A little game for a lovely day</div><h1>The Little Train</h1></div>
</div>
<div class="top-note"><span>Take the scenic route<br>and enjoy the ride</span><span class="sun">✳</span></div>
</header>
<main class="layout">
<section class="game-card" aria-label="Snake game">
<div class="game-top">
<div class="route">
<div class="route-icon">
<svg width="18" height="18" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M4 15c2-4 4-2 6-6s4-3 6-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-dasharray="2 2"/><circle cx="4" cy="15" r="2" fill="currentColor"/><circle cx="16" cy="5" r="2" fill="currentColor"/></svg>
</div>
<div><div class="route-title">THE MEADOW LINE</div><div class="route-sub">A passenger-picking adventure</div></div>
</div>
<div class="stats">
<div class="stat"><div class="stat-label">PASSENGERS<br>ON BOARD</div><div class="stat-value" id="score">00</div></div>
<div class="stat"><div class="stat-label">TRAIN<br>LENGTH</div><div class="stat-value" id="length">06</div></div>
</div>
</div>
<div class="playfield" id="playfield">
<canvas id="game" width="920" height="598" aria-label="Train moving around a meadow to pick up passengers"></canvas>
<div class="start-hint" id="hint">
<div class="hint-icon">▶</div>
<div><div class="hint-small">DEMO RIDE IN PROGRESS</div><div class="hint-main">Click or press a key to start</div></div>
</div>
<div class="end-overlay" id="endOverlay">
<div class="end-card">
<div class="side-kicker">END OF THE LINE</div>
<h2>What a lovely ride!</h2>
<p id="endText">You picked up 0 passengers.</p>
<button class="primary" id="restart">Ride again →</button>
</div>
</div>
</div>
</section>
<aside class="sidebar">
<div class="side-kicker">✳ ALL ABOARD</div>
<h2>A little trip,<br>a lot of joy.</h2>
<p class="intro">There are friends waiting at the platform. Let's give them a ride!</p>
<div class="ticket">
<div><div class="ticket-label">YOUR DESTINATION</div><div class="ticket-title">Somewhere lovely</div></div>
<div class="faces" aria-hidden="true">
<div class="face f1"><svg viewBox="0 0 20 20"><circle cx="10" cy="8" r="4" fill="#f7dbba"/><path d="M4 18c0-4 2-6 6-6s6 2 6 6" fill="#cc705c"/><path d="M6 7c0-5 8-6 8 0-2-1-5-2-8 0" fill="#614b3f"/></svg></div>
<div class="face f2"><svg viewBox="0 0 20 20"><circle cx="10" cy="8" r="4" fill="#bd825c"/><path d="M4 18c0-4 2-6 6-6s6 2 6 6" fill="#769c86"/><path d="M6 7c0-5 8-6 8 0-2-1-5-2-8 0" fill="#443b35"/></svg></div>
<div class="face f3"><svg viewBox="0 0 20 20"><circle cx="10" cy="8" r="4" fill="#f2c9a4"/><path d="M4 18c0-4 2-6 6-6s6 2 6 6" fill="#d48869"/><path d="M6 7c0-5 8-6 8 0-2-1-5-2-8 0" fill="#725342"/></svg></div>
</div>
</div>
<div class="section-title">HOW TO PLAY</div>
<div class="instruction"><div class="number">01</div><div><strong>Take the controls</strong><span>Steer your train around the meadow.</span></div></div>
<div class="instruction"><div class="number">02</div><div><strong>Pick up passengers</strong><span>Each new friend adds a colorful wagon.</span></div></div>
<div class="instruction"><div class="number">03</div><div><strong>Mind the tracks</strong><span>Avoid the edges and your own train!</span></div></div>
<div class="control-area">
<div class="control-copy">TO STEER<b>Arrow keys or WASD</b>OR TAP THE BUTTONS</div>
<div class="dpad" aria-label="Direction controls">
<button class="up" data-dir="up" aria-label="Up">↑</button>
<button class="left" data-dir="left" aria-label="Left">←</button>
<button class="down" data-dir="down" aria-label="Down">↓</button>
<button class="right" data-dir="right" aria-label="Right">→</button>
</div>
</div>
<div class="side-bottom">
<div class="status" id="status">AUTOPILOT ON</div>
<button class="pause" id="pause">Pause</button>
</div>
</aside>
</main>
<div class="footer"><span>Made for slow journeys & happy hearts</span><span>THE LITTLE TRAIN ✳</span></div>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const lengthEl = document.getElementById('length');
const hint = document.getElementById('hint');
const status = document.getElementById('status');
const pauseButton = document.getElementById('pause');
const endOverlay = document.getElementById('endOverlay');
const endText = document.getElementById('endText');
const cols = 20, rows = 13, size = 46, interval = 220;
const directions = { up: {x:0,y:-1}, down: {x:0,y:1}, left: {x:-1,y:0}, right: {x:1,y:0} };
const colors = ['#e2ac55', '#6e9fa2', '#d77e69', '#a795b0', '#8eae7d', '#d6a064', '#6f9da2'];
let snake, previous, dir, queued, food, score, started = false, paused = false, over = false, lastStep = performance.now(), demoReset = 0;
function reset() {
snake = [{x:6,y:8},{x:5,y:8},{x:4,y:8},{x:3,y:8},{x:2,y:8},{x:1,y:8}];
previous = snake.map(p => ({...p}));
dir = {x:1,y:0}; queued = null; food = {x:16,y:3}; score = 0;
paused = false; over = false; demoReset = 0; lastStep = performance.now();
endOverlay.classList.remove('show');
pauseButton.textContent = 'Pause';
updateStats();
}
function updateStats() {
scoreEl.textContent = String(score).padStart(2, '0');
lengthEl.textContent = String(snake.length).padStart(2, '0');
}
function start() {
if (started) return;
started = true;
reset();
hint.style.display = 'none';
status.textContent = 'YOUR TURN';
}
function spawnFood() {
let choices = [];
for (let y = 2; y < rows - 2; y++) for (let x = 2; x < cols - 2; x++) {
if (!snake.some(p => p.x === x && p.y === y) && Math.abs(x-snake[0].x) + Math.abs(y-snake[0].y) > 3) choices.push({x,y});
}
if (!choices.length) {
for (let y = 1; y < rows-1; y++) for (let x = 1; x < cols-1; x++)
if (!snake.some(p => p.x === x && p.y === y)) choices.push({x,y});
}
food = choices.length ? choices[Math.floor(Math.random() * choices.length)] : null;
}
function autoDirection() {
if (!food) return dir;
const blocked = new Set(snake.slice(0, -1).map(p => p.x + ',' + p.y));
const head = snake[0], queue = [{x:head.x,y:head.y,first:null}];
const visited = new Set([head.x + ',' + head.y]);
const options = [{x:1,y:0},{x:0,y:-1},{x:0,y:1},{x:-1,y:0}];
for (let i = 0; i < queue.length; i++) {
const p = queue[i];
for (const d of options) {
if (i === 0 && d.x === -dir.x && d.y === -dir.y) continue;
const x = p.x+d.x, y = p.y+d.y, key = x+','+y;
if (x < 0 || y < 0 || x >= cols || y >= rows || blocked.has(key) || visited.has(key)) continue;
const first = p.first || d;
if (x === food.x && y === food.y) return first;
visited.add(key); queue.push({x,y,first});
}
}
const safe = options.filter(d => {
const x=head.x+d.x, y=head.y+d.y;
return !(d.x === -dir.x && d.y === -dir.y) && x>=0 && x<cols && y>=0 && y<rows && !blocked.has(x+','+y);
});
return safe[0] || dir;
}
function step() {
if (over || paused) return;
if (!started) dir = autoDirection();
else if (queued) { dir = queued; queued = null; }
const next = {x:snake[0].x+dir.x, y:snake[0].y+dir.y};
const eating = food && next.x === food.x && next.y === food.y;
const collision = next.x < 0 || next.y < 0 || next.x >= cols || next.y >= rows ||
snake.slice(0, eating ? snake.length : -1).some(p => p.x === next.x && p.y === next.y);
if (collision) {
over = true;
if (started) {
endText.textContent = 'You picked up ' + score + (score === 1 ? ' passenger.' : ' passengers.');
endOverlay.classList.add('show');
status.textContent = 'JOURNEY COMPLETE';
} else demoReset = performance.now() + 700;
return;
}
previous = snake.map(p => ({...p}));
snake.unshift(next);
if (eating) { score++; spawnFood(); } else snake.pop();
updateStats();
}
function roundRect(x,y,w,h,r,fill,stroke) {
ctx.beginPath(); ctx.roundRect(x,y,w,h,r);
if (fill) { ctx.fillStyle=fill; ctx.fill(); }
if (stroke) { ctx.strokeStyle=stroke; ctx.stroke(); }
}
function circle(x,y,r,color) {
ctx.beginPath(); ctx.arc(x,y,r,0,Math.PI*2); ctx.fillStyle=color; ctx.fill();
}
function drawGround() {
ctx.fillStyle = '#d8e7cb'; ctx.fillRect(0,0,920,598);
// The little dotted grid gives the meadow a board-game feel.
ctx.strokeStyle = 'rgba(113,151,111,.16)'; ctx.lineWidth = 1;
ctx.setLineDash([2,6]);
for (let x=0; x<=920; x+=size) { ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,598); ctx.stroke(); }
for (let y=0; y<=598; y+=size) { ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(920,y); ctx.stroke(); }
ctx.setLineDash([]);
const patches = [[85,95,57,27],[316,74,65,23],[687,88,62,26],[813,258,48,25],[335,320,60,25],[127,490,65,29],[667,481,65,27],[488,544,50,22]];
ctx.fillStyle='rgba(191,215,176,.38)';
patches.forEach(p => {ctx.beginPath();ctx.ellipse(p[0],p[1],p[2],p[3],0,0,Math.PI*2);ctx.fill();});
const flowers = [[61,175],[155,235],[238,70],[326,190],[441,96],[548,244],[672,177],[810,141],[850,389],[751,548],[559,405],[450,490],[245,540],[66,364],[360,410],[716,350],[116,300],[597,76],[285,260],[890,76]];
flowers.forEach((p,i) => {
const x=p[0],y=p[1], c=i%3===0?'#edb6a1':i%3===1?'#fff8dc':'#efd585';
circle(x-3,y,2.4,c); circle(x+3,y,2.4,c); circle(x,y-3,2.4,c); circle(x,y+3,2.4,c); circle(x,y,1.7,'#d3ad64');
});
drawBush(69,70,1); drawBush(846,65,.9); drawBush(73,547,.9); drawBush(865,532,1);
// A tiny mile marker.
roundRect(26, 246, 45, 23, 5, '#f8f3dd', '#c2d1b7');
ctx.fillStyle='#829d7c'; ctx.font='bold 8px Arial'; ctx.textAlign='center'; ctx.fillText('01',48.5,261);
ctx.textAlign='left';
}
function drawBush(x,y,s) {
ctx.save(); ctx.translate(x,y); ctx.scale(s,s);
ctx.fillStyle='rgba(73,116,76,.10)'; ctx.beginPath();ctx.ellipse(0,17,32,12,0,0,Math.PI*2);ctx.fill();
circle(-17,2,15,'#a0c398'); circle(2,-6,20,'#94b98b'); circle(19,4,15,'#a4c89c');
circle(-6,-10,5,'rgba(224,241,209,.35)'); circle(15,0,4,'rgba(224,241,209,.35)');
ctx.restore();
}
function drawPassenger(x,y,shirt,skin,hair,scale) {
ctx.save(); ctx.translate(x,y); ctx.scale(scale,scale);
ctx.fillStyle='rgba(94,74,47,.17)'; ctx.beginPath(); ctx.ellipse(0,13,11,4,0,0,Math.PI*2); ctx.fill();
roundRect(-7,0,14,14,5,shirt);
circle(-7,6,2.5,skin); circle(7,6,2.5,skin);
circle(0,-4,6,skin);
ctx.fillStyle=hair; ctx.beginPath(); ctx.arc(0,-5,6,Math.PI,Math.PI*2); ctx.lineTo(6,-4); ctx.quadraticCurveTo(2,-6,-1,-7); ctx.quadraticCurveTo(-4,-4,-6,-4); ctx.fill();
circle(-2,-4,0.7,'#4b4038'); circle(2,-4,0.7,'#4b4038');
ctx.restore();
}
function drawPlatform(time) {
if (!food) return;
const x=(food.x+.5)*size, y=(food.y+.5)*size;
ctx.save();
ctx.fillStyle='rgba(79,111,73,.16)'; ctx.beginPath();ctx.ellipse(x,y+20,59,29,0,0,Math.PI*2);ctx.fill();
roundRect(x-52,y-32,104,62,9,'#ecddba','#d0bd93');
roundRect(x-48,y-28,96,52,6,'#f5e9cb');
ctx.fillStyle='#dfcda6'; ctx.fillRect(x-48,y+17,96,7);
ctx.strokeStyle='#fff8e8'; ctx.lineWidth=2; ctx.setLineDash([5,5]);
ctx.beginPath();ctx.moveTo(x-44,y+20);ctx.lineTo(x+44,y+20);ctx.stroke();ctx.setLineDash([]);
// A little station sign and two waiting friends.
roundRect(x-46,y-43,52,17,4,'#bd7359');
ctx.fillStyle='#fff8e9'; ctx.font='bold 7px Arial'; ctx.textAlign='center'; ctx.fillText('PLATFORM',x-20,y-32);
ctx.textAlign='left';
drawPassenger(x-13,y+1,'#729c95','#eab995','#654b3e',.85);
drawPassenger(x+12,y+1,'#d8816c','#f0caaa','#5a493e',1);
roundRect(x+30,y+5,9,10,2,'#d8ad62');
ctx.strokeStyle='#b1844f';ctx.lineWidth=1;ctx.beginPath();ctx.arc(x+34.5,y+5,2.5,Math.PI,0);ctx.stroke();
const pulse = .5 + .5*Math.sin(time/450);
ctx.strokeStyle='rgba(255,250,217,'+(0.35+pulse*.35)+')';ctx.lineWidth=2;
ctx.beginPath();ctx.ellipse(x,y,58+pulse*3,36+pulse*2,0,0,Math.PI*2);ctx.stroke();
ctx.restore();
}
function drawTracks(points) {
for (let i=0;i<points.length-1;i++) {
const a=points[i], b=points[i+1], dx=b.x-a.x, dy=b.y-a.y, len=Math.hypot(dx,dy)||1;
const nx=-dy/len, ny=dx/len;
ctx.strokeStyle='rgba(101,139,102,.26)';ctx.lineWidth=21;ctx.lineCap='round';
ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke();
for (let j=0;j<=4;j++) {
const t=j/4, x=a.x+dx*t,y=a.y+dy*t;
ctx.strokeStyle='rgba(135,114,83,.35)';ctx.lineWidth=3;
ctx.beginPath();ctx.moveTo(x+nx*9,y+ny*9);ctx.lineTo(x-nx*9,y-ny*9);ctx.stroke();
}
ctx.strokeStyle='rgba(246,243,217,.65)';ctx.lineWidth=2;
[-6,6].forEach(offset=>{ctx.beginPath();ctx.moveTo(a.x+nx*offset,a.y+ny*offset);ctx.lineTo(b.x+nx*offset,b.y+ny*offset);ctx.stroke();});
}
ctx.lineCap='butt';
}
function drawCar(x,y,angle,index) {
ctx.save(); ctx.translate(x,y); ctx.rotate(angle);
ctx.fillStyle='rgba(52,76,50,.18)';ctx.beginPath();ctx.ellipse(0,5,21,15,0,0,Math.PI*2);ctx.fill();
// Wheels peek out on both sides.
[-11,10].forEach(wx => {
roundRect(wx-4,-17,8,7,3,'#43574a');
roundRect(wx-4,10,8,7,3,'#43574a');
});
if (index === 0) {
roundRect(-20,-14,37,28,7,'#b95e49');
roundRect(-18,-12,34,24,5,'#db785a');
// Golden front and headlamp.
roundRect(13,-11,6,22,2,'#e3b76a');
ctx.fillStyle='#e8c47e';ctx.beginPath();ctx.moveTo(19,-11);ctx.lineTo(23,-8);ctx.lineTo(23,8);ctx.lineTo(19,11);ctx.closePath();ctx.fill();
circle(21,0,3,'#fff0b8');
roundRect(-17,-11,13,22,4,'#f3d29e');
roundRect(-14,-8,7,16,2,'#b5d1c4');
roundRect(-2,-10,14,20,5,'#c8654d');
circle(5,0,6,'#654c40'); circle(5,0,3.7,'#806450'); circle(4,-1,1.2,'#b9a184');
ctx.fillStyle='#ed9c78';ctx.fillRect(-1,-9,11,2);
} else {
const color=colors[(index-1)%colors.length];
roundRect(-19,-14,38,28,6,'#5e7563');
roundRect(-18,-12,36,24,5,color);
ctx.fillStyle='rgba(255,255,255,.20)'; ctx.fillRect(-15,-10,30,3);
roundRect(-13,-6,11,12,3,'#f7e7c1');
roundRect(3,-6,11,12,3,'#f7e7c1');
circle(-7.5,0,3.2,index%2?'#d9a27f':'#bc896c');
circle(8.5,0,3.2,index%2?'#edc29c':'#e0ad87');
ctx.fillStyle='rgba(71,91,71,.22)';ctx.fillRect(-16,9,32,2);
roundRect(-21,-2,4,4,1,'#5c7060');
}
ctx.restore();
}
function draw(time) {
drawGround(); drawPlatform(time);
const progress = paused || over ? 1 : Math.max(0,Math.min(1,(time-lastStep)/interval));
const points = snake.map((p,i) => {
const old=previous[Math.min(i,previous.length-1)] || p;
return {x:(old.x+(p.x-old.x)*progress+.5)*size, y:(old.y+(p.y-old.y)*progress+.5)*size};
});
drawTracks(points);
for (let i=points.length-1;i>=0;i--) {
let angle;
if (i===0) angle=Math.atan2(dir.y,dir.x);
else {
const a=points[i-1], b=points[i];
angle=Math.atan2(a.y-b.y,a.x-b.x);
}
drawCar(points[i].x,points[i].y,angle,i);
}
}
function frame(time) {
if (demoReset && time >= demoReset) reset();
if (!paused && !over && time-lastStep >= interval) {
step(); lastStep=time;
}
draw(time);
requestAnimationFrame(frame);
}
function setDirection(name) {
if (!started) start();
if (over) return;
if (paused) { paused=false; pauseButton.textContent='Pause'; status.textContent='YOUR TURN'; lastStep=performance.now(); }
const d=directions[name];
if (d && !(d.x === -dir.x && d.y === -dir.y)) queued=d;
}
document.querySelectorAll('[data-dir]').forEach(button => {
button.addEventListener('click', event => { event.stopPropagation(); setDirection(button.dataset.dir); });
});
document.getElementById('playfield').addEventListener('click', event => {
if (event.target.closest('#restart')) return;
if (!started) start();
});
document.getElementById('restart').addEventListener('click', event => {
event.stopPropagation(); started=true; reset(); hint.style.display='none'; status.textContent='YOUR TURN';
});
pauseButton.addEventListener('click', event => {
event.stopPropagation();
if (!started) { start(); return; }
if (over) return;
paused=!paused; pauseButton.textContent=paused?'Resume':'Pause';
status.textContent=paused?'TAKING A BREAK':'YOUR TURN'; lastStep=performance.now();
});
document.addEventListener('keydown', event => {
const map={ArrowUp:'up',ArrowDown:'down',ArrowLeft:'left',ArrowRight:'right',w:'up',s:'down',a:'left',d:'right'};
const name=map[event.key] || map[event.key.toLowerCase()];
if (name) { event.preventDefault(); setDirection(name); }
else if (event.key === ' ') {
event.preventDefault();
if (!started) start(); else if (!over) pauseButton.click();
} else if (!started) start();
else if (over && event.key === 'Enter') { reset(); status.textContent='YOUR TURN'; }
});
reset();
draw(performance.now());
requestAnimationFrame(frame);
})();
</script>
</body>
</html>Reasoning trace (4,076 tokens)
**Designing a layout** I'm thinking about a design layout that uses a max width of 1180, with specific dimensions for headers and the main content areas. The header could be 110, and the main section at 560. I might also consider an aesthetically pleasing left-side canvas that's 860x570, and a right side around 280. I want to include labels like "NOW BOARDING" for clarity. As for the background, I'm envisioning a soft color with subtle repeating lines for texture—maybe a track line design in the corners for visual interest.**Designing a visual platform** I'm considering a right-side vertical card design with a dark forest theme and a caption, "ALL ABOARD!" I want a cream background border and colorful visuals of travelers. The user's goal is to pick up passengers on the platform, generating a passenger one at a time with a green signal for the next pickup. I plan to draw the platform as a rectangle spanning two grid cells, centered at a food cell. The locomotion involves a BFS for the shortest path while avoiding collisions. I'll ensure it auto-moves every 200ms, starting from set coordinates.**Adjusting initial conditions** I'm considering whether the initial target for the food might be moving too quickly. For example, with the food at coordinates (15, 3) and the initial head at (4, 7), I calculate that it takes about 3.08 seconds to reach the target based on a Manhattan distance of 15 steps. At a speed of 230ms, that extends to roughly 3.45 seconds. I want to ensure I can pick up the passenger quickly after this time. I should track board drawings with a green field background and tile settings for the canvas.**Designing the journey card** I'm focusing on the right panel design. At the top, I want a card titled "YOUR JOURNEY" with an icon and a catchy line: "A little ride, a lot of joy." The description will guide users on helping passengers. Illustrative avatars could be displayed using CSS circles. I also need to outline steps for using the train controls and keeping on track. At the bottom, a ticket-style footer should indicate the next stop, while the top info bar reflects the train's status.**Structuring the right panel size** I'm determining the appropriate size for the right panel, aiming for about 278 pixels wide with a height of around 500 pixels. After calculating the viewport, the total height comes to 760, factoring in the header and toolbar. For the canvas, I'd like a ratio of 20x13, which leads to a nice layout. I'll stick with Georgia for the title and a system sans font for the body. The background will be a subtle warm color, and I want to ensure the design is clean and responsive!**Designing the layout** I'm working on the layout, considering margins and borders for the board panel and footer. The top of the canvas will be white, so it stands out. With a viewport width of 1280, I'll account for padding and aim for a maximum width of 1190 with an 850 right panel and a 280 left gap. The board header will display the main title "THE MEADOW LINE" along with the subtitle "A passenger-picking adventure." I also want a score area for passengers and a decorative illustration, possibly using inline SVG to enhance the visuals!**Clarifying game interactions** I need to figure out how user interactions will work in the game. When clicked, the direction buttons should start moving the character. I’m thinking about showing overlay messages at the top and a banner at the bottom. When the game starts, I need to reset the state, including initial positions and passenger locations. I want to provide clear instructions about controls and make sure they disappear after a few seconds to keep the focus on the game.**Planning game mechanics** I need to implement an account reversal based on the current direction and queue. For automatic movement, I'll use a breadth-first search (BFS) with a 220ms tick. I'll ensure that ambient animations are always drawn with `requestAnimationFrame`, but only update them based on the time elapsed. I want to handle canvas interactions to initiate movement and set up mobile d-pad event listeners. AI movement using BFS should be robust, avoiding entrapment and ensuring safe navigation around walls and food.**Planning game layout** I'm figuring out how to create a wrap for the game where walls cause a game over, as instructed. It seems like using a breadth-first search (BFS) could be robust here. I'm wondering about how the start mechanism works—should clicking anywhere or pressing any key initiate it? I need to attach the start to the canvas overlay and ensure that the pause button stops propagation. The initial snake length will be 6, but the score starts at 0. I’m mapping out the visual layout too, which is quite detailed!**Designing interface elements** I want to create a header badge titled "A VERY SMALL ADVENTURE" with a subtitle "EST. TODAY / OPEN FOR JOY" and use a logo. I need to focus on CSS for smaller viewports, making sure the layout stacks the board and sidebar responsively. The board canvas dimensions will be 375 width by 223 height. I plan to show the D-pad on both mobile and desktop, keeping visual elements like shadows and labels consistent. Lastly, I need to ensure the AI's route is optimized using BFS with the target coordinates, maintaining clear dynamic outputs!**Addressing overlay positioning** I think there might be an issue with the canvas overlay message at the bottom—it could be centered and cover part of the content, which isn't ideal. I need to write the HTML to position this correctly so that it doesn't obstruct any significant parts of the interface. It's crucial to ensure that users can see everything clearly without distractions. I’m considering ways to properly format it, perhaps adjusting the margins or using z-index to layer things appropriately.