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
- 1 min 3 s
- Cost
- $0.0038
- Output tokens
- 9,488
- Spent thinking
- 4,736 tokens
- Speed
- 158.5 tok/s
- Console errors
- 0
- First token after
- 3.2 s
- First visible text after
- 34.1 s
- Prompt tokens
- 361
- Code size
- 16.7 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"medium"}- 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 · 570 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Train Stop: Passengers on Platform</title>
<style>
:root {
--bg: #f5ecd6;
--platform: #8b8b8b;
--platform-edge: #6b6b6b;
--track: #b2a38a;
--loc: #1f4e8a;
--wagon: #4cc9f0;
--text: #2b2b2b;
}
html, body {
height: 100%;
margin: 0;
background: var(--bg);
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu;
color: var(--text);
overflow: hidden; /* avoid horizontal scrollbar on small screens */
}
.wrap {
height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
gap: 8px;
padding: 8px;
box-sizing: border-box;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 10px;
border-radius: 8px;
background: rgba(255,255,255,0.75);
backdrop-filter: blur(2px);
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
header h1 {
font-size: 1.05rem;
margin: 0;
letter-spacing: .4px;
}
.hud {
display: flex; gap: 14px; align-items: center;
font-variant-numeric: tabular-nums;
}
#stage {
display: grid;
place-items: center;
width: 100%;
height: 100%;
position: relative;
}
canvas {
width: 100%;
height: auto;
max-width: 1280px;
aspect-ratio: 4 / 3;
/* The canvas will be sized by JS to fit the grid nicely on any screen */
background: #efe6d0;
border-radius: 12px;
box-shadow: 0 6px 20px rgba(0,0,0,.15);
}
/* Overlay message for starting the demo */
#overlay {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: rgba(255,255,255,0.92);
border-radius: 12px;
text-align: center;
padding: 20px;
font-family: inherit;
color: #2b2b2b;
user-select: none;
cursor: pointer;
}
#overlay h2 {
margin: 0 0 8px 0;
font-size: 1.4rem;
}
#overlay p {
margin: 0;
font-size: 0.98rem;
color: #555;
}
@media (max-width: 900px) {
header { padding: 6px 8px; }
#overlay h2 { font-size: 1.2rem; }
}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>Train Stop: Passengers on Platform</h1>
<div class="hud" aria-label="Score and status">
<span>Score: <strong id="score">0</strong></span>
<span>Passengers: <strong id="count">0</strong></span>
<span id="status" style="opacity:.6">Autonomous demo</span>
</div>
</header>
<section id="stage" aria-label="Game stage">
<canvas id="game"></canvas>
<div id="overlay" tabindex="0" aria-label="Click to start the game or press any key">
<div>
<h2>Click or press any key to start</h2>
<p>The train (locomotive + colorful wagons) will automatically pick up passengers waiting on the platform.</p>
</div>
</div>
</section>
<footer style="text-align:center; opacity:.6; font-size:.85rem; padding:6px 0 12px;">
Tip: The AI plans paths to the nearest waiting passenger. If it crashes, press Start to try again.
</footer>
</div>
<script>
// Game configuration
const COLS = 24; // grid width (columns)
const ROWS = 20; // grid height (rows)
const PLATFORM_ROWS = 4; // bottom rows that represent the platform where passengers wait
const INITIAL_LENGTH = 4; // locomotive + 3 wagons
const TICK_MS = 120; // AI step every 120ms
// Visuals
const LOC_COLOR = "#1f4e8a";
const WAGON_COLORS = ["#4cc9f0","#ff7b54","#ffd166","#8bd3dd","#b28a6b","#c084fc"];
const PLATFORM_COLOR = "#8b8b8b";
const BG_COLOR = "#efe6d0";
// Canvas and grid
const canvas = document.getElementById('game');
const overlay = document.getElementById('overlay');
const scoreEl = document.getElementById('score');
const countEl = document.getElementById('count');
const statusEl = document.getElementById('status');
const ctx = canvas.getContext('2d');
// Game state
let cell = 16; // size of a grid cell in pixels (dynamic)
let train = []; // segments: [{x, y}, ...] head at index 0
let passengers = []; // waiting passengers on platform: [{x, y}, ...]
let score = 0;
let passengerCount = 0;
let started = false;
let gameOver = false;
let timerId = null;
// Derived constants
const PLATFORM_Y0 = ROWS - PLATFORM_ROWS;
// Helpers
function inside(x, y) { return x >= 0 && x < COLS && y >= 0 && y < ROWS; }
// Initialize/rescale grid to fit viewport
function resize() {
// Determine cell size to fit nicely but keep aspect
const w = window.innerWidth;
const h = window.innerHeight;
// Reserve room for header; compute best cell
const maxW = Math.max(320, w * 0.95);
const maxH = Math.max(240, h * 0.75);
const c1 = Math.floor(maxW / COLS);
const c2 = Math.floor(maxH / ROWS);
cell = Math.max(8, Math.min(c1, c2));
canvas.width = COLS * cell;
canvas.height = ROWS * cell;
// Paint background of the canvas area
draw(); // redraw after resize
}
// Spawn an initial train somewhere central-ish
function spawnInitialTrain() {
const midX = Math.floor(COLS * 0.25);
const midY = Math.floor(ROWS * 0.5);
train = [
{x: midX, y: midY}, // locomotive head
{x: midX - 1, y: midY},
{x: midX - 2, y: midY},
{x: midX - 3, y: midY}
];
}
// Create a fresh set of passengers on the platform
function spawnPassengers(n = 3) {
passengers = [];
let tries = 0;
while (passengers.length < n && tries < 200) {
const x = Math.floor(Math.random() * COLS);
const y = PLATFORM_Y0 + Math.floor(Math.random() * PLATFORM_ROWS);
// Ensure the spot is empty (not on the train)
if (!isOccupied(x, y)) {
passengers.push({x, y});
}
tries++;
}
passengerCount = passengers.length;
countEl.textContent = passengerCount;
}
function isOccupied(x, y) {
for (const s of train) if (s.x === x && s.y === y) return true;
for (const p of passengers) if (p.x === x && p.y === y) return true;
// platform cells are allowed to host passengers
return false;
}
function resetGame() {
// fresh state
score = 0;
scoreEl.textContent = score;
started = false;
gameOver = false;
statusEl.textContent = "Autonomous demo";
// build initial train
spawnInitialTrain();
// place initial passengers
spawnPassengers(3);
}
// Breadth-first search to build a path from start to goal,
// considering current body as obstacles (except tail, which will move).
function bfsPath(start, goal, obstaclesSet) {
const key = (x, y) => x + "," + y;
const visited = new Set();
const q = [];
const parent = new Map();
q.push(start);
visited.add(key(start.x, start.y));
const dirs = [
{dx:1, dy:0}, {dx:-1, dy:0}, {dx:0, dy:1}, {dx:0, dy:-1}
];
while (q.length) {
const cur = q.shift();
if (cur.x === goal.x && cur.y === goal.y) {
// reconstruct path
const path = [];
let c = goal;
while (c) {
path.push({x: c.x, y: c.y});
const p = parent.get(key(c.x, c.y));
c = p ? {x: p.x, y: p.y} : null;
}
path.reverse();
return path;
}
for (const d of dirs) {
const nx = cur.x + d.dx;
const ny = cur.y + d.dy;
if (!inside(nx, ny)) continue;
const nk = key(nx, ny);
if (visited.has(nk)) continue;
// treat body (except tail) as obstacles
if (obstaclesSet.has(nk)) continue;
visited.add(nk);
parent.set(nk, {x: cur.x, y: cur.y});
q.push({x: nx, y: ny});
}
}
return null;
}
// Build a set of obstacle coordinates for BFS (current body excluding tail)
function bodyObstacles() {
const s = new Set();
for (let i = 0; i < train.length - 1; i++) {
const seg = train[i];
s.add(seg.x + "," + seg.y);
}
// Do not treat tail as obstacle since it will move away
return s;
}
// Choose next move by pathfinding to nearest passenger
function nextMove() {
if (train.length === 0) return null;
if (passengers.length === 0) return null;
const head = train[0];
const obstacles = bodyObstacles();
// Find best path to any passenger
let bestPath = null;
let bestPassenger = null;
for (const p of passengers) {
const path = bfsPath(head, p, obstacles);
if (path && path.length >= 2) {
if (!bestPath || path.length < bestPath.length) {
bestPath = path;
bestPassenger = p;
}
}
}
if (bestPath) {
// next step is the second element in path (index 1)
return bestPath[1];
}
// Fallback: simple greedy move towards nearest passenger
let target = passengers[0];
const dx = Math.sign(target.x - head.x);
const dy = Math.sign(target.y - head.y);
// Try x-direction first
if (dx !== 0) {
const nx = head.x + dx;
const ny = head.y;
if (inside(nx, ny) && !obstacles.has(nx + "," + ny)) return {x: nx, y: ny};
}
// Then y-direction
if (dy !== 0) {
const nx = head.x;
const ny = head.y + dy;
if (inside(nx, ny) && !obstacles.has(nx + "," + ny)) return {x: nx, y: ny};
}
// If blocked, try any free neighbor
const dirs = [
{dx:1, dy:0}, {dx:-1, dy:0}, {dx:0, dy:1}, {dx:0, dy:-1}
];
for (const d of dirs) {
const nx = head.x + d.dx;
const ny = head.y + d.dy;
if (inside(nx, ny) && !obstacles.has(nx + "," + ny)) return {x: nx, y: ny};
}
// No move possible
return null;
}
// Spawn a single passenger after eating one
function spawnPassengerAfterEat() {
// try to spawn in a random platform cell that isn't occupied
const candidate = [];
for (let x = 0; x < COLS; x++) {
for (let y = PLATFORM_Y0; y < ROWS; y++) {
if (!isOccupied(x, y)) candidate.push({x, y});
}
}
if (candidate.length === 0) return;
const idx = Math.floor(Math.random() * candidate.length);
passengers.push({x: candidate[idx].x, y: candidate[idx].y});
passengerCount = passengers.length;
countEl.textContent = passengerCount;
}
// Drawing
function clearCanvas() {
ctx.fillStyle = BG_COLOR;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function drawPlatform() {
// Draw platform ground on the bottom PLATFORM_ROWS rows
for (let y = PLATFORM_Y0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
const px = x * cell;
const py = y * cell;
// platform shade
ctx.fillStyle = PLATFORM_COLOR;
ctx.fillRect(px, py, cell, cell);
// subtle edge stripe
if (y === ROWS - PLATFORM_ROWS) {
ctx.fillStyle = "#7a7a7a";
ctx.fillRect(px, py, cell, 2);
}
}
}
}
function drawPassengers() {
for (const p of passengers) {
const cx = p.x * cell + cell / 2;
const cy = p.y * cell + cell / 2;
// simple stick figure
ctx.fillStyle = "#f8a602"; // passenger color
// head
ctx.beginPath();
ctx.arc(cx, cy - cell * 0.15, cell * 0.15, 0, Math.PI * 2);
ctx.fill();
// body
ctx.strokeStyle = "#3b3b3b";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cx, cy - cell * 0.15);
ctx.lineTo(cx, cy + cell * 0.25);
ctx.stroke();
// arms
ctx.beginPath();
ctx.moveTo(cx - cell * 0.18, cy);
ctx.lineTo(cx + cell * 0.18, cy);
ctx.stroke();
// legs
ctx.beginPath();
ctx.moveTo(cx, cy + cell * 0.25);
ctx.lineTo(cx - cell * 0.15, cy + cell * 0.55);
ctx.moveTo(cx, cy + cell * 0.25);
ctx.lineTo(cx + cell * 0.15, cy + cell * 0.55);
ctx.stroke();
}
}
function drawTrain() {
for (let i = 0; i < train.length; i++) {
const seg = train[i];
const x = seg.x * cell;
const y = seg.y * cell;
// add some rounded look
ctx.fillStyle = (i === 0) ? LOC_COLOR : WAGON_COLORS[i % WAGON_COLORS.length];
ctx.fillRect(x + 2, y + 2, cell - 4, cell - 4);
// wheels
ctx.fillStyle = "#111";
ctx.fillRect(x + 6, y + cell - 8, 4, 4);
ctx.fillRect(x + cell - 12, y + cell - 8, 4, 4);
// locomotive chimney
if (i === 0) {
ctx.fillStyle = "#222";
ctx.fillRect(x + cell - 14, y - 6, 8, 8);
}
}
}
function draw() {
clearCanvas();
// background ground line
drawPlatform();
drawPassengers();
drawTrain();
// subtle grid (optional)
ctx.strokeStyle = "rgba(0,0,0,0.04)";
ctx.lineWidth = 1;
for (let x = 0; x <= COLS; x++) {
ctx.beginPath();
ctx.moveTo(x * cell, 0);
ctx.lineTo(x * cell, ROWS * cell);
ctx.stroke();
}
for (let y = 0; y <= ROWS; y++) {
ctx.beginPath();
ctx.moveTo(0, y * cell);
ctx.lineTo(COLS * cell, y * cell);
ctx.stroke();
}
}
// Game loop: AI step and render
function step() {
if (!started || gameOver) return;
const nxt = nextMove();
if (!nxt) {
// No move possible: end game
gameOver = true;
statusEl.textContent = "Game over";
overlay.style.display = "grid";
started = false;
return;
}
// Move train
train.unshift({x: nxt.x, y: nxt.y});
// Check collision with self
for (let i = 1; i < train.length; i++) {
if (train[0].x === train[i].x && train[0].y === train[i].y) {
gameOver = true;
statusEl.textContent = "Game over";
overlay.style.display = "grid";
started = false;
return;
}
}
// Check passenger pickup
let ate = false;
for (let i = 0; i < passengers.length; i++) {
if (train[0].x === passengers[i].x && train[0].y === passengers[i].y) {
ate = true;
// remove passenger
passengers.splice(i, 1);
break;
}
}
if (ate) {
// Grow (do not remove tail)
score++;
scoreEl.textContent = score;
// spawn new passenger
spawnPassengerAfterEat();
} else {
// Normal move: remove tail
train.pop();
}
// Safety: if train goes out of bounds (shouldn't due to checks)
const head = train[0];
if (!inside(head.x, head.y)) {
gameOver = true;
statusEl.textContent = "Game over (out of bounds)";
overlay.style.display = "grid";
started = false;
return;
}
draw();
}
// Start the demo
function startGame() {
if (started) return;
overlay.style.display = "none";
// Focus is not strictly required, but helps with accessibility
// (keyboard events are not essential for this demo)
started = true;
gameOver = false;
statusEl.textContent = "Autonomous demo running";
// reset any previous state but keep current visuals (fresh feel)
// ensure there is at least some train and passengers
if (train.length === 0) spawnInitialTrain();
if (passengers.length === 0) spawnPassengers(3);
// show HUD
scoreEl.textContent = score;
countEl.textContent = passengers.length;
// begin ticking
if (timerId != null) clearInterval(timerId);
timerId = setInterval(step, TICK_MS);
// initial render
draw();
// First path planning might need an immediate step
// We don't do an extra step here to let the first tick plan.
}
// Init
function init() {
// Initial layout
// Start with a nice initial train and some passengers
resize();
resetGame();
// Overlay handlers
overlay.addEventListener('click', () => {
startGame();
});
// Also allow keyboard to start
document.addEventListener('keydown', (e) => {
if (!started) startGame();
}, {passive: true});
// Ensure the canvas handles high-DPI crispness (optional)
// Redraw on resize
window.addEventListener('resize', () => {
resize();
});
}
// Kick off
init();
</script>
</body>
</html>Reasoning trace (4,736 tokens)
**Planning a train game** I'm thinking about implementing a "platform" row at the bottom where passengers are represented by dots or small character icons. The train will collect them by moving into their cells, and it will grow by one wagon per passenger, with differently colored wagons. The locomotive will have a distinct emblem. I also need a grid-based movement system with arrow key controls. Importantly, there will be a self-playing demo mode behind a "Click or press a key to start" message. The AI will guide the train autonomously, and the user can only interact after clicking to start. I'll ensure the iframe receives keyboard events post-click. I'll also add a "Start" overlay indicating to click or press a key to get started.**Designing AI pathfinding** I'm considering implementing a simple BFS pathfinding algorithm for the AI to reach the next passenger position on a grid. It will be based on the head's coordinate while avoiding collisions with the body. Since it’s self-contained, I can use a heuristic that allows the head to move toward the nearest passenger in either the x or y direction. For simplicity, I’ll set up a "greedy chase" approach to minimize the Manhattan distance to the target passenger while avoiding the tail. If a collision is likely, the AI can choose an alternate path or direction. To prevent any errors, I will also implement path planning using A* or BFS to ensure safe navigation with the body as obstacles.**Creating the BFS pathfinding** I'm thinking about implementing a BFS function that returns a path of positions within the grid so the AI can navigate around its body. The grid will have a width and height of 28x20 cells, which I can scale to fit the viewport. Next, I want to create a function to spawn passengers on the platform, defined as the bottom row or two. I'll randomly place passenger tokens along the platform without overlapping the existing train cells, ensuring there are spots for passengers to wait, potentially in the bottom four rows.**Designing the train's visuals** I'm envisioning that the train can move along tracks anywhere on the grid. To fit the theme, I plan to create a platform area at the bottom using a colored rectangle row with little seat icons. For simplicity, I'll render the grid on a canvas, where the platform lane will be a dark gray area, and passengers will be small person icons, probably drawn as circles or squares labeled "P." The locomotive will be a slightly larger rectangle with dynamic smoke effects from its funnel, and I'll color the wagons in different bright shades. It's essential that all of this code runs smoothly without any errors in the console. Ultimately, I want to create a "SNAKE game" concept where the train acts as the snake, picking up passengers from the platform.**Implementing passenger and train mechanics** I want to create a "Passengers waiting on the platform" feature with a pool of spots. When the train's head collides with a passenger cell, the train will grow by adding a wagon, remove the passenger, and spawn a new one on the platform. The display must adapt from 1280x800 down to 375px. I’ll implement responsive canvas scaling to keep the grid square. I'll fix the grid dimensions to 28x20 and calculate cell sizes accordingly. The canvas width will be set to columns times the cell size. I need to ensure no horizontal scrollbar by setting the body to overflow hidden. Additionally, a screenshot will be captured three seconds after loading. I'll structure the HTML, CSS, and JavaScript properly while ensuring that the AI uses BFS to navigate to the nearest passengers. The visuals will include a light beige background, a dark gray platform, and small stick figure passengers. The train will have a colorful locomotive and wagons for aesthetics.**Implementing the game mechanics** I’m working on displaying passengers waiting on the platform as small icons while the train moves across, keeping the passengers static. We'll start with an overlay showing "Click to start." Once the user clicks or presses a key, the game starts. The AI loop is crucial, where I’ll compute the next step using BFS for pathfinding to the nearest passenger. If the train collides with its body, it's game over. The train will grow without removing the last segment when it eats a passenger, and I'll spawn new passengers afterward while ensuring there’s space. I’ll also implement a predetermined color array for wagons and the locomotive. The color for the locomotive will be distinct to ensure it leads the train, while the passengers will be small stick figures drawn on the platform. Lastly, I'll use CSS to prevent horizontal scrollbars and compute the canvas size dynamically. I want to ensure that something meaningful is visible on the first paint, like the initial train and overlay message, and that the code runs without errors in the console. The global variables will help organize the grid dimensions.**Developing game mechanics** I'm setting the initial position of the train head at the middle-left of the grid and must ensure it’s visible. I’m starting with a train length of three segments. Next, I’ll implement the BFS function to find a path to the nearest passenger. If no passengers are present, I’ll return a random direction, but I plan to pre-spawn three passengers. The AI will compute potential paths, excluding the tail of the train from obstacles. Movement involves updating the head's position and checking for collisions with the train body or edges of the grid. If the train eats a passenger, I’ll increase the score and spawn a new passenger in an empty spot. If it doesn't eat, I'll remove the last segment. I need a clear method to restart the game upon a collision and utilize an overlay for the game over screen. The game will run with a boolean to signify whether it has started, updating the UI with the score. I'll implement a simple animation loop for rendering the graphics and input handling, ensuring keyboard events begin after clicking on the overlay.**Implementing game start mechanics** If we don’t have focus, the keyboard events won’t work, but that’s okay since we only need a click to start. I’ll make the overlay focusable by adding `tabindex="0"`. Clicking the overlay will trigger the game start and call `overlay.blur()` to manage focus. I won't require keyboard events post-start, which is a relief since we only need to detect clicks to simplify things. When the overlay is clicked or a key is pressed, I’ll start the game. I’ll also set the initial train length to four segments, ensuring the platform is visible and appears behind the train graphics.