GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 38 s
- Cost
- $0.044
- Output tokens
- 8,609
- Spent thinking
- 1,268 tokens
- Speed
- 93.7 tok/s
- Console errors
- 0
- First token after
- 5.7 s
- First visible text after
- 26.3 s
- Prompt tokens
- 493
- Code size
- 21.5 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
model default- Max output
- 128,000 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
Build a polished Snake game in a single HTML file using vanilla JavaScript and a <canvas>. Requirements: - A grid of at least 20×20 cells, centered on the page. - Arrow keys and WASD control the snake. Quick successive key presses must both be honored, but the snake can never reverse onto itself. - Eating food adds 10 points and grows the snake by one cell; new food never spawns on the snake. - Game over when the snake hits a wall or itself, with an overlay showing the score; Space or Enter restarts, P pauses. - Visual polish: smooth movement, a snake with a distinct head and eyes facing its direction, and a small particle burst when eating. - Display the current score and the best score of the session.
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 · 448 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Snake Club</title>
<style>
:root {
color-scheme: dark;
--bg: #0a1110;
--panel: #101b19;
--line: #263b34;
--muted: #82958c;
--bright: #c4ff70;
--white: #edf5e8;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
color: var(--white);
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background:
radial-gradient(ellipse at 47% 18%, rgba(54, 95, 65, .18), transparent 43%),
radial-gradient(ellipse at 90% 90%, rgba(27, 72, 48, .13), transparent 38%),
var(--bg);
}
button { font: inherit; }
.shell { width: min(1050px, calc(100% - 48px)); margin: 0 auto; padding: 28px 0 24px; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-bottom: 24px; }
.brand { display: flex; gap: 13px; align-items: center; }
.brand-icon {
width: 45px; height: 45px; display: grid; place-items: center;
border: 1px solid #597c4a; border-radius: 13px;
color: var(--bright); background: #1c3021; box-shadow: inset 0 0 18px #b7ff6d0b;
}
.eyebrow { color: #9bb794; font-size: 10px; font-weight: 800; letter-spacing: .21em; text-transform: uppercase; }
h1 { margin: 2px 0 0; font-size: 23px; line-height: 1; letter-spacing: -.05em; }
.brand-sub { color: #91a399; font-weight: 500; }
.scores { display: flex; gap: 10px; }
.score-card {
min-width: 112px; padding: 10px 16px 9px; border: 1px solid #2a3d34;
border-radius: 11px; background: #14201b; display: flex; align-items: center; gap: 14px;
}
.score-card.best { background: #101a17; }
.score-label { display: block; color: #8ca196; font-size: 9px; font-weight: 800; letter-spacing: .15em; }
.score-number { display: block; margin-top: 2px; font-size: 22px; font-weight: 750; line-height: 1; font-variant-numeric: tabular-nums; }
.score-icon { color: #baff70; opacity: .9; }
.best .score-icon { color: #a3b6a2; }
.layout { display: flex; align-items: stretch; justify-content: center; gap: 24px; }
.board-wrap { flex: 0 1 586px; min-width: 0; }
.board-frame {
position: relative; width: 100%; aspect-ratio: 1;
padding: 7px; border: 1px solid #365141; border-radius: 16px;
background: #15251b; box-shadow: 0 20px 65px #0007, 0 0 0 1px #ffffff05 inset;
overflow: hidden; cursor: pointer;
}
canvas { display: block; width: 100%; height: 100%; border-radius: 9px; }
.board-status {
position: absolute; left: 20px; top: 20px; display: flex; align-items: center; gap: 7px;
padding: 7px 9px; border: 1px solid #54715a70; border-radius: 6px;
background: #101c16bb; color: #b6c9b3; font-size: 9px; letter-spacing: .15em; font-weight: 800;
pointer-events: none;
}
.dot { width: 6px; height: 6px; border-radius: 50%; background: #a8ef6c; box-shadow: 0 0 8px #a8ef6c; }
.start-hint {
position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%);
width: max-content; max-width: calc(100% - 32px); padding: 12px 18px;
background: #111d17eF; border: 1px solid #73965d; border-radius: 9px;
box-shadow: 0 8px 25px #0009; text-align: center; pointer-events: none;
font-size: 12px; font-weight: 750; letter-spacing: .015em; color: #e7f5da;
}
.start-hint span { color: #baff78; }
.overlay {
position: absolute; inset: 7px; border-radius: 9px; display: none;
align-items: center; justify-content: center; flex-direction: column;
background: #07100dc9; backdrop-filter: blur(3px); text-align: center;
}
.overlay.visible { display: flex; }
.overlay-kicker { color: #a7ed72; letter-spacing: .19em; font-weight: 800; font-size: 10px; }
.overlay h2 { margin: 9px 0 4px; font-size: clamp(31px, 4vw, 45px); letter-spacing: -.06em; }
.overlay p { color: #a9bbb0; margin: 0 0 22px; font-size: 14px; }
.overlay button {
border: 0; border-radius: 8px; background: #c2ff79; color: #142316;
font-weight: 800; padding: 12px 22px; cursor: pointer;
}
.overlay small { color: #8fa496; margin-top: 12px; font-size: 11px; }
.board-caption { display: flex; justify-content: space-between; color: #70887a; font-size: 10px; font-weight: 700; letter-spacing: .12em; margin: 13px 2px 0; }
.sidebar { flex: 0 0 270px; display: flex; flex-direction: column; gap: 14px; }
.side-card { background: linear-gradient(145deg, #14221b, #101a16); border: 1px solid #2a4032; border-radius: 14px; padding: 20px; }
.side-heading { display: flex; align-items: center; gap: 9px; color: #d9ebd6; font-size: 12px; font-weight: 800; letter-spacing: .09em; }
.side-heading svg { color: #baff79; }
.side-copy { font-size: 13px; line-height: 1.6; color: #91a99a; margin: 12px 0 18px; }
.control-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 0; border-top: 1px solid #293b30; }
.control-row > span { font-size: 12px; color: #b5c5b8; }
.keys { display: flex; gap: 3px; }
kbd { min-width: 23px; height: 23px; display: inline-grid; place-items: center; padding: 0 5px; border: 1px solid #405748; border-bottom-width: 2px; border-radius: 4px; background: #1b2b21; color: #c4d3c4; font-family: inherit; font-size: 10px; font-weight: 800; }
.tip { display: flex; gap: 12px; align-items: flex-start; }
.fruit-icon { flex: none; width: 31px; height: 31px; border-radius: 9px; display: grid; place-items: center; background: #4c2923; color: #ff9f83; }
.tip strong { display: block; font-size: 12px; color: #e2eee0; margin: 1px 0 5px; }
.tip p { color: #8fa698; font-size: 12px; line-height: 1.55; margin: 0; }
.side-bottom { margin-top: auto; color: #71877a; font-size: 10px; letter-spacing: .1em; line-height: 1.8; }
.side-bottom strong { color: #b3c7b5; }
.mobile-controls { display: none; }
@media (max-width: 760px) {
.shell { width: min(586px, calc(100% - 30px)); padding-top: 18px; }
.topbar { margin-bottom: 16px; }
.sidebar { display: none; }
.layout { display: block; }
.board-wrap { width: 100%; }
.score-card { min-width: 75px; padding: 8px 10px; gap: 7px; }
.score-icon { display: none; }
.score-number { font-size: 18px; }
.mobile-controls { display: flex; align-items: center; justify-content: space-between; margin: 18px 0 0; }
.mobile-controls p { margin: 0; font-size: 11px; line-height: 1.6; color: #8fa799; }
.dpad { display: grid; grid-template-columns: repeat(3, 38px); grid-template-rows: repeat(2, 36px); gap: 4px; }
.dpad button { border: 1px solid #3b5643; border-radius: 6px; background: #1a2c20; color: #d5ebcf; font-size: 16px; touch-action: manipulation; }
.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; }
}
@media (max-width: 430px) {
.brand-icon { width: 39px; height: 39px; }
h1 { font-size: 19px; }
.eyebrow { font-size: 8px; }
.scores { gap: 6px; }
.score-card { min-width: 59px; padding: 7px 8px; }
.board-status { top: 16px; left: 16px; }
.start-hint { font-size: 11px; bottom: 18px; padding: 10px 12px; }
}
</style>
</head>
<body>
<div class="shell">
<header class="topbar">
<div class="brand">
<div class="brand-icon">
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" aria-hidden="true"><path d="M5 19h8a4 4 0 0 0 4-4v-1a4 4 0 0 1 4-4h1M5 19a3 3 0 0 1 0-6h4a3 3 0 0 0 3-3V7a3 3 0 0 1 3-3h3" stroke="currentColor" stroke-width="3" stroke-linecap="round"/><circle cx="19" cy="4" r="2.2" fill="currentColor"/><circle cx="19.6" cy="3.5" r=".55" fill="#15251b"/></svg>
</div>
<div><div class="eyebrow">The arcade collection</div><h1>snake<span class="brand-sub"> club.</span></h1></div>
</div>
<div class="scores">
<div class="score-card">
<svg class="score-icon" width="21" height="21" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="m12 3 2.5 5.1 5.6.8-4 3.9.9 5.6-5-2.7-5 2.7 1-5.6-4.1-3.9 5.6-.8L12 3Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg>
<div><span class="score-label">SCORE</span><span class="score-number" id="score">00</span></div>
</div>
<div class="score-card best">
<svg class="score-icon" width="21" height="21" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M8 4h8v5a4 4 0 0 1-8 0V4ZM8 6H5v2a4 4 0 0 0 4 4m7-6h3v2a4 4 0 0 1-4 4M12 13v4m-4 3h8m-6-3h4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
<div><span class="score-label">BEST</span><span class="score-number" id="best">00</span></div>
</div>
</div>
</header>
<main class="layout">
<section class="board-wrap" aria-label="Snake game">
<div class="board-frame" id="board">
<canvas id="game" width="660" height="660" aria-label="Snake game board"></canvas>
<div class="board-status"><span class="dot"></span><span id="status">DEMO MODE</span></div>
<div class="start-hint" id="hint"><span>✦</span> Click or press a key to start</div>
<div class="overlay" id="overlay">
<div class="overlay-kicker" id="overlayKicker">NICE RUN</div>
<h2 id="overlayTitle">Game over</h2>
<p id="overlayText">You scored 0 points.</p>
<button id="overlayButton">Play again</button>
<small id="overlayHelp">or press Space / Enter</small>
</div>
</div>
<div class="board-caption"><span>CLASSIC MODE</span><span>22 × 22 GRID · ENDLESS FUN</span></div>
<div class="mobile-controls">
<p>Use the arrows to move.<br>Eat, grow, and keep going.</p>
<div class="dpad" aria-label="Touch 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>
</section>
<aside class="sidebar">
<div class="side-card">
<div class="side-heading">
<svg width="17" height="17" viewBox="0 0 20 20" fill="none" aria-hidden="true"><rect x="2" y="3" width="16" height="14" rx="3" stroke="currentColor" stroke-width="1.5"/><path d="M6 10h4M8 8v4m5-3h.01M15 11h.01" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
HOW TO PLAY
</div>
<p class="side-copy">A little game of appetite and ambition. How long can you last?</p>
<div class="control-row"><span>Move</span><div class="keys"><kbd>↑</kbd><kbd>←</kbd><kbd>↓</kbd><kbd>→</kbd></div></div>
<div class="control-row"><span>Or use</span><div class="keys"><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd></div></div>
<div class="control-row"><span>Pause</span><div class="keys"><kbd>P</kbd></div></div>
<div class="control-row"><span>Restart</span><div class="keys"><kbd>SPACE</kbd></div></div>
</div>
<div class="side-card">
<div class="tip">
<div class="fruit-icon">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M10 6c-3-2-6 0-6 3.5C4 13 6.5 16 8.4 16c.8 0 1-.4 1.6-.4s.9.4 1.7.4c1.8 0 4.3-3 4.3-6.5C16 6 13 4 10 6Z" fill="currentColor"/><path d="M10 6c0-2 1-3 3-3" stroke="#caff8c" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<div><strong>Good things grow.</strong><p>Each bite is worth 10 points. Avoid the walls and your own tail.</p></div>
</div>
</div>
<div class="side-bottom"><strong>TAKE A LITTLE BREAK.</strong><br>YOUR NEXT HIGH SCORE AWAITS ↗</div>
</aside>
</main>
</div>
<script>
(() => {
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const statusEl = document.getElementById('status');
const hint = document.getElementById('hint');
const overlay = document.getElementById('overlay');
const overlayKicker = document.getElementById('overlayKicker');
const overlayTitle = document.getElementById('overlayTitle');
const overlayText = document.getElementById('overlayText');
const overlayButton = document.getElementById('overlayButton');
const overlayHelp = document.getElementById('overlayHelp');
const N = 22, CELL = 30, SIZE = N * CELL, STEP = 140;
const dirs = { up: {x:0,y:-1}, down:{x:0,y:1}, left:{x:-1,y:0}, right:{x:1,y:0} };
const keyDirs = { ArrowUp:'up', ArrowDown:'down', ArrowLeft:'left', ArrowRight:'right', w:'up', s:'down', a:'left', d:'right' };
const cycle = [];
for (let x = 0; x < N; x++) cycle.push({x, y:0});
for (let y = 1; y < N; y++) {
if (y % 2) for (let x = N - 1; x >= 1; x--) cycle.push({x,y});
else for (let x = 1; x < N; x++) cycle.push({x,y});
}
cycle.push({x:0,y:N-1});
for (let y = N-2; y >= 1; y--) cycle.push({x:0,y});
let phase = 'demo', snake, previous, direction, queue, food, score, best = 0;
let particles = [], accumulator = 0, lastTime = 0, cycleIndex = 0, foodPulse = 0;
const same = (a,b) => a.x === b.x && a.y === b.y;
const copy = p => ({x:p.x,y:p.y});
function setScore() {
scoreEl.textContent = String(score).padStart(2,'0');
bestEl.textContent = String(best).padStart(2,'0');
}
function spawnFood() {
if (phase === 'demo') {
let offset = 7 + Math.floor(Math.random() * 7);
for (let i = 0; i < cycle.length; i++) {
const p = cycle[(cycleIndex + offset + i) % cycle.length];
if (!snake.some(s => same(s,p))) return copy(p);
}
} else {
const empty = [];
for (let y=0; y<N; y++) for (let x=0; x<N; x++) {
if (!snake.some(s => s.x===x && s.y===y)) empty.push({x,y});
}
if (empty.length) return empty[Math.floor(Math.random()*empty.length)];
}
return null;
}
function reset(mode) {
phase = mode;
cycleIndex = cycle.findIndex(p => p.x === 9 && p.y === 10);
snake = [];
for (let i=0;i<6;i++) snake.push(copy(cycle[(cycleIndex-i+cycle.length)%cycle.length]));
previous = snake.map(copy);
direction = copy(dirs.right);
queue = [];
score = 0;
particles = [];
accumulator = 0;
food = mode === 'demo' ? copy(cycle[(cycleIndex+5)%cycle.length]) : spawnFood();
statusEl.textContent = mode === 'demo' ? 'DEMO MODE' : 'IN PLAY';
hint.style.display = mode === 'demo' ? 'block' : 'none';
overlay.classList.remove('visible');
setScore();
}
function enqueue(name) {
const next = dirs[name];
const last = queue.length ? queue[queue.length-1] : direction;
if (!next || (next.x === -last.x && next.y === -last.y) || (next.x === last.x && next.y === last.y)) return;
queue.push(copy(next));
}
function burst(x,y) {
for (let i=0;i<15;i++) {
const a = Math.random()*Math.PI*2, speed = 35+Math.random()*100;
particles.push({x:(x+.5)*CELL,y:(y+.5)*CELL,vx:Math.cos(a)*speed,vy:Math.sin(a)*speed,life:.45+Math.random()*.35,max:.8,r:2+Math.random()*3,color:Math.random()>.4?'#ff9c79':'#d3ff8e'});
}
}
function showOverlay(type) {
overlay.classList.add('visible');
if (type === 'over') {
overlayKicker.textContent = 'NICE RUN';
overlayTitle.textContent = 'Game over';
overlayText.textContent = `You scored ${score} points.`;
overlayButton.textContent = 'Play again';
overlayHelp.textContent = 'or press Space / Enter';
statusEl.textContent = 'GAME OVER';
} else if (type === 'win') {
overlayKicker.textContent = 'INCREDIBLE';
overlayTitle.textContent = 'You did it!';
overlayText.textContent = `A full board and ${score} points.`;
overlayButton.textContent = 'Play again';
overlayHelp.textContent = 'or press Space / Enter';
statusEl.textContent = 'YOU WIN';
} else {
overlayKicker.textContent = 'TAKE YOUR TIME';
overlayTitle.textContent = 'Paused';
overlayText.textContent = 'Your snake is right where you left it.';
overlayButton.textContent = 'Resume game';
overlayHelp.textContent = 'or press P';
statusEl.textContent = 'PAUSED';
}
}
function togglePause() {
if (phase === 'playing') { phase = 'paused'; showOverlay('paused'); }
else if (phase === 'paused') { phase = 'playing'; overlay.classList.remove('visible'); statusEl.textContent = 'IN PLAY'; accumulator = 0; }
}
function tick() {
if (phase === 'demo') {
const nextIndex = (cycleIndex+1)%cycle.length;
const next = cycle[nextIndex];
direction = {x:next.x-snake[0].x,y:next.y-snake[0].y};
cycleIndex = nextIndex;
} else if (queue.length) {
direction = queue.shift();
}
const head = {x:snake[0].x+direction.x,y:snake[0].y+direction.y};
const eating = food && same(head,food);
const collides = head.x<0 || head.x>=N || head.y<0 || head.y>=N ||
snake.slice(0, eating ? snake.length : -1).some(p => same(p,head));
if (collides) {
if (phase === 'demo') { reset('demo'); return; }
phase = 'over'; accumulator = 0; showOverlay('over'); return;
}
previous = snake.map(copy);
snake.unshift(head);
if (!eating) snake.pop();
else {
score += 10;
burst(food.x,food.y);
if (phase === 'playing' && score > best) best = score;
setScore();
food = spawnFood();
if (!food) { phase = 'over'; showOverlay('win'); }
}
}
function rounded(x,y,w,h,r) {
ctx.beginPath(); ctx.roundRect(x,y,w,h,r);
}
function drawBoard(time) {
ctx.clearRect(0,0,SIZE,SIZE);
ctx.fillStyle = '#13251b'; ctx.fillRect(0,0,SIZE,SIZE);
for (let y=0; y<N; y++) for (let x=0; x<N; x++) {
ctx.fillStyle = (x+y)%2 === 0 ? '#192d20' : '#1b3022';
ctx.fillRect(x*CELL,y*CELL,CELL,CELL);
}
ctx.strokeStyle = '#c6eea809'; ctx.lineWidth = 1;
for (let i=0;i<=N;i++) {
ctx.beginPath(); ctx.moveTo(i*CELL+.5,0); ctx.lineTo(i*CELL+.5,SIZE); ctx.stroke();
ctx.beginPath(); ctx.moveTo(0,i*CELL+.5); ctx.lineTo(SIZE,i*CELL+.5); ctx.stroke();
}
if (food) {
const fx=(food.x+.5)*CELL, fy=(food.y+.5)*CELL;
const pulse = 1 + Math.sin(time/260)*.07;
ctx.save(); ctx.translate(fx,fy); ctx.scale(pulse,pulse);
ctx.shadowColor='#ff8264'; ctx.shadowBlur=20;
ctx.fillStyle='#ff997a'; ctx.beginPath(); ctx.arc(0,2,9,0,Math.PI*2); ctx.fill();
ctx.shadowBlur=0;
ctx.fillStyle='#ffd2b1'; ctx.beginPath(); ctx.arc(-3,-1,2.1,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#caff91'; ctx.lineWidth=2; ctx.lineCap='round'; ctx.beginPath(); ctx.moveTo(0,-7); ctx.quadraticCurveTo(1,-12,5,-12); ctx.stroke();
ctx.restore();
}
const alpha = phase === 'paused' || phase === 'over' ? 1 : Math.min(1, accumulator/STEP);
for (let i=snake.length-1;i>=0;i--) {
const curr=snake[i], prev=previous[Math.min(i,previous.length-1)] || curr;
const x=(prev.x+(curr.x-prev.x)*alpha)*CELL;
const y=(prev.y+(curr.y-prev.y)*alpha)*CELL;
const isHead=i===0;
ctx.save();
if (isHead) { ctx.shadowColor='#b9ff6d'; ctx.shadowBlur=17; }
const gradient=ctx.createLinearGradient(x,y,x+CELL,y+CELL);
if (isHead) { gradient.addColorStop(0,'#e0ff9e'); gradient.addColorStop(1,'#a7ef65'); }
else { gradient.addColorStop(0, i%2 ? '#9de878' : '#a9ef7c'); gradient.addColorStop(1,'#6dc461'); }
ctx.fillStyle=gradient;
rounded(x+2,y+2,CELL-4,CELL-4,isHead?9:8); ctx.fill();
ctx.restore();
if (isHead) {
const cx=x+CELL/2, cy=y+CELL/2;
const dx=direction.x, dy=direction.y;
const px=-dy, py=dx;
ctx.fillStyle='#1b3923';
for (const side of [-1,1]) {
ctx.beginPath(); ctx.arc(cx+dx*5+px*5*side,cy+dy*5+py*5*side,2.15,0,Math.PI*2); ctx.fill();
}
}
}
for (const p of particles) {
ctx.globalAlpha=Math.max(0,p.life/p.max);
ctx.fillStyle=p.color;
ctx.beginPath(); ctx.arc(p.x,p.y,p.r,0,Math.PI*2); ctx.fill();
}
ctx.globalAlpha=1;
}
function frame(time) {
const dt = lastTime ? Math.min((time-lastTime)/1000,.05) : 0;
lastTime=time; foodPulse=time;
if (phase === 'demo' || phase === 'playing') {
accumulator += dt*1000;
while (accumulator >= STEP && (phase === 'demo' || phase === 'playing')) {
accumulator -= STEP;
tick();
}
particles.forEach(p => { p.x+=p.vx*dt; p.y+=p.vy*dt; p.vy+=90*dt; p.life-=dt; });
particles=particles.filter(p=>p.life>0);
}
drawBoard(foodPulse);
requestAnimationFrame(frame);
}
document.addEventListener('keydown', e => {
const key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
const dir = keyDirs[key];
if (dir || key === ' ' || key === 'Enter' || key === 'p') e.preventDefault();
if (phase === 'demo') {
reset('playing');
if (dir) enqueue(dir);
return;
}
if (phase === 'over') {
if (key === ' ' || key === 'Enter') reset('playing');
return;
}
if (key === 'p') { togglePause(); return; }
if (phase === 'playing' && dir) enqueue(dir);
});
document.getElementById('board').addEventListener('click', () => {
if (phase === 'demo') reset('playing');
});
overlayButton.addEventListener('click', e => {
e.stopPropagation();
if (phase === 'paused') togglePause();
else reset('playing');
});
document.querySelectorAll('[data-dir]').forEach(button => {
button.addEventListener('click', () => {
if (phase === 'demo' || phase === 'over') reset('playing');
if (phase === 'playing') enqueue(button.dataset.dir);
});
});
reset('demo');
requestAnimationFrame(frame);
})();
</script>
</body>
</html>Reasoning trace (1,268 tokens)
**Planning game development** I need to create a polished game that’s responsive. I should have a self-playing demo behind a start message that’s meaningful and visible. I’ll design a dark page with a header logo, score cards, and a centered canvas board. The sidebar might be around 1280, while the game stage should be about 560 square and responsive. I need to track the snake’s grid position and ensure smooth movement with interpolation. The snake AI should safely navigate to food.**Constructing the cycle** I’m designing a 22x22 cycle for the snake game. My plan starts at (0,0), where I’ll traverse the top row across to (21,0) and then descend. The cycle should cover all spaces, alternating between rows as I go down. This creates a loop, and the AI will follow it to look active. I can even place food ahead on the cycle and start the snake’s initial position along the row, like at (7,10), to encourage early action.**Designing the game flow** I could use a BFS shortcut, but the cycle approach seems adequate. I plan to set the food 8 steps ahead, and once it's eaten, it can spawn randomly. I want the initial snake snapshot to be interesting, positioned at row 10 from x1 to 21, with the head at (9,10) and food at (13,10). I’ll ensure the demo phases are clear — playing, paused, and over — while avoiding opaque overlays. A banner overlay will guide users, and I'll design a header in neon with a dark background for a vibrant feel.**Setting up the game board** I’m designing the game board as a 660x660 canvas with a grid of 22 squares. The header will be positioned at the top. This setup will give players a clear view of the game area. With the grid in place, I can ensure that the snake’s movements are organized and easy to follow. It’s crucial to have an appealing layout for a better user experience!**Evaluating AI Initialization Logic** I'm working on an AI cycle initialization, specifically looking at row 10, column 9. I need to compute the cycle and select previous positions for the best outcomes. For the demo, I'm choosing a food item that corresponds to head +5, and planning a reset for consistency. I’ll also spawn randomly in a regular manner, keeping controls accessible. Plus, the game over overlay should be centered for clarity. Let's make sure everything flows smoothly!