Robocode TankRoyale en Python¶
Antes de empezar¶
Leed primero la Comparativa Java vs Python para entender las diferencias y elegir vuestro camino.
Esta guía es la versión Python de la actividad Robocode. Si buscáis la versión Java, id a P01 Robocode (Java).
Preparación del entorno¶
Requisitos previos¶
- Python 3.10 o superior instalado en el sistema
- VS Code (Visual Studio Code) u otro editor de código
- Conexión a Internet para instalar dependencias
Comprobación de Python¶
Abrid un terminal y ejecutad:
1 | |

Windows
Si usas Windows, el comando es python --version en lugar de python3 --version.
Si no tenéis Python instalado, descargadlo desde https://www.python.org/downloads/ o usad el gestor de paquetes de vuestro sistema:
1 | |
1 | |
1 | |
Descargad el instalador desde https://www.python.org/downloads/ y marcad la opción "Add Python to PATH"
VS Code y extensiones¶
Si aún no tenéis VS Code, descargadlo desde https://code.visualstudio.com/
Una vez instalado, añadid las siguientes extensiones:
- Python (de Microsoft) — todo el soporte para Python: linting, debugging, IntelliSense
- Pylance (de Microsoft) — completado de código y tipado avanzado
- Opcional: Python Environments (de Microsoft)

Instalación de la API de Robocode¶
La API de Python para Robocode Tank Royale se distribuye como paquete pip:
1 | |
O si trabajáis con entornos virtuales:
1 2 3 4 5 6 7 8 9 | |

La GUI de Robocode Tank Royale¶
Descarga y ejecución de la GUI¶
La GUI (interfaz gráfica) de Robocode es independiente del lenguaje. El proceso es idéntico tanto si usáis Java como Python:
- Id a https://github.com/robocode-dev/tank-royale/releases
- Descargad la última versión del fichero
robocode-tankroyale-gui-x.y.z.jar - Ejecutadlo:
1 | |
Java necesario para la GUI
La GUI requiere Java 11 o superior para ejecutarse, independientemente de que vuestro bot esté escrito en Python. Si no tenéis Java, ved el Taller T01: Preparar entorno para Java o instalad OpenJDK:
1 | |
Configuración inicial de la GUI¶
Una vez abierta la GUI:
- Descargad y descomprimid los Bots de ejemplo desde la misma página de releases
- Id a
Config→Bot Root Directoriesy añadid la carpeta donde habéis descomprimido los bots de ejemplo - Opcional: descargad y descomprimid
sounds.zipde los releases de sonidos en el mismo directorio que el JAR
1 2 3 4 5 6 7 | |

Consejo
Jugad un poco con el entorno GUI, explorad las opciones, tipos de batallas, cread alguna ronda, inicializad bots, añadidlos, ajustad la velocidad de juego, etc.
Vuestro primer Bot en Python¶
Estructura del proyecto¶
Cread una carpeta para vuestro bot (por ejemplo, MiBot) con la siguiente estructura:
1 2 3 4 5 | |
Fichero de configuración JSON¶
El bot necesita un fichero .json con la información del bot. Cread MiBot.json:
1 2 3 4 5 6 7 8 9 10 | |
Código básico del Bot¶
Cread MiBot.py con el siguiente contenido:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
VS Code: Pylint/Pylance no encuentran el paquete
Si después de seleccionar el intérprete del venv (Ctrl+Shift+P → "Python: Select Interpreter" → ./.venv/bin/python) los errores persisten, es porque Pylint usa su propio intérprete y Pylance necesita el fichero .vscode/settings.json.
Cread la carpeta .vscode/ dentro de la carpeta de vuestro bot (/home/ubuntu/robocode/MiBot/) y añadid el siguiente fichero:
| .vscode/settings.json | |
|---|---|
1 2 3 4 5 6 | |
Si no tenéis Pylint instalado en el venv (solo lo tiene el sistema), VS Code puede ejecutar el Pylint del sistema que no ve los paquetes instalados en el venv. Opciones: (1) desactivar Pylint ("python.linting.enabled": false) y confiar solo en Pylance, o (2) instalar Pylint en el venv: pip install pylint.
Los subrayados rojos desaparecerán al recargar la ventana (Ctrl+Shift+P → "Developer: Reload Window").
Scripts de inicio¶
Windows (MiBot.cmd)¶
1 2 | |
Linux/macOS (MiBot.sh)¶
1 2 | |
Hacedlo ejecutable:
1 | |

Prueba del bot¶
- Iniciad la GUI de Robocode (
java -jar robocode-tankroyale-gui-x.y.z.jar) - Abrid un terminal en el directorio de vuestro bot
- Ejecutad:
1 | |
Si todo va bien, veréis un mensaje similar a:
1 | |
Y vuestro bot aparecerá en la lista de bots disponibles en la GUI.

Configuración del servidor y secrets¶
Secrets del servidor¶
El servidor de Robocode puede requerir una contraseña (secret) para conectarse. Esta contraseña se genera automáticamente la primera vez que lanzáis la GUI.
Para habilitar los secrets:
- Id a
Config→Server Options→Enable server secretsen la GUI - O editad
server.propertiesy añadid:
1 2 | |
Conectar el bot con secret¶
Estableced la variable de entorno SERVER_SECRET antes de ejecutar el bot:
1 2 | |
1 2 | |
1 2 | |
Conectarse a un servidor remoto¶
Si el profesor indica una IP remota, podéis especificarla con SERVER_URL:
1 2 3 | |
1 2 3 | |
Uso con VS Code (launch.json)¶
Para mayor comodidad, podéis configurar VS Code para ejecutar el bot con las variables de entorno definidas en el fichero .vscode/launch.json:
Cread la carpeta .vscode/ dentro del proyecto y el fichero launch.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |

Ahora podéis ejecutar el bot directamente desde VS Code pulsando F5 o yendo a Run → Start Debugging.
El servidor debe estar en marcha
El servidor (la GUI) debe estar inicializado antes de lanzar el bot. Si no, obtendréis un error:
1 | |
API de Python: diferencias clave con Java¶
La API de Python utiliza snake_case en lugar de camelCase. Aquí tenéis una tabla de correspondencia:
| Java | Python |
|---|---|
setTurnLeft(degrees) | set_turn_left(degrees) |
setForward(dist) | set_forward(dist) |
setTurnRadarLeft(degrees) | set_turn_radar_left(degrees) |
setAdjustRadarForBodyTurn(bool) | set_adjust_radar_for_body_turn(bool) |
distanceTo(x, y) | distance_to(x, y) |
bearingTo(x, y) | bearing_to(x, y) |
gunBearingTo(x, y) | gun_bearing_to(x, y) |
getX() | self.x (propiedad) |
getY() | self.y (propiedad) |
getSpeed() | self.speed (propiedad) |
getEnergy() | self.energy (propiedad) |
getTurnRemaining() | self.turn_remaining (propiedad) |
getGunHeat() | self.gun_heat (propiedad) |
isRunning() | self.is_running (propiedad) |
onScannedBot(e) | on_scanned_bot(self, e) |
e.getX() | e.x (propiedad) |
e.getY() | e.y (propiedad) |
e.getSpeed() | e.speed (propiedad) |
e.getDirection() | e.direction (propiedad) |
Double.POSITIVE_INFINITY | float("inf") |
Math.min(a, b) | min(a, b) |
Math.cos(angle) | math.cos(angle) |
Math.toRadians(degrees) | math.radians(degrees) |
¿Cómo mejoro mi Bot?¶
Dividiremos todo lo que debemos conocer en 4 apartados:
Conociendo el campo de batalla¶
Sigue atentamente la documentación de RCTR sobre la anatomía de los Bots
Puntos importantes:
- Si el radar no se mueve, no detecta
- Inicialmente el robot, el cañón y el radar se mueven conjuntamente (pero se puede cambiar)
- El Bot se simplifica a un cuadrado de 36x36 unidades.
- Para las colisiones (con Bots o paredes) se simula como un círculo de 18 unidades de radio.
A continuación revisa las coordenadas y ángulos
Puntos importantes:
- Este apartado es totalmente diferente al de Robocode Original.
Estudia también las físicas
Puntos importantes:
- Se frena más rápido que se acelera.
- Cuanto más rápido vas, más lento giras.
- El cañón gira máximo 20º por turno.
- El radar gira máximo 45º por turno.
- La potencia de tiro influye en el daño provocado y los puntos conseguidos, pero también en el calentamiento del cañón.
- Inicialmente el cañón está caliente (3 unidades), al principio has de esperar a que se enfríe para disparar.
- El atropello da más puntos que los disparos (pero te resta energía)
- Si chocas con una pared, pierdes puntos.
- La energía que te sobra en una ronda no da puntos (modo kamikaze con el último robot?)
Por último ten en cuenta las puntuaciones
Puntos importantes:
- Consigues puntos si:
- tu disparo golpea a otro Bot (sino, te resta)
- eres el que mata al Bot
- cada vez que otro Bot muere, pero tu sigues vivo
- eres el último robot vivo
- atropellas a otro Bot
- si matas por atropello a otro Bot
- El último Bot que quede vivo no tiene porqué ser el ganador.
Eventos propios¶
Definimos una condición, y cuando esta se cumple se dispara un evento:
1 2 3 4 5 6 7 8 9 | |
Podríamos usar esta condición en un fragmento similar a este:
1 | |
Podríamos usar funciones lambda de la siguiente manera:
1 | |
El resultado de los dos fragmentos sería el mismo.
Personalizar mi Bot¶
Podemos establecer colores para el cuerpo, torreta de cañón, el radar, el arco de escaneo y de la bala:
1 2 3 4 5 6 | |
Puedes encontrar la lista completa de colores disponibles en la API de Robocode TankRoyale.
Técnicas de escaneo (Radar)¶
Sentidos de nuestro Bot:
Sentido del tacto, tu Bot sabe cuando:
- golpea una pared (
on_hit_wall), - es alcanzado por un disparo (
on_hit_by_bullet), - es alcanzado por otro Bot (
on_hit_bot).
Sentido de la vista, tu Bot sabe cuándo ha visto otro robot, pero sólo si lo escanea (on_scanned_bot)
Otros sentidos, tu robot también sabe cuándo ha muerto (on_death), cuándo ha muerto otro robot (on_bot_death).
Además también es consciente de sus balas y sabe cuando una bala ha sido disparada (on_bullet_fired) ha alcanzado a un oponente (on_bullet_hit), cuando una bala golpea una pared (on_bullet_wall_hit) o cuando una bala golpea a otra bala (on_bullet_hit_bullet).
Configurar correctamente tu Bot con:
1 2 3 4 5 6 | |
Que el radar siempre de vueltas:
1 | |
Revisa el API (rescan() y set_rescan())
Fijar el radar en un enemigo (one on one radar):
- Escaneo con multiplicador
1 2 3 | |
Según el factor:
1.0- Bloqueo de radar delgado. Debe llamar a scan() para evitar perder el bloqueo. Que Dios te ayude si alguna vez te saltas un turno.1.9- El arco del radar comienza amplio y se estrecha lentamente tanto como sea posible mientras se mantiene en el objetivo.-
2.0: el arco del radar recorre un ángulo fijo. El ángulo exacto elegido depende de las posiciones del enemigo y del radar cuando se detecta al enemigo por primera vez. El ángulo se incrementará si es necesario para mantener el bloqueo. -
Arco de radar Ancho
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
-
Radar Oscilante (variable global que sepa la dirección y nos ayude a cambiarla de vez en cuando)
-
Escaneo más inteligente (seguir al cercano? según la situación?)
-
Nos interesa mantener una lista de enemigos?
e.scanned_bot_id? y por ejemplo fijar el radar en el más débil?
Técnicas de desplazamiento (Movimiento)¶
Pensamiento lateral¶
Si has jugado baloncesto antes, sabes que si quieres defender a alguien que sostiene el balón, debes maximizar tu movimiento lateral enfrentándote siempre a él (de frente). Lo mismo ocurre con tu robot.
Para conseguir esta posición debemos hacer algo similar a esto:
1 | |
que siempre colocará tu robot perpendicular (90 grados) a tu enemigo.
¿Hacia adelante o hacia atrás?¶
Cuando te enfrentas a un oponente, la idea de "adelante" y "atrás" (del primer Bot) se vuelven algo obsoletas. Probablemente estés pensando más en términos de "atacar a la izquierda" o "atacar a la derecha". Para realizar un seguimiento de la dirección del movimiento, simplemente declara una variable como hicimos para oscilar el radar.
1 2 3 4 | |
luego, cuando quieras mover tu robot, simplemente puedes decir:
1 | |
Puedes cambiar de dirección cambiando el valor de move_direction de 1 a -1 así:
1 | |
Cambiar de dirección¶
El enfoque más intuitivo para cambiar de dirección es simplemente cambiar la dirección del movimiento cada vez que golpeas una pared o golpeas a otro robot de esta manera:
1 2 3 4 5 | |
Sin embargo, descubrirás que si haces eso, terminarás presionando obstinadamente contra un robot que te embiste desde un costado (como un perro en celo). Esto se debe a que se llama a on_hit_bottantas veces que move_direction sigue cambiando y nunca te alejas.
Un mejor enfoque es simplemente probar para ver si tu robot se ha detenido. Si es así, probablemente significa que has golpeado algo y querrás cambiar de dirección. Puedes hacerlo con el código:
1 2 | |
Ponlo en tu método que maneje el movimiento y podrás manejar todos los eventos de impacto con las paredes u otros Bots.
¿Bailamos?¶
Dando vueltas (Círculos)
Puedes rodear a tu enemigo simplemente usando las técnicas anteriores:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Objetivo: rodea a tu enemigo usando el código de movimiento anterior, como un tiburón rodeando a su presa en el agua.
Evita Ametrallamiento
Un problema que puedes notar con el anterior tipo de movimiento es que es presa fácil para los objetivos predictivos porque sus movimientos son tan... predecibles.
Para evadir las balas de manera más efectiva, debes moverte de lado a lado o "atacar". Una buena forma de hacerlo es cambiar de dirección después de un cierto número de "tics", así:
1 2 3 4 5 6 7 | |
Mira como se balancea hacia adelante y hacia atrás usando el código de movimiento anterior. Observa lo bien que esquiva las balas. Pero ojo, porque puede afectar a tu precisión de disparo.
Cada vez más cerca
Notarás que tanto el primero como este último tipo de movimientos tienen otro problema: se atascan fácilmente en las esquinas y terminan golpeándose contra las paredes. Un problema adicional es que si tu enemigo está lejos, disparan mucho pero no aciertan mucho.
Para hacer que tu robot se acerque a tu enemigo, simplemente modifica el código para que se gire ligeramente hacia su enemigo, así:
1 | |
15 es un factor en grados que puedes modificar y ajustar. ¡Prueba!
Modificando el primero conseguimos una variación que utiliza el código anterior para lanzarse en espiral hacia su enemigo.
Mientras que con el segundo que utiliza el código anterior para acercarse cada vez más. También es bastante bueno para evadir balas.
Fíjate que ninguno de los Bots anteriores queda atrapado en una esquina por mucho tiempo.
Evitando las paredes¶
Un problema con todos los Bots anteriores es que golpean mucho las paredes y golpearlas agota tu energía. Una mejor estrategia sería detenerse antes de chocar contra las paredes. ¿Pero cómo?
Agregar un evento personalizado
Lo primero que debemos hacer es decidir qué tan cerca permitiremos que nuestro robot llegue a las paredes:
1 2 3 4 5 6 | |
A continuación, agregamos un evento personalizado que se activará cuando se cumpla una determinada condición dentro del método run():
1 2 3 4 5 6 7 8 9 10 | |
Necesitamos hacer override del método test() para devolver un valor booleano cuando ocurra nuestro evento personalizado.
Gestionar el evento personalizado
Lo siguiente que debemos hacer es manejar el evento, que se puede hacer así:
1 2 3 4 | |
Sin embargo, el problema con ese enfoque es que este evento podría dispararse una y otra vez, provocando que cambiemos rápidamente de un lado a otro, sin alejarnos nunca. O si ya aparecemos cerca de la pared quedamos atrapados.
Para evitar este "sacudida de la muerte" deberíamos tener una variable que indique que estamos manejando el evento:
1 2 3 4 5 | |
Manejo de los dos modos
Hay dos últimos problemas que debemos resolver. En primer lugar, tenemos un método do_move() donde colocamos todo nuestro código de movimiento normal. Si estamos tratando de alejarnos de una pared, no queremos que se llame a nuestro código de movimiento normal, creando (una vez más) la "sacudida de la muerte". En segundo lugar, queremos volver eventualmente al movimiento "normal", por lo que eventualmente deberíamos tener el "tiempo de espera" de la variable too_close_to_wall.
Podemos resolver ambos problemas con la siguiente implementación de do_move():
1 2 3 4 5 6 7 | |
Con todo el código anterior evitamos chocar contra las paredes. Observa cómo se desliza suavemente hacia los lados pero nunca (bueno, rara vez) los golpea.
Bot multimodo¶
Además de los colores que elijas, la mayor parte de la personalidad de tu robot está en su código de movimiento. Por otra parte, situaciones diferentes requieren tácticas diferentes. Usando el ejemplo de evitar paredes, es posible que desees codificar tu bot para que cambie los "modos" según ciertos criterios. Podrías pensar en algo como esto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
Los detalles se dejan (como siempre) como ejercicio para el alumnado.
Técnicas de ataque (Disparo)¶
Técnicas básicas¶
- Separa el movimiento del cañón del movimiento del Bot (
set_adjust_gun_for_body_turn) - Técnica de apuntado básica:
set_turn_gun_leftoset_turn_gun_rightjunto congun_bearing_to
Fórmula de cálculo de potencia de fuego¶
Otro aspecto importante al disparar es calcular la potencia de fuego de tu bala. La documentación del método fire() explica que puedes disparar una bala en el rango de 0.1 a 3.0. Como probablemente ya habrás concluido, es una buena idea disparar balas de baja potencia cuando tu enemigo está lejos y balas de alta resistencia cuando está cerca.
1 2 3 4 5 6 7 | |
Podrías usar una serie de declaraciones if-elif-else para determinar la potencia de fuego, en función de si el enemigo está a 50 unidades, a 200, etc (como en el fragmento anterior). Pero tales construcciones son demasiado rígidas. Después de todo, el rango de posibles valores de potencia de fuego cae a lo largo de un continuo, no de bloques discretos. Un mejor enfoque es utilizar una fórmula. He aquí un ejemplo:
1 | |
Con esta fórmula, a medida que aumenta la distancia del enemigo, la potencia de fuego disminuye. Asimismo, a medida que el enemigo se acerca, la potencia de fuego aumenta. Los valores superiores a 3 se reducen a 3, por lo que nunca dispararemos una bala mayor que 3, pero probablemente deberíamos reducir el valor de todos modos (solo para estar seguros) de esta manera:
1 | |
Evitar disparos prematuros¶
Una situación que encontraras es que tu Bot dispare antes de haber girado el arma hacia el objetivo. Para evitar disparos prematuros, usa la propiedad gun_turn_remaining para ver qué tan lejos está tu cañón del objetivo y no dispare hasta que esté cerca.
Además, no puedes disparar si el arma está "caliente" desde el último disparo y llamar a fire() o set_fire() solo desperdiciará un turno. Podemos probar si el arma está fría llamando a gun_heat.
Apuntando de manera predictiva¶
O... "Usar la trigonometría para impresionar a tus amigos y destruir a tus enemigos".
Si quisiéramos poder golpear a un robot que recorre las paredes siempre fallaríamos, necesitamos poder predecir dónde estará en el futuro, pero ¿cómo podemos hacerlo?
Usando la anterior fórmula podemos calcular cuánto tiempo tardará una bala en llegar allí.
-
Distancia: se puede encontrar llamando a
distance_to(e.x, e.y) -
Velocidad: según la documentación de RCTR, una bala viaja a una velocidad de: $$ 20 - potenciaDeFuego \times 3 $$
-
Tiempo: podemos calcular el tiempo resolviendo: $$ Tiempo = {Distancia \over Velocidad} $$
El siguiente código lo hace:
1 2 3 4 5 6 7 | |
Obteniendo futuras coordenadas X, Y¶
A continuación, debemos calcular la posición futura de nuestro enemigo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Girando el arma al punto previsto¶
Ahora debemos apuntar nuestro cañón al lugar previsto de impacto:
1 | |
¡Disparando!¶
Por último disparamos nuestro cañón:
1 2 | |
Mejor aún...!¶
Aquí te dejo algunas mejoras para que las valores:
- ¿Debemos esperar siempre a que el arma esté completamente en la dirección calculada?
- Cuando nuestro enemigo se acerca a una pared, nuestros disparos impactan en ella.
- ¿Puedo saber a quién disparo si mi previsión de impacto falla mucho?
Investigación y desarrollo propio¶
A partir de aquí el trabajo será individual de cada alumno (puede solaparse con el paso 3). Deberéis investigar/prever a vuestros adversarios (los conocidos, y los de vuestros compañeros). Aplicar las técnicas que consideréis más útiles para intentar quedar lo más arriba posible en la tabla de clasificación.
Algunos recursos adicionales:
- Documentación oficial de la API Python
- Tutorial oficial "My First Bot for Python"
- The Book of Robocode — estrategias avanzadas
- RoboWiki — conocimiento comunitario (código en Java, conceptos universales)