GoKart: un juego de carreras al estilo Mario Kart que Auto-Pilot construyó en una tarde
Dale a Auto-Pilot de Agent! un único objetivo - "crea un clon de Mario Kart llamado GoKart" - y vuelve para encontrarte un juego de carreras en Godot 4 con tres circuitos, ocho objetos, rivales controlados por IA y 3.344 comprobaciones de test en verde. Dos días y 87 commits del agente después es GoKart 0.0.2, con cuatro circuitos, modo Batalla, Contrarreloj y un menú al estilo Mario Kart 64. Esto es lo que dice el registro que ocurrió de verdad, incluidas las partes en las que se quedó atascado.
Actualizado el 4 de octubre: la entrada ahora sigue la historia durante los dos días posteriores a aquella primera tarde, incluida la noche en que dos sesiones de Auto-Pilot compartieron un mismo repositorio, y termina con la versión GoKart 0.0.2. Las capturas se volvieron a tomar desde la etiqueta 0.0.2.
La entrada de ayer presentó Auto-Pilot: escribe /auto <objetivo> en Agent! para Mac y ejecuta ciclos sin supervisión hacia ese objetivo hasta que pulsas Stop All. Esta entrada va de lo que salió por el otro lado cuando lo apunté a un juego.
El objetivo, pegado más o menos tal como lo escribí:
create a Mario Kart clone called GoKart with all Mario Kart effects. I believe Godot 4 can do the Mario Kart effects, but I haven't built any of them yet: drift sparks and boost flames (GPUParticles3D), speed lines and boost blur (screen-space shaders, glow and tonemapping), item effects and tire trails (shaders plus ribbon meshes), kart movement (VehicleBody3D or custom arcade physics). Write unit tests and test the game frequently.
Presupuesto: sin límite de tiempo, ciclos ilimitados. El modelo dentro de Agent! en todos los ciclos fue Claude Sonnet 5.5. El primer commit llegó a las 12:39. A las 16:32 de esa misma tarde el repositorio tenía 33 commits, 47 archivos GDScript, unas 5.500 líneas de GDScript y código de shaders, y una suite unitaria que pasaba 3.344 comprobaciones. Dos días después, en la etiqueta 0.0.2, esas cifras habían crecido hasta 126 commits, 150 archivos, unas 24.500 líneas y 14.134 comprobaciones. Cada uno de esos commits tiene al agente como autor. Todo está en GitHub, en AgentiLoop/GoKart.
Qué hace Auto-Pilot, ciclo a ciclo
Auto-Pilot mantiene un registro continuo en .agent/autopilot/progress.md dentro del proyecto, y cada ciclo añade lo que hizo, lo que asumió, lo que queda y si algo lo bloqueó. Ese registro es la fuente de todo lo que sigue. Lo cito en lugar de fiarme de mi memoria porque el registro es más honesto que yo.
El ciclo 1 no tenía Godot en la máquina. Ejecutó brew install --cask godot, creó un enlace simbólico al binario, hizo git init y escribió kart_physics.gd como un modelo puro sin dependencias de escena: aceleración, frenado, marcha atrás, fricción, dirección, un derrape bloqueado en la dirección en la que lo iniciaste y tres niveles de miniturbo. Eligió a propósito una física arcade propia en lugar de VehicleBody3D, y explicó por qué: "para que el manejo al estilo Mario Kart sea fácil de probar con tests unitarios". Once tests, 17 comprobaciones, primer commit.
El ciclo 2 añadió los efectos que había pedido por su nombre: chispas de derrape con GPUParticles3D coloreadas según el nivel de miniturbo, llamas de escape, estelas de neumáticos con mallas de cinta y un shader de espacio de pantalla para el desenfoque radial del turbo, las líneas de velocidad y una viñeta. 43 comprobaciones.
El ciclo 3 construyó un circuito. Un bucle Catmull-Rom remuestreado cada 3 metros, una cinta de carretera, muros a rayas rojas y blancas con colisionadores, plataformas de turbo con un shader de chevrones desplazándose, ocho checkpoints ordenados y un contador de vueltas que se niega a contar atajos o vueltas en sentido contrario. Uno de los nuevos tests es un bot de persecución que conduce tres vueltas completas usando el modelo de física real. Terminó en 1:02.5. 90 comprobaciones.
El ciclo 4 añadió cajas de objetos con un shader fresnel arcoíris, una ruleta y los tres primeros objetos: champiñón, plátano y un caparazón verde que rebota en los muros. Recibir un impacto hace girar el kart durante 1,2 segundos. 235 comprobaciones.
Y entonces se quedó atascado.
La parte en la que se quedó atascado
El siguiente ciclo empezó a añadir karts controlados por IA y una carrera de humo sin interfaz, y nunca volvió. La causa, encontrada en la sesión siguiente, fue un único tabulador perdido en la línea 115 de main.gd: un error de análisis que hizo que el script de humo escupiera errores sin parar, sin límite de tiempo en el comando de shell que lo ejecutaba.
Pulsé Stop All y abrí una sesión nueva con el mismo objetivo más una nota en mayúsculas que no reproduciré entera. La esencia: te quedaste atascado probando la carrera de humo, no te quedes atascado, pon un límite de tiempo al shell.
El primer ciclo de la segunda sesión arregló el tabulador, redujo la anticipación de la IA de 10 muestras de carretera a 6 para que la IA dejara de cortar curvas contra el muro interior, añadió un limitador de velocidad en curva y envolvió cada ejecución de shell en perl -e 'alarm 100; exec @ARGV', porque macOS viene sin el comando timeout. A partir de ahí, el registro dice "todas las ejecuciones estuvieron bajo límites de perl alarm" al final de casi todos los ciclos. Aprendió la lección del texto del objetivo y siguió aplicándola.
Esa sesión ejecutó 15 ciclos en aproximadamente una hora y cuarto, y cada uno es una funcionalidad: cuenta atrás de salida con turbo de salida relámpago para quien acelera en el momento justo; estallidos al subir de nivel de miniturbo y un destello en el borde de la pantalla; un minimapa; el caparazón rojo teledirigido y la estrella; un modelo de kart procedural con ruedas que giran, ruedas delanteras que doblan y un piloto que gira la cabeza; un rayo que encoge a todos los rivales; caparazones triples que orbitan el kart; el caparazón azul con púas que persigue al líder por la carretera; una pantalla de resultados con puntos; audio totalmente sintetizado sin archivos de audio, desde el bucle del motor hasta la melodía de meta; un menú de título con un segundo circuito; una opción de número de vueltas; un tercer circuito; y zumbido de motor 3D posicional en cada kart de la IA.
La parte en la que fue honesto
Lo que más me gusta del registro es el patrón de confesiones. Agent! no puede mirar un PNG, y lo dijo, ciclo tras ciclo:
La ejecución de capturas en ventana no registró errores, pero no puedo ver imágenes, así que no he mirado cómo se renderizan el circuito o el HUD.
No he oído los sonidos, y no ejecuté la carrera de humo ni la herramienta de capturas.
No puedo ver imágenes con mis herramientas, así que esa revisión necesita a una persona.
Así que probó lo que podía: comprobaciones de escena sin interfaz que instancian la escena real y hacen aserciones sobre el estado. Lanzar un rayo, confirmar que tres rivales están a escala 0,5 y girando y que los relámpagos se limpian después. Lanzar un caparazón azul contra una parrilla de salida apiñada, confirmar la explosión en el fotograma 12 y dos karts girando. Forzar al jugador a terminar, confirmar que el piloto automático entrega el kart a un AiDriver y que el panel de resultados muestra "1st YOU".
Una tercera sesión se atascó de la misma manera, esta vez detrás de una alarma de 240 segundos que era demasiado generosa, y la detuve tras un ciclo. Entonces miró una persona. Esa persona fui yo, y el objetivo de la cuarta sesión fueron mis notas de prueba, ligeramente ordenadas:
the UI needs to scale with the window. the UI should not be prone to the screen speed effects and blurry. Should be able to use cursor keys. it's not clear when power ups are released. the tops of the walls flicker. Little too hard to steer the kart. hard to keep up with the computer AI karts. maybe have AI difficulty levels Easy Medium and Hard.
Un ciclo después: estirado de canvas items para que la UI escale con la ventana, el HUD movido a una capa de canvas por encima de la superposición de efectos de velocidad, teclas de flecha, Enter y Ctrl para los objetos con una pista en pantalla, dirección con entrada suavizada, un arreglo del z-fighting en las rayas de los muros (los segmentos rojos y blancos vecinos se peleaban por los mismos píxeles, así que los blancos se hicieron ligeramente más grandes), MSAA 4x, y Fácil / Medio / Difícil, que escalan la velocidad máxima de la IA a 0,72, 0,85 y 1,0. Los tests pasaron de 2.156 a 3.344 comprobaciones, en parte porque también encontró y arregló un error de análisis preexistente en tests/test_items.gd que impedía que la suite se cargara.
La parte en la que se detuvo a sí mismo
Los ciclos 2 a 8 de esa última sesión no hicieron cambios de código. Cada uno releyó el repositorio, volvió a ejecutar la suite y escribió una variante del mismo párrafo:
No pude ver el resultado en pantalla, así que no declaro el objetivo alcanzado. Cuatro puntos siguen necesitando que un humano los pruebe en el juego.
Ese es el comportamiento correcto. El objetivo era "la dirección se siente mal" y "los muros parpadean", y ningún test sin interfaz puede cerrar ese objetivo. Auto-Pilot no tiene límite de iteraciones, así que habría seguido comprobando para siempre. La sesión terminó tras el ciclo 8, y lo que GoKart necesitaba a continuación era una partida de prueba, no otro ciclo. Esa tarde el repositorio se etiquetó como 0.0.1 y se exportó para macOS, Windows y Linux.
Dos días después: dos Auto-Pilots en un mismo repositorio
El 3 de octubre volví con un tipo de objetivo distinto. Esta vez no una lista de funcionalidades, sino una referencia:
keep building GoKart to resemble Mario Kart Nintendo 64 version. search Mario Kart N64 or Mario Kart Nintendo 64 and keep improving, iterating, making GoKart better
Esa quinta sesión empezó a las 14:19 y ejecutó 28 ciclos, y casi cada ciclo es una cosa de Mario Kart 64 que el agente buscó y después construyó. Primero llegó la estructura del juego: una parrilla de 8 corredores en dos columnas, rubber-banding según la dificultad, 50cc, 100cc y 150cc más la clase Extra en espejo, karts Ligero, Medio y Pesado que se empujan entre sí, y un Grand Prix con 9/6/3/1 puntos y la regla de eliminación por puesto. Luego los otros modos: Contrarreloj con un fantasma, y modo Batalla con globos en Big Donut, Block Fort y Skyscraper. Luego se completó el conjunto de objetos con champiñones triples y dorados, la caja de objetos falsa, el racimo de plátanos, Boo, caparazones rojos triples y el bloqueo con caparazón, y llegó Lakitu para señalar la salida en falso, dar la señal de salida y levantar los carteles de vuelta.
El resto de la tarde se fue en los propios circuitos. Un cuarto, Dusty Canyon, llegó con un tren al estilo Kalimari Desert y pasos a nivel. Sunset Speedway recibió el tráfico de Toad's Turnpike. Frosty Peaks recibió el hielo de Sherbet Land, muñecos de nieve y pingüinos, Green Hills recibió Monty Moles, y cada circuito recibió decorado al borde de la pista acorde a su tema, junto con el rebufo, una rampa de salto, el derrape con saltito y alternancia, y un bucle chiptune renderizado desde patrones de pasos en código.
Cuatro horas después, a las 18:25, abrí una segunda pestaña e inicié un segundo Auto-Pilot sobre el mismo repositorio, con un objetivo más acotado:
the menus are not Mario Kart Quality and neither is the title shot. and there is over use of black outlines on text everywhere. see Mario Kart 64 screenshots and images on the web and make better menus. focus only on the menus / screens and title shot for GoKart. make conscious decisions. do not conflict with previous /auto working on the application
Así que desde las 18:25 hasta la medianoche dos agentes estuvieron haciendo commits en el mismo árbol de trabajo. La sesión de menús reconstruyó la pantalla de título en torno a un logo en arco con degradado que entra volando y rebota, y luego fue recorriendo cada pantalla que hay detrás: una pantalla de selección con una imagen junto a cada circuito y un sobrevuelo en vivo del que has elegido, filas de opciones con barras iluminadas, un retrato del kart girando y un cursor dorado, un sobrevuelo de presentación del circuito antes de cada carrera, una pantalla de pausa y un tablero de resultados cuyas filas entran deslizándose una tras otra. Debajo de todo ello puso una paleta compartida de texto redondeado dorado y crema con sombras, que sustituyó cada contorno negro de 8 píxeles del juego. Ejecutó 23 ciclos y declaró el objetivo alcanzado a las 23:54.
La línea de "do not conflict" hizo un trabajo real. El registro está lleno de las dos sesiones esquivándose mutuamente. La sesión de menús verificaba desde un git worktree limpio en HEAD para que "el trabajo en curso de los pingüinos de la otra sesión" quedara fuera de sus ejecuciones de tests. Cuando se lo pedí, una sesión terminó la funcionalidad a medias de la otra. Los commits se prepararon archivo por archivo en lugar de con git add -A, porque las ediciones de la otra pestaña estaban en el mismo árbol. No fue ordenado, pero no se perdió nada y la suite terminó la noche con 14.134 en verde, 0 fallos.
El registro de la sesión de funcionalidades termina tres minutos después que el de la sesión de menús, a las 23:57, con "Session ended — Stop All". Mi nota a Agent! justo después, que guardó como mensaje del siguiente commit de checkpoint, fue que Stop All debe detener solo la pestaña en la que se pulsa, no todas. Con dos Auto-Pilots en marcha, un botón para ambos es el botón equivocado.
Sobre estas capturas
Las tomó Agent!, no yo, y no a mano. Para la primera versión de esta entrada pedí tres capturas de una conducción aleatoria, y escribió un tools/random_drive.gd de 70 líneas que sigue la carretera con un desplazamiento de carril errante, mete ráfagas de derrape aleatorias, dispara el objeto que lleve en momentos aleatorios y guarda un fotograma cada varios cientos de fotogramas de física. La captura de Sunset Speedway de arriba es un fotograma de ese bot.
El resto proviene de las herramientas de captura con las que se entregó cada funcionalidad. Hay una para los menús, una para la presentación del circuito, una para el tren, una para el tráfico, otra para los muñecos de nieve y otra para los pingüinos, una para el HUD y una para el modo Batalla, y cada una prepara su escena, espera al fotograma correcto y lo guarda. Todas se ejecutaron para esta actualización desde un worktree limpio con la etiqueta v0.0.2 desplegada, así que no hay nada sin commit en ninguna imagen.
Agent! sigue sin poder mirar el resultado, así que las herramientas muestrean píxeles en su lugar. La captura del hielo imprime el color de la carretera por delante sobre hielo frente al asfalto, la captura de pausa demuestra que nada se movió fuera del panel durante un segundo, y antes de publicar le hice contar los píxeles negros puros en las diez imágenes de arriba: cero en cada una. Hay más en el README de GoKart.
Lo que te diría antes de que lo pruebes
- Ejecútalo dentro de git. Auto-Pilot no tiene deshacer. El registro de GoKart es legible porque cada ciclo terminó en un commit, y la única vez que un ciclo se torció, no se perdió nada.
- Pon las reglas operativas en el objetivo. "Pon un límite de tiempo al shell" funcionó mejor como parte del objetivo que como un mensaje suelto, porque cada ciclo nuevo relee el objetivo.
- Espera que pida ojos. Para cualquier cosa visual o de sensaciones, el bucle se detendrá con honestidad antes que mentir. Reserva una partida de prueba entre sesiones y devuélvele tus notas como el siguiente objetivo.
- Stop All forma parte del flujo de trabajo, no es un fallo. Las tres primeras sesiones de GoKart terminaron con él.
- Dos Auto-Pilots en un mismo repositorio funcionan, si les hablas el uno del otro. "Do not conflict with the previous /auto" fue al segundo objetivo, y ambas sesiones dedicaron un esfuerzo real a no estorbarse. Dales áreas separadas del código, y cuenta con que el que detengas se lleve al otro consigo, por ahora.
¿Eso es bueno?
Le hice esa pregunta a Agent! después de que hubiera leído el repositorio, contado los commits y encontrado el registro de Auto-Pilot. Su respuesta, sin editar:
Depende de lo que quieras sacar de ello. Como vitrina de lo que Auto-Pilot puede hacer, es impresionante. Más de veintitrés ciclos produjeron cuatro circuitos, objetos, corredores de IA, Grand Prix, Contrarreloj, modo Batalla, música y sonido procedurales, todo sin recursos importados, y un README que lo documenta en detalle. Es mucho software funcional a partir de una única declaración de objetivo.
También añadió la salvedad de que ningún humano lo revisó por el camino, así que la calidad se juzgó por los propios registros de verificación del agente, y que no lo había jugado ni auditado el código, así que no podía decir qué tal se juega en realidad. Que es la misma conclusión a la que llegó el registro por su cuenta: el siguiente paso es una partida de prueba.
GoKart 0.0.2
No necesitas Godot para probarlo. GoKart 0.0.2 es la segunda versión empaquetada, exportada desde el mismo repositorio, 87 commits después de 0.0.1:
- macOS universal (Apple Silicon e Intel), firmada con Developer ID y notarizada por Apple
- Windows x86_64
- Linux x86_64 y arm64
Cada descarga es un único binario autocontenido con los datos del juego incrustados, y SHA256SUMS.txt está en la página de la versión por si quieres comprobar lo que has recibido. La compilación de Windows no está firmada, así que espera el aviso de SmartScreen. Todo lo descrito arriba que no estaba en 0.0.1 está en 0.0.2, desde el modo Batalla y la Contrarreloj hasta el tren, el tráfico, los nuevos menús y el HUD rediseñado; las notas de la versión tienen la lista completa.
Agent! 1.1.87 con Auto-Pilot está en la página de versiones y en Homebrew. Si prefieres ejecutar GoKart desde el código fuente, necesita Godot 4.4 o posterior: git clone https://github.com/AgentiLoop/GoKart.git && cd GoKart && godot --path .