← Back to home ← Volver al inicio

CreatedCreado UpdatedActualizado

Greedy

Snake, but the snake is a language model — eat the next token and the body becomes the sentence you decode. Built entirely by AI; the write-up below explains how. Snake, pero la serpiente es un modelo de lenguaje — cómete el siguiente token y el cuerpo se vuelve la oración que decodificas. Construido completamente por IA; el artículo de abajo explica cómo.

scorepuntos 0 comboracha ×1 losspérdida

Greedy

You are the language model — again. Steer the snake to eat the token that should come next. Each token you swallow is appended to your output, so the snake's body is the sentence you're generating, one greedy pick at a time.

  • ← ↑ → ↓ or W A S D to turn · on-screen arrows or swipe on touch
  • Eat the right token: points × combo · a wrong one: loss climbs
  • Bite your own trail (a repetition loop) or hit the wall → run over

Tú eres el modelo de lenguaje — otra vez. Guía la serpiente para comerte el token que sigue. Cada token que tragas se añade a tu salida, así que el cuerpo de la serpiente es la oración que estás generando, una decisión codiciosa a la vez.

  • ← ↑ → ↓ o W A S D para girar · flechas en pantalla o desliza en táctil
  • Come el token correcto: puntos × racha · uno errado: sube la pérdida
  • Muerde tu propia cola (un bucle de repetición) o choca con el muro → fin
speed velocidad

Paused En pausa

Generation halted Generación detenida

You bit your own trail — the model fell into a repetition loop. Mordiste tu propia cola — el modelo cayó en un bucle de repetición.

You ran off the edge — the model lost the thread. Te saliste del borde — el modelo perdió el hilo.

Too many nonsense tokens — the model lost coherence. Demasiados tokens disparatados — el modelo perdió la coherencia.

scorepuntos 0 bestrécord 0 tokens decodedtokens decodificados 0

your loss curve tu curva de pérdida

speed velocidad

← ↑ → ↓ / W A S D turn · P pauses · tap the on-screen arrows or swipe on touch ← ↑ → ↓ / W A S D para girar · P pausa · toca las flechas en pantalla o desliza en táctil

How Greedy Was Built

This is the second entry in the “you are the model” series that began with Token Rain. Same premise — predict the next token — but a completely different game. Token Rain is a falling-token catcher. Greedy is the oldest arcade game there is, Snake, rebuilt so that the snake itself is a language model generating text.


A tiny history of Snake

Snake is almost as old as video games themselves. The mechanic — a line that grows as it eats and dies if it crosses itself — first appeared in the 1976 arcade game Blockade, was cloned for years under names like Worm and Nibbler, and became a worldwide household name in 1998 when Nokia preloaded it on the 6110 mobile phone, putting it in hundreds of millions of pockets. It’s now one of the most re-implemented games ever — which, conveniently, makes it perfect to reimagine here. (Full lineage: Snake on Wikipedia.)

The idea: greedy decoding

A language model produces a sentence one token at a time. At each step it scores every possible next token and — in the simplest decoding strategy of all — just takes the single highest-scoring one. That strategy has a name: greedy decoding. Append the token, look again, take the next best. The chain of choices is the output.

That’s the game:

  • The sentence at the top is the context — what’s been generated so far, with a blinking blank for the token you owe next.
  • Scattered on the grid are candidate tokens. One genuinely belongs; the rest are low-probability nonsense (a real model scores “banana” too — it just scores it terribly). They look identical, so you decide by reading, exactly like the model does.
  • You steer the snake to eat the right token. Each correct token is appended to your output and the snake grows by one segment — so the snake’s body is the running text you’ve decoded, newest at the head, oldest at the tail.

The name is a double pun. “Greedy Snake” is what the classic game is called in much of the world, and greedy is the name of the decoding rule the game dramatizes — the snake greedily eating the best next token is greedy decoding, made literal.

Two failure modes, and both are real things that happen to language models:

  • Bite your own trail → a repetition loop. When a model’s generation curls back onto itself, it can get stuck repeating — a genuine, well-known degenerate failure mode. In the game it’s lethal: the longer your output, the more of the board your own history occupies, and the easier it is to trap yourself. A long context is a gift and a hazard.
  • Run off the edge → you lost the thread. The arena is bounded; wander out and the generation ends.

And a soft fail you share with Token Rain: every nonsense token you swallow pushes your loss up, and idling without decoding lets perplexity creep up too. Fill the loss bar and the model loses coherence. When the run ends you get your own loss curve — the same chart researchers stare at during real training.

How the AI built it

Same editor/author split as the other essays on this site, and the same one used for Token Rain:

  1. The brief (human). “Make a second game, based on Snake. Keep it unique — don’t just reskin Snake — and tie it to the AI ideas the site already teaches. Same look and feel as Token Rain.”
  2. Research (AI). Claude reread the site’s essays and Token Rain’s source, so the new game could reuse its sentence bank, its distractor vocabulary, its loss bar, and its palette — putting both games in the same universe instead of next to each other.
  3. The design decision (AI). Snake’s signature is a body that grows and can trap you. The insight that made the concept click was mapping that one mechanic onto two real model behaviors at once: the growing body is the generated text, and self-collision is a repetition loop. Once those lined up, the rest of the game fell out of the metaphor.
  4. Implementation (AI). One Astro component: plain JavaScript on a <canvas>, no game engine, no libraries, no assets. Bilingual, and it reads the site’s colors from CSS variables so it can’t fall out of theme.
  5. Review (human). Play-testing, tuning the speed and the loss numbers, and the decision to ship.

The JavaScript inside (and the tricky part)

If you’re learning to code, here’s what’s doing the work. View source — all of it is readable.

The tricky part: a grid that ticks, a screen that doesn’t

This is the one genuinely hard thing in a snake game, and it’s worth slowing down for.

Snake’s logic is discrete: the snake occupies whole grid cells and jumps exactly one cell per tick. But the screen refreshes 60 (or 120, or 144) times a second. If you only redraw on each tick, the snake teleports cell-to-cell and looks like a 1997 phone. If you move the logic every frame instead, the snake leaves the grid and every collision test turns into messy floating-point math.

The fix is to keep them separate. The grid steps on a fixed timer (a fixed-timestep accumulator); the renderer runs every frame and interpolates the visual position between ticks:

acc += dt;                       // pile up real time
while (acc >= tickInterval) {    // spend it one whole tick at a time
  acc -= tickInterval;
  step();                        // discrete grid logic: move, eat, collide
}
const t = acc / tickInterval;    // 0…1: how far into the current tick we are

Then each segment is drawn sliding from where it was to where it is:

// snapshot the cells right before a tick, then lerp toward the new cells
const from = oldCells[i] || cell;          // (grow-tick tail has no "old" cell)
const x = lerp(centerX(from), centerX(cell), t);
const y = lerp(centerY(from), centerY(cell), t);

The subtlety that bites everyone: on the tick where the snake grows, the new tail segment didn’t exist a moment ago, so it has no previous cell to slide from. Interpolate it anyway and the whole tail visibly snaps a cell sideways every time you eat. The fix is the little oldCells[i] || cell — if a segment has no past, it stays put. One ||, but finding out why the tail twitched is an afternoon. That’s the trickiest part of the whole game.

The famous Snake bug: turning into yourself

Press Down then Up faster than one tick and a naive snake reverses straight into its own neck and dies — through no fault of the player. The same gap (input arrives continuously, the snake turns discretely) causes it. The fix is to buffer turns in a small queue, commit at most one per tick, and reject any 180° reversal against the committed heading, not the latest keypress:

function queueDir(x, y) {
  if (dirQueue.length >= 2) return;            // hold at most two turns
  const last = dirQueue.at(-1) || dir;
  if (x === -last.x && y === -last.y) return;   // never allow a reversal
  dirQueue.push({ x, y });
}

Buffering two turns is what lets a quick “right, then down” around a corner both land, on consecutive ticks, instead of the second keypress erasing the first.

Collision is just integer equality

Token Rain needed real rectangle-overlap math (AABB) because its tokens fell at any pixel position. On a grid, everything snaps to whole cells, so “did I hit it?” is just a.x === b.x && a.y === b.y. Eating a token, biting yourself, hitting a wall — all integer comparisons. Putting the game on a grid deleted an entire category of code.

Everything reused from Token Rain

The state machine (title / playing / paused / over mirrored onto a data-state attribute, with CSS deciding which panel shows), the loss bar, the end-of-run loss curve, the bilingual text, the particle bursts and floating score text, and the palette pulled from CSS variables — all of it came straight over from Token Rain. The new code is really just the grid, the snake, and the tick/interpolation bridge. Reuse is most of why a second, different game took a single session.

Why this game won’t get a cease-and-desist

As the history above shows, Snake is one of the most-cloned ideas in software — no single company owns it. Game mechanics aren’t copyrightable; only a specific game’s expression is. Greedy borrows the public-domain mechanic and dresses it in its own expression: a body that spells out decoded tokens, death-by-repetition-loop, this site’s palette, and Token Rain’s data. Original expression on a free mechanic is the clean path.

Try it yourself

The lesson is the same one Token Rain ended on, now with a second data point: the reusable skill isn’t any one trick, it’s the workflow. Pick something you understand well enough to judge, write a tight brief, let the AI propose and build, and make it explain every hard part — like the tick-versus-frame interpolation above. You end up with working software and the understanding to maintain it. That’s learning by doing, with a very patient lab partner.

Cómo se construyó Greedy

Esta es la segunda entrega de la serie “tú eres el modelo” que empezó con Token Rain. La misma premisa — predecir el siguiente token — pero un juego completamente distinto. Token Rain es un juego de atrapar tokens que caen. Greedy es el juego de arcade más viejo que existe, Snake, reconstruido para que la serpiente misma sea un modelo de lenguaje generando texto.


Una pequeña historia de Snake

Snake es casi tan viejo como los videojuegos mismos. La mecánica — una línea que crece al comer y muere si se cruza consigo misma — apareció por primera vez en el juego de arcade Blockade de 1976, se clonó durante años con nombres como Worm y Nibbler, y se volvió un nombre conocido en todo el mundo en 1998 cuando Nokia lo trajo precargado en el teléfono 6110, metiéndolo en cientos de millones de bolsillos. Hoy es uno de los juegos más reimplementados que existen — lo cual, convenientemente, lo hace perfecto para reimaginarlo aquí. (Linaje completo: Snake en Wikipedia.)

La idea: decodificación codiciosa

Un modelo de lenguaje produce una oración un token a la vez. En cada paso le da un puntaje a cada token posible y — en la estrategia de decodificación más simple de todas — simplemente toma el de mayor puntaje. Esa estrategia tiene nombre: decodificación codiciosa (greedy decoding). Añade el token, mira otra vez, toma el siguiente mejor. La cadena de decisiones es la salida.

Ese es el juego:

  • La oración de arriba es el contexto — lo que se ha generado hasta ahora, con un espacio en blanco parpadeando por el token que debes ahora.
  • Repartidos por la cuadrícula hay tokens candidatos. Uno de verdad encaja; el resto son disparates de baja probabilidad (un modelo real también puntúa “banana” — solo que le da un puntaje pésimo). Se ven idénticos, así que decides leyendo, igual que el modelo.
  • Guías la serpiente para comerte el token correcto. Cada token correcto se añade a tu salida y la serpiente crece un segmento — así que el cuerpo de la serpiente es el texto que vas decodificando, lo más nuevo en la cabeza, lo más viejo en la cola.

El nombre es un doble juego de palabras. “Greedy Snake” (serpiente codiciosa) es como se llama el juego clásico en buena parte del mundo, y greedy es el nombre de la regla de decodificación que el juego dramatiza — la serpiente comiéndose codiciosamente el mejor siguiente token es la decodificación codiciosa, hecha literal.

Dos formas de perder, y ambas son cosas reales que les pasan a los modelos de lenguaje:

  • Muerde tu propia cola → un bucle de repetición. Cuando la generación de un modelo se enrolla sobre sí misma, se puede quedar atascado repitiendo — una falla degenerada genuina y muy conocida. En el juego es letal: mientras más larga tu salida, más tablero ocupa tu propia historia, y más fácil es atraparte solo. Un contexto largo es un regalo y un peligro.
  • Sálete del borde → perdiste el hilo. El área es limitada; si te vas afuera, la generación termina.

Y una falla suave que compartes con Token Rain: cada token disparatado que tragas sube tu pérdida, y quedarte dando vueltas sin decodificar también deja que la perplejidad suba poco a poco. Si llenas la barra de pérdida, el modelo pierde la coherencia. Al final de la partida recibes tu propia curva de pérdida — la misma gráfica que los investigadores miran fijamente durante un entrenamiento real.

Cómo lo construyó la IA

La misma división editor/autor que los otros ensayos del sitio, y la misma que se usó para Token Rain:

  1. El encargo (humano). “Haz un segundo juego, basado en Snake. Que sea único — no solo un reskin de Snake — y conéctalo con las ideas de IA que el sitio ya enseña. La misma estética de Token Rain.”
  2. Investigación (IA). Claude releyó los ensayos del sitio y el código de Token Rain, para que el nuevo juego pudiera reutilizar su banco de oraciones, su vocabulario de distractores, su barra de pérdida y su paleta — poniendo ambos juegos en el mismo universo en vez de uno al lado del otro.
  3. La decisión de diseño (IA). La firma de Snake es un cuerpo que crece y te puede atrapar. La idea que hizo encajar el concepto fue mapear esa única mecánica a dos comportamientos reales de los modelos a la vez: el cuerpo que crece es el texto generado, y chocar contigo mismo es un bucle de repetición. Una vez que esos dos se alinearon, el resto del juego salió de la metáfora.
  4. Implementación (IA). Un solo componente de Astro: JavaScript puro sobre un <canvas>, sin motor de juegos, sin librerías, sin assets. Bilingüe, y lee los colores del sitio desde variables CSS para no desentonar con el tema.
  5. Revisión (humano). Pruebas jugando, ajustar la velocidad y los números de la pérdida, y la decisión de publicar.

El JavaScript por dentro (y la parte difícil)

Si estás aprendiendo a programar, esto es lo que hace el trabajo. Mira el código fuente — todo es legible.

La parte difícil: una cuadrícula que avanza por tics, una pantalla que no

Esto es lo único genuinamente difícil en un juego de Snake, y vale la pena ir despacio.

La lógica de Snake es discreta: la serpiente ocupa celdas enteras de la cuadrícula y salta exactamente una celda por tic. Pero la pantalla se refresca 60 (o 120, o 144) veces por segundo. Si solo redibujas en cada tic, la serpiente se teletransporta de celda en celda y se ve como un teléfono de 1997. Si en cambio mueves la lógica en cada frame, la serpiente se sale de la cuadrícula y cada prueba de colisión se vuelve matemática de punto flotante engorrosa.

La solución es mantenerlos separados. La cuadrícula avanza con un temporizador fijo (un acumulador de paso fijo); el renderizador corre en cada frame e interpola la posición visual entre tics:

acc += dt;                       // acumula tiempo real
while (acc >= tickInterval) {    // gástalo de un tic entero a la vez
  acc -= tickInterval;
  step();                        // lógica discreta: mover, comer, colisionar
}
const t = acc / tickInterval;    // 0…1: qué tan adentro del tic actual vamos

Luego cada segmento se dibuja deslizándose desde donde estaba hacia donde está:

// captura las celdas justo antes de un tic, luego interpola hacia las nuevas
const from = oldCells[i] || cell;          // (la cola del tic-de-crecer no tiene celda "vieja")
const x = lerp(centroX(from), centroX(cell), t);
const y = lerp(centroY(from), centroY(cell), t);

El detalle que sorprende a todos: en el tic en que la serpiente crece, el nuevo segmento de la cola no existía un instante antes, así que no tiene celda previa desde la cual deslizarse. Interpólalo de todos modos y toda la cola da un salto visible de una celda hacia el lado cada vez que comes. El arreglo es ese pequeño oldCells[i] || cell — si un segmento no tiene pasado, se queda quieto. Un solo ||, pero averiguar por qué temblaba la cola es una tarde entera. Esa es la parte más difícil de todo el juego.

El famoso bug de Snake: girar hacia ti mismo

Presiona Abajo y luego Arriba más rápido que un tic y una serpiente ingenua se reversa directo hacia su propio cuello y muere — sin culpa del jugador. La misma brecha (la entrada llega de forma continua, la serpiente gira de forma discreta) lo causa. El arreglo es almacenar los giros en una cola pequeña, ejecutar como máximo uno por tic, y rechazar cualquier reversa de 180° contra el rumbo ya ejecutado, no contra la última tecla:

function queueDir(x, y) {
  if (dirQueue.length >= 2) return;            // guarda como máximo dos giros
  const last = dirQueue.at(-1) || dir;
  if (x === -last.x && y === -last.y) return;   // nunca permitas una reversa
  dirQueue.push({ x, y });
}

Guardar dos giros es lo que permite que un rápido “derecha, luego abajo” para doblar una esquina aterrice completo, en tics consecutivos, en vez de que la segunda tecla borre la primera.

La colisión es solo igualdad de enteros

Token Rain necesitaba matemática real de superposición de rectángulos (AABB) porque sus tokens caían en cualquier posición de píxel. En una cuadrícula, todo se ajusta a celdas enteras, así que “¿le pegué?” es solo a.x === b.x && a.y === b.y. Comerse un token, morderte a ti mismo, chocar con un muro — todas son comparaciones de enteros. Poner el juego en una cuadrícula borró una categoría entera de código.

Todo lo reutilizado de Token Rain

La máquina de estados (title / playing / paused / over reflejada en un atributo data-state, con el CSS decidiendo qué panel se ve), la barra de pérdida, la curva de pérdida del final, el texto bilingüe, los estallidos de partículas y el texto de puntaje flotante, y la paleta sacada de variables CSS — todo eso vino directo de Token Rain. El código nuevo es realmente solo la cuadrícula, la serpiente y el puente tic/interpolación. La reutilización es buena parte de por qué un segundo juego, distinto, tomó una sola sesión.

Por qué este juego no va a recibir una carta de cese y desistimiento

Como muestra la historia de arriba, Snake es una de las ideas más clonadas del software — ninguna empresa es dueña de ella. Las mecánicas de juego no son protegibles por derechos de autor; solo lo es la expresión de un juego específico. Greedy toma la mecánica de dominio público y la viste con su propia expresión: un cuerpo que deletrea los tokens decodificados, la muerte por bucle de repetición, la paleta de este sitio y los datos de Token Rain. Expresión original sobre una mecánica libre es el camino limpio.

Pruébalo tú mismo

La lección es la misma con la que terminó Token Rain, ahora con un segundo dato: la habilidad reutilizable no es ningún truco en particular, es el flujo de trabajo. Escoge algo que entiendas lo suficiente como para juzgarlo, escribe un encargo claro, deja que la IA proponga y construya, y hazla explicar cada parte difícil — como la interpolación entre tic y frame de arriba. Terminas con software que funciona y con el entendimiento para mantenerlo. Eso es aprender haciendo, con un compañero de laboratorio bien paciente.