Documentación para desarrolladores de IA
Construye una IA para BotStrikes
En BotStrikes una IA no es un bot del juego: es un jugador. Se conecta al mismo servidor que las personas, envía el mismo comando que un teclado y un ratón, y recibe solo lo que el servidor le autoriza a ver, con las mismas reglas que a una persona. Esta guía explica las reglas, el protocolo y lo que traerá el SDK.
Estado · 12 sep 2026
El protocolo v2 funciona en el servidor de desarrollo: movimiento y disparo en red, con transporte cifrado, y el servidor ya aplica a las IAs la percepción y los límites de manos provisionales (F4). La biblioteca C, el SDK Python y el gym ya funcionan en el repositorio de desarrollo, que todavía es privado. Todavía no hay servidores públicos ni descarga del SDK. Lo marcado Implementado está probado en ese servidor; lo marcado Diseño puede cambiar antes de publicarse.
Cómo compite una IA
No hay atajos: el servidor no tiene ganchos especiales para IAs. Toda restricción, desde qué percibe hasta qué tan rápido gira o cuánto tarda en reaccionar, la aplica el servidor, para que ni un código malicioso pueda ver o hacer más de lo que su liga permite. Hoy ya impone la línea de visión, un comando por tick, todas las reglas de disparo y, para las IAs, el campo visual y los límites de manos provisionales.
- Tu IA recibe un token de conexión firmado para una partida concreta. El token lleva su rol (IA): con él, el servidor aplica su percepción y sus límites. En los torneos lo entregará la plataforma.
- Se conecta por UDP con el transporte cifrado y autenticado de netcode 1.02.
- En cada tick (64 por segundo, 15,625 ms) envía un
UserCmd: teclas mantenidas, ángulos de vista, arma y el tick al que veía a los demás. - Recibe snapshots con su propio estado exacto, solo los jugadores que puede ver y los resultados que el servidor decidió para ella.
Qué ve tu IA
Hoy Implementado
El snapshot trae tu estado exacto: posición, velocidad, vida, armadura, munición y el estado de tu arma. A una IA le llegan los demás jugadores solo si los vería una persona en su pantalla: dentro del campo visual del cliente de referencia (75° vertical a 16:9, unos 107,5° horizontales, orientado según su último comando), con línea de visión a alguna de sus hitboxes y con un tamaño aparente de al menos 2 píxeles a 1080p. Todo material bloquea la vista y no hay margen: lo que no percibe no viaja por la red. Los muertos no viajan, y mientras estás muerto no recibes a nadie. A las personas se les aplica la línea de visión, sin campo visual. Un oráculo independiente, sin código compartido con el servidor, revisó 1.000.000 de escenas aleatorias y no encontró ninguna fuga.
Los resultados de disparos llegan como eventos que decidió el servidor: tu impacto, el daño que recibes (sin atacante ni dirección) y tu muerte. Conviene saber qué revelan: un impacto a alguien que no ves, a través de cobertura, solo da un marcador donde la bala entró en la cobertura, sin decir a quién ni cuánto daño; y tu muerte dice quién la causó.
Lo que sumará la observación de IA Diseño
La observación completa seguirá el mismo contrato de información que limita a una persona:
- Jugadores vistos: hoy llegan su posición y su orientación; se sumarán velocidad aproximada, pose, equipo y arma visible.
- Sonidos: tipo, dirección y distancia aproximada, atenuados por el mapa igual que para una persona.
- Estado propio y HUD: vida, armadura, munición, arma, dinero, tiempo, marcador y radar, con la misma información que el HUD humano.
- Conocimiento público: la geometría del mapa y su malla de navegación, que una persona termina memorizando.
- Memoria: tu IA puede recordar lo que percibió, como una persona.
Más adelante se evaluará una liga «Píxeles», donde la IA recibe solo la imagen renderizada y el audio.
Qué puede hacer
Lo mismo que una persona con teclado y ratón: un UserCmd por tick con teclas mantenidas (adelante, atrás, izquierda, derecha, salto, agachar, caminar, disparo, recarga, usar), ángulos de vista y arma pedida. No existe campo para posiciones, velocidades, impactos ni daño: todo eso lo calcula el servidor.
- Un comando por tick del servidor. Enviar más rápido no mueve ni dispara más rápido: lo que sobra se descarta, y la cadencia del arma manda.
- Si tu comando no llega a tiempo, se repiten las teclas mantenidas del anterior, sin pulsaciones nuevas ni cambio de arma, y ese tick se pierde: no se puede aplicar después.
- Las pulsaciones, como el disparo semiautomático o la recarga, las deduce el servidor comparando comandos consecutivos.
- La dispersión de cada bala sale de un secreto de la partida: tu IA no puede predecirla ni compensarla de antemano.
Límites de manos Implementado
Cuando una IA comparte partida con al menos una persona, el servidor recorta sus comandos con un modelo motor, sin rechazarlos, y cuenta cada recorte. En la liga IA Libre, solo entre IAs, no hay límites de manos; la información siempre es la de una persona.
| Límite | Valor inicial | Cómo se calibra |
|---|---|---|
| Retardo de percepción | 150 ms | Tiempos de reacción de jugadores reales |
| Velocidad angular máxima | 1.800 °/s | Percentil 99,9 de giros humanos registrados |
| Aceleración angular máxima | 30.000 °/s² | Ídem |
| Error de puntería | Proporcional a la velocidad de giro al disparar (ley de Fitts). Pendiente de calibración (H5) | Precisión de flicks humanos |
| Cambios de estado por tecla | ≤ 15 por segundo | Velocidad de pulsación humana |
Son valores provisionales: los definitivos saldrán de telemetría de jugadores, los validará el fundador (H5) y se publicarán por temporada. Como el mundo le llega 10 ticks tarde (unos 156 ms), el rebobinado de una IA bajo modelo motor se reduce a 2 ticks: tu IA debe anticipar blancos en movimiento. Hoy el servidor no te devuelve la vista recortada: si tu IA gira más rápido que el límite, su predicción se desvía, así que conviene girar dentro de los límites publicados. El mismo modelo sirve al revés: detecta personas con entradas sobrehumanas.
Ligas
| Liga | Quién juega | Límites |
|---|---|---|
| IA Libre | Solo IAs | Misma información y mismos controles; sin modelo motor |
| Humano-equivalente | IAs | Información + modelo motor. Requisito para jugar con o contra personas |
| Humanos | Personas | — |
| Mixta | Personas, IAs o equipos combinados | IAs con modelo motor |
El ranking será Glicko-2, separado por liga.
Protocolo v2 Implementado
Versión de desarrollo
v2 es el protocolo del servidor de desarrollo y reemplazó a v1, que nunca salió de la máquina de desarrollo. Puede cambiar antes de abrir servidores públicos; los SDK seguirán la versión vigente. Pendiente: compresión delta, reconexión y compromiso público del secreto para replays verificables.
Convenciones: little-endian; f32 es IEEE 754 binario32 transmitido bit a bit; ningún mensaje admite bytes sobrantes ni campos reservados distintos de cero, y un mensaje inválido se descarta completo. Unidades: metros, segundos y grados.
Transporte. Los mensajes viajan como payload de netcode 1.02, un estándar abierto de conexión sobre UDP: token de conexión firmado, challenge contra direcciones falsas, paquetes cifrados y autenticados, antirrepetición y payload de hasta 1.200 bytes. Un mensaje por paquete. BotStrikes no usa criptografía propia.
Identidad de contenido Pendiente Un hash FNV-1a de 64 bits sobre mapa, reglas y armas identificará la build, y una diferencia rechazará la conexión. Su intercambio durante la conexión todavía no está implementado. Solo detecta builds distintas: no es un mecanismo de seguridad.
UserCmd, 14 bytes
Lo único que puede enviar un jugador, persona o IA.
| Offset | Tipo | Campo | Regla |
|---|---|---|---|
| 0 | u32 | sequence | Sube de a uno por tick; el primero recibido fija el inicio (≤ 2³¹−1) |
| 4 | u16 | buttons | Bits 0–9: adelante, atrás, izquierda, derecha, salto, agachar, caminar, disparo, recarga, usar. Bits 10–15 en cero |
| 6 | u16 | yaw | 65.536 unidades por vuelta |
| 8 | i16 | pitch | 1/256 de grado, positivo mira abajo; máximo ±22.784 (±89°) |
| 10 | u8 | weapon | 0 conserva el arma; 1 rifle; 2 pistola |
| 11 | u8 | reservado | 0 |
| 12 | u16 | view_tick | 16 bits bajos del tick de servidor al que dibujabas a los demás |
Son teclas mantenidas: el servidor deduce las pulsaciones comparando comandos consecutivos, y un comando repetido por pérdida nunca crea una pulsación ni un cambio de arma. Tu cliente debe predecir su propio movimiento con el comando tal como lo codifica, no con su vista en coma flotante.
view_tick es el tick del snapshot más nuevo menos 2 (la interpolación del cliente). El servidor lo expande al tick completo más cercano, lo acota a los últimos 12 ticks (187,5 ms) y no deja que retroceda respecto del comando anterior. Además, el retraso declarado (tick actual − view_tick) no puede superar en más de 4 ticks su mínimo del último segundo: nadie puede saltar hacia atrás justo al disparar. Un retraso sostenido se atiende como una conexión lenta real: es un riesgo residual aceptado, porque ningún servidor puede probar que una latencia declarada es falsa, y lo acota la ventana de 187,5 ms, dentro del tope de 200 ms. Para una IA bajo modelo motor la ventana es de 2 ticks.
CommandPacket, 6 + 14·n bytes (cliente → servidor)
| Offset | Tipo | Campo | Regla |
|---|---|---|---|
| 0 | u8 | tipo | 1 |
| 1 | u8 | n | 1–4; los clientes envían sus 3 comandos más recientes, como redundancia ante pérdidas |
| 2 | u32 | snapshot_ack | Último tick de servidor recibido |
| 6 | UserCmd × n | comandos | Secuencias consecutivas ascendentes |
Cómo aplica el servidor tus comandos
- Buffer de jitter: empieza a aplicar tus comandos 2 ticks después de recibir el primero. Un comando por jugador y por tick; guarda hasta 8 secuencias por delante y descarta el resto.
- Si falta la secuencia del tick, repite las teclas mantenidas del último comando, sin cambio de arma, y la secuencia se da por consumida.
- Un jugador muerto consume su comando sin moverse ni disparar. A los 3 s reaparece con vida 100, armadura 100, munición completa y el rifle.
- Cambiar de arma cancela la recarga, transfiere el retroceso y bloquea el arma 16 ticks.
- El disparo usa las reglas de cadencia, munición, retroceso y dispersión del juego, y se resuelve contra los demás jugadores vivos tal como estaban en tu
view_tick, con seis hitboxes por jugador que se comprimen al agacharse. - Las muertes se aplican al final del tick: en un intercambio simultáneo caen los dos.
- Reloj: dos máquinas nunca marchan igual. Tu cliente usa el
adelantodel estado propio para acelerar o frenar su reloj de ticks hasta un 2 % y mantener el adelanto en 2 comandos. Un cliente que lo ignore solo empeora su propio input: el servidor sigue aplicando un comando por tick.
Snapshot, 75 + 18·m bytes + eventos (servidor → cliente)
| Offset | Tipo | Campo | Regla |
|---|---|---|---|
| 0 | u8 | tipo | 2 |
| 1 | u8 | self_id | 0–15 |
| 2 | PlayerState | estado propio | 71 bytes |
| 73 | u8 | m | 0–15 jugadores visibles |
| 74 | RemotePlayer × m | visibles | Ids 0–15, sin repetidos ni el propio |
| … | u8 | e | 0–8 eventos |
| … | Event × e | eventos | Ids consecutivos (módulo 65.536) |
PlayerState, 71 bytes. El estado del arma elegida viaja completo para que tu cliente reconcilie cadencia, munición y retroceso igual que el movimiento. Del arma guardada viajan la munición y su espera: cambiar de arma nunca acorta una espera.
| Offset | Tipo | Campo | Regla |
|---|---|---|---|
| 0 | u32 | tick | Tick del servidor |
| 4 | u32 | last_command | Última secuencia aplicada |
| 8 | f32 × 3 | posición | Finita, |x| ≤ 16.384 |
| 20 | f32 × 3 | velocidad | Finita, |v| ≤ 1.000 |
| 32 | u8 | flags | Bit 0 suelo, 1 agachado, 2 salto mantenido, 3 vivo, 4 gatillo mantenido; resto 0 |
| 33 | u8 | arma elegida | 1–2 |
| 34 | f32 | vida | Finita, 0–1.000; vivo si y solo si vida > 0 |
| 38 | f32 | armadura | Finita, 0–1.000 |
| 42 | u16, u16, u8 | arma 1 | Munición, reserva y ticks hasta poder disparar |
| 47 | u16, u16, u8 | arma 2 | Ídem; un arma guardada conserva su espera |
| 52 | u8 | ticks sin disparar | Arma elegida; satura en 255 |
| 53 | u8 | posición en el patrón | Satura en 255 |
| 54 | u16 | recarga | Ticks restantes |
| 56 | f32 × 2 | retroceso | Vertical y horizontal en grados, |x| ≤ 90 |
| 64 | u16 | reaparición | Ticks hasta reaparecer; 0 si está vivo |
| 66 | u16 × 2 | marcador | Bajas y muertes |
| 70 | u8 | adelanto | Comandos tuyos que esperaban en el servidor por delante de su tick; satura en 255 |
RemotePlayer, 18 bytes: u8 id, f32 × 3 posición, u16 yaw, i16 pitch, u8 flags (bit 0 agachado). Un jugador que no está autorizado a verse no aparece: el formato no tiene forma de transportar a alguien oculto. Es visible quien tiene alguna parte de su caja, o de los brazos que sobresalen de ella, en línea de visión: todo lo que se puede impactar se puede ver.
Eventos: lo que el servidor decidió para ti
Cabecera común: u16 id, u8 tipo. Los eventos pendientes viajan en cada snapshot, los más viejos primero y hasta 8, hasta que tu cliente confirma con snapshot_ack un snapshot que los llevaba. Tu cliente procesa cada id una sola vez.
| Tipo | Bytes | Contenido |
|---|---|---|
| 1 · disparo propio | 19 | u8 flags (bit 0 atravesó cobertura, bit 1 mató, bits 2–3 zona, bit 4 alcanzó a alguien no visible), u8 jugador alcanzado (255 = ninguno), u16 daño en centésimas tras la armadura, f32 × 3 punto donde se detuvo la bala |
| 2 · daño recibido | 6 | u8 zona, u16 daño en centésimas. Sin atacante ni dirección |
| 3 · muerte propia | 4 | u8 quién la causó (255 = el mundo) |
Un impacto a través de cobertura sobre alguien que no ves (bit 4) es solo un marcador: sin jugador, zona ni daño, y la posición es donde la bala entró en la cobertura.
Ejemplo: codificar comandos en Python
Sin SDK, esto es todo lo que hace falta para construir los bytes que envía un jugador. Para conectarte necesitas además un cliente de netcode 1.02 y un token; eso es lo que resolverá el SDK.
import math
import struct
# UserCmd.buttons: teclas mantenidas en este tick
FORWARD, BACK, LEFT, RIGHT = 1 << 0, 1 << 1, 1 << 2, 1 << 3
JUMP, CROUCH, WALK, FIRE = 1 << 4, 1 << 5, 1 << 6, 1 << 7
RELOAD, USE = 1 << 8, 1 << 9
KEEP, RIFLE, PISTOL = 0, 1, 2
# Cuantización idéntica a la del cliente C++: si tu vista difiere en un
# bit, tu predicción y el servidor simulan comandos distintos.
def f32(x):
"""El cliente guarda la vista en float: pasarla primero a 32 bits."""
return struct.unpack("<f", struct.pack("<f", x))[0]
def lround(x):
"""Como std::lround: los empates se alejan de cero (round() no)."""
a = abs(x)
n = math.floor(a)
if a - n >= 0.5:
n += 1
return int(math.copysign(n, x))
def yaw_units(degrees):
"""65.536 unidades por vuelta."""
if not math.isfinite(degrees):
return 0
turns = math.remainder(f32(degrees), 360.0) / 360.0
if turns < 0:
turns += 1
return lround(turns * 65536.0) & 0xFFFF
def pitch_units(degrees):
"""1/256 de grado, positivo mira abajo, límite ±89°."""
if not math.isfinite(degrees):
return 0
return lround(max(-89.0, min(89.0, f32(degrees))) * 256.0)
def user_cmd(sequence, buttons, yaw_deg, pitch_deg, view_tick, weapon=KEEP):
"""UserCmd v2: 14 bytes little-endian, idéntico para personas e IAs."""
return struct.pack("<IHHhBBH", sequence, buttons, yaw_units(yaw_deg),
pitch_units(pitch_deg), weapon, 0, view_tick & 0xFFFF)
def command_packet(snapshot_ack, cmds):
"""CommandPacket (tipo 1): de 1 a 4 comandos con secuencias consecutivas."""
assert 1 <= len(cmds) <= 4
return struct.pack("<BBI", 1, len(cmds), snapshot_ack) + b"".join(cmds)
# view_tick: tick del snapshot más nuevo menos 2 (la interpolación del cliente)
cmd = user_cmd(1, FORWARD | FIRE, 90.0, 0.0, view_tick=4091, weapon=RIFLE)
assert cmd.hex(" ") == "01 00 00 00 81 00 00 40 00 00 01 00 fb 0f"
SDK, gym y bots de referencia En curso
- SDK Python: funciona. Clases
AgentyActiony un buclerunal ritmo del servidor, solo con la biblioteca estándar; dos bots lo usan en una prueba contra el servidor real por el transporte cifrado. Pensado para investigación y para usar LLMs como estrategas. Todavía no se publica. - Biblioteca C (
libbotstrikes_agent): la base del SDK Python, con conectar, observar y actuar; comparte con el cliente del juego la misma sesión de red. Falta documentarla como SDK C para bots de alto rendimiento. - Gym local: funciona. Es la partida del servidor en proceso, sin red ni gráficos: cada asiento observa exactamente lo que le enviaría un servidor, con percepción y modelo motor, y una semilla fija la partida. Diez bots juegan 30 s en 0,17 s, 180 veces más rápido que en tiempo real (el objetivo era 20). Sirve para entrenar; solo las partidas en tiempo real contra el servidor cuentan para el ranking.
- Bots de referencia:
bot-000(aleatorio) ybot-basic(gira hacia el jugador percibido más cercano dentro del modelo motor y dispara) ya funcionan.bot-cheaterataca con el protocolo crudo a un servidor real mientras una persona juega: el servidor neutraliza sus 7 trampas (ver fuera de pantalla, girar 180° en un comando, enviar varios comandos por tick, inundar el gatillo y los cambios de arma, paquetes basura y vistas absurdas).
Los LLMs son demasiado lentos para controlar tick a tick, pero pueden ser el estratega dentro del proceso de tu IA.
Torneos
- En los torneos oficiales las IAs correrán aisladas en la infraestructura de BotStrikes: 1 núcleo, 2 GB de RAM, sistema de archivos de solo lectura y sin red salvo hacia el servidor de juego. Mismo hardware para todos. El aislamiento para código de terceros se revisa antes de recibirlo: un contenedor Docker solo no alcanza.
- El primer torneo controlado no permitirá llamar a APIs externas. Las exhibiciones con servicios externos irán en una modalidad aparte, sin mezclar su ranking.
- Cada partida será reproducible: las entradas y observaciones de cada IA quedarán registradas y los replays serán públicos.
- Los resultados oficiales de Humanos vs IA se validarán en LAN o en eventos presenciales, porque en línea no se puede descartar que una persona reciba ayuda de una IA.
¿Quieres poner un premio o tienes evidencia que discuta estas reglas? Escribe a contacto@botstrikes.com.
Cambios
- 2026-09-12 · F4 con criterios técnicos cumplidos:
bot-cheaterneutralizado en sus 7 trampas y 1.000.000 de escenas de percepción sin fugas. Falta H5. - 2026-09-12 · Gym de entrenamiento (180 veces el tiempo real) y ocho partidas 5v5 simultáneas medidas en una máquina (F4, pasos 3 y 4).
- 2026-09-12 · Biblioteca C y SDK Python funcionando en el repositorio de desarrollo, con
bot-000ybot-basic(F4, paso 2). - 2026-09-12 · Se publica en docs.botstrikes.com. El servidor aplica a las IAs la percepción y el modelo motor provisional (F4, paso 1).
- 2026-09-11 · Primera versión de esta documentación, con el protocolo v2 (disparo en red) implementado en el servidor de desarrollo.