No todos los bytes merecen voto: diseño de checksums para replay determinista

Fuentes: Not Every Byte Gets a Vote
Imagen generada por IA con el prompt: abstract diagram of a deterministic game tick loop with arrows showing data flowing between ordered phases, retro pixel-art aesthetic, dark background with cyan and amber accents
Imagen generada con IA

En el desarrollo de motores de juego deterministas, diseñar un sistema de replay que verifique fielmente el estado del juego sin romperse ante cambios menores de implementación es un reto técnico habitual. Este artículo, escrito desde la experiencia práctica con un motor ARPG en Zig, analiza cómo construir una superficie de checksum que distinga el estado que afecta al gameplay del que solo sirve para inspección o presentación.

El autor parte de la tentación inicial de aplicar un hash a toda la memoria del mundo: entidades, proyectiles, estado del RNG, trazas de IA y helpers de renderizado. Pronto descubre que esta estrategia trata todos los campos como igualmente significativos: detecta divergencias reales, pero convierte refactors inocuos en falsos fallos de replay. Un caso concreto lo ilustra: modificar un campo de depuración cambiaba el checksum aunque el resultado para el jugador fuera idéntico. La simulación fallaba por una reorganización de datos de depuración.

La solución propuesta es clasificar explícitamente cada campo en uno de cuatro roles: estado autoritativo de gameplay (vida del jugador, posición de proyectiles, flujo del RNG), cachés derivadas (como el flowfield de pathfinding), observaciones y eventos de depuración, y estado de presentación (interpolación del renderer). La pregunta guía es si el campo puede alterar un tick futuro: si la respuesta es sí, entra en la superficie del checksum; si solo ayuda a inspeccionar o dibujar, queda fuera.

El texto describe también la arquitectura del tick fijo del motor, con fases ordenadas (idle, ingress, control, derive, plan, apply, cleanup) que dan al replay un punto estable de medición. El archivo de replay almacena semilla, cinta de inputs y conteo de ticks, y un pequeño harness compara el checksum final tras una segunda ejecución.

Otro punto relevante es la distinción entre los contratos de replay y de snapshot: replay verifica si, partiendo de la misma semilla e inputs, se llega al mismo estado autoritativo; snapshot comprueba si el mundo puede congelarse, serializarse y restaurarse íntegro. Una caché puede quedar fuera del checksum si se reconstruye deterministamente antes de su uso, y aun así serializarse en el snapshot si resulta más eficiente o útil para depuración.

El autor recuerda además que el checksum no convierte una simulación en determinista por sí mismo: siguen siendo necesarios el tamaño de tick fijo, el RNG explícito, el orden de iteración estable, el estado inicializado y la ausencia de dependencias del renderizado. El checksum es únicamente el punto de comparación entre dos ejecuciones.