- Introducció
- Sobre la Competició
- Arquitectura General
- Versions del Projecte
- Reflexions i Aprenentatges
- Conclusions
Astro és una solució per a la competició MIT BattleCode 2026, una competició acadèmica on es desenvolupen IA per a jocs en temps real. El projecte evolucionà a través de tres versions progressives, cada una millorant els algorismes de navegació i les estratègies de cooperació.
BattleCode és una competició anual organitzada pel MIT on equips competeixen amb robots virtuals en un mapa 2D. Els robots han de:
- Explorar el mapa
- Recopilar recursos (formatge)
- Lluitар contra els enemics rivals
- Comunicar-se entre ells de forma cooperativa
La competició presenta desafiaments únics:
- Limitacions de bytecode: Cada turno tenim un pressupost limitat de bytecode (operacions de máquina)
- Comunicació limitada: La comunicació entre robots pot ser detectada per enemics
- Mapa desconegut: Els robots només "veuen" un radi limitat
- Temps d'execució: Cada turno ha de calcular-se en mil·lisegons
Implementar algorismes eficients ha estat crítica pel èxit del projecte:
- A Pathfinding*: Permet navegació òptima dins el pressupost de bytecode
- Mapes Lleugers: Emmagatzematge eficient d'informació sense esgotar memòria
- Lazy Evaluation: Calcular només el necessari per turno
- Heurístiques Intel·ligents: Detectar patrons i recursos de forma eficient
Els deadlines de la competició feien que cada iteració hagués de ser viable:
- Versió 1 (Primera Versió): Implementació bàsica amb exploració i comunicació
- Versió 2 (A)*: Millora significativa en navegació amb algoritmes més complexos
- Versió 3 (ALazy): Optimitzacions finals per rendiment
Aquesta pressió va ajudar a prioritzar les funcionalitats més crítiques i evitar sobre-enginyeria.
Totes les versions segueixen una arquitectura RAT_KING + BABY_RATS:
┌─────────────────────────────────────┐
│ RAT_KING (Unitat Principal) │
│ • Spawna BABY_RATS │
│ • Coordina accions globals │
│ • Defensa del nucli │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
│ │
┌───▼──┐ ┌──▼──┐
│BABY │ ... │BABY │
│RAT 1 │ │RAT N│
└──────┘ └─────┘
Objectiu: Implementar els fonaments de cooperació i navegació
- Exploració Directa: Patrons de moviment predefinits (PATTERN_NORTH, PATTERN_NORTHEAST, etc.)
- Memòria de Parets: Recordar quines direccions estan obstruïdes
- Coordinació bàsica: Comunicació per missatges simples
- Gestió de Recursos: Identificar mines de formatge disponibles
- Defensa Cooperativa: Reaccionar davant amenaces
graph TD
A["Inici Torn"] --> B{Soc RAT_KING?}
B -->|Sí| C["Spawnar BABY_RATS<br/>si és necessari"]
B -->|No| D["Soc BABY_RAT"]
C --> E["Escanejar mapa"]
D --> F{Hi ha amenaça?}
F -->|Sí| G["Retornar al RAT_KING"]
F -->|No| H{Tenc objectiu?}
H -->|Sí| I["Seguir patró de moviment"]
H -->|No| J["Explorar segons patrón"]
I --> K["Moure'm"]
J --> K
G --> K
E --> L["Actualitzar comunicació"]
L --> M["Fi Torn"]
K --> M
- Patrons de moviment rígids
- Exploració ineficient (pot revisitar la mateixa zona)
- No recalcula rutes dinàmicament
Objectiu: Introduir navegació intel·ligent basada en gràfics
- Graph Data Structure: Representació del mapa passable
- A Algorithm*: Trobar camins òptims entre dos punts
- Heurística Euclidiana: Guiar la búsqueda
- Pathfinding Col·laboratiu: Múltiples BABY_RATS busquen rutes òptimes
- Graph Dinàmic: El gràfic s'actualitza quan es descobreixen parets
- Evitar Atolladissos: Si la ruta bloqueja, recalcular
- Coordinació per Objectius: Cada BABY_RAT té un objectiu específic
graph TD
A["Inici Torn"] --> B{Necessito nova ruta?}
B -->|Sí| C["Construir Graph del mapa"]
C --> D["Executar A* fins a objectiu"]
D --> E["Guardar ruta"]
B -->|No| F["Utilitzar ruta anterior"]
E --> G{Path està bloquejat?}
F --> G
G -->|Sí| H["Marcar com a bloquejat -
Recalcular"]
G -->|No| I["Seguir següent pas de ruta"]
H --> J["Moure'm o buscar alternativa"]
I --> J
J --> K["Fi Torn"]
Graph.java:
- Emmagatzema nodes (locations passables)
- Arestes entre ubicacions adjacents
- Actualitzacions dinàmiques
AStar.java:
- Algoritme A* estàndard
- Open/Closed sets
- Heurística de distància euclidiana
Edge.java i Vertex.java:
- Structures de dades per al gràfic
Objectiu: Optimitzar bytecode i memòria per millorar el rendiment
- LazyAStar: No precalcula tot el gràfic, només expande nodes necessaris
- LightweightMap: Estructura més eficient per emmagatzemar una mapa del terreny
- Lazy Evaluation: Calcular només el necessari per turno
- Generació Lazy del Graph: Expandir nodes sota demanda
- Memòria Eficient: Usar bitmaps per a la mapa
- Priorització Dinàmica: Enfatitzar exploration basada en context
- Recollida de Formatge Optimitzada: Detectar mines amb menys escanneigs
- Patrons Adaptatius: Canviar estratègia segons el context del joc
graph TD
A["Inici Torn"] --> B["Actualitzar LightweightMap"]
B --> C{Bytecode disponible?}
C -->|Poc| D["Accions mínimes exploració
bàsica"]
D --> E["Moure'm"]
C -->|Suficient| F{Ja tenim ruta?}
F -->|Sí| G["Expandir AStar incrementalment"]
F -->|No| H["Iniciar AStar amb heurística"]
G --> I{Path completa?}
H --> I
I -->|Sí| J["Seguir ruta"]
I -->|No| K["Fer un pas cap a objectiu"]
J --> E
K --> E
E --> L["Actualitzar mapa local"]
L --> M["Fi Torn"]
- Expanded Nodes: Mantenir una llista de nodes que ja hem explorat
- Open Set petit: Prioritzar els nodes més prometedors
- Heurística adaptativa: Ajustar segons la situació
- Bytecode Budget: Cada decisió es pesa pel cost
- Memòria Compactada: Usar bit-flags i estructures eficients
- Batch Processing: Processar múltiples BABY_RATS en paral·lel
L'evolució de les tres versions demostra clarament com els algorismes adequats milloren exponencialment el rendiment:
Els deadlines de la competició obligaven a:
- Prioritzar funcionalitats críticas (pathfinding > gràfics visuals)
- Iterar ràpidament (versions cada poques setmanes)
- Acceptar trade-offs (complexitat vs. velocitat)
Aquesta pressió era beneficiosa perquè:
- Força a pensar quins algorismes són essencials
- Motiva a mesurar el rendiment (bytecode, memòria)
- Prevé sobre-enginyeria sobre detalls menors
Cada versió illustra un trade-off diferent:
| Versió | Memòria | Velocitat | Flexibility |
|---|---|---|---|
| 1 | Baixa | Lenta | Baixa |
| 2 | Alta | Mig | Alta |
| 3 | Mig | Ràpida | Alta |
- Evolució Clara: 3 versions que mostren millora progressiva
- Algorismes Robustos: A* i LazyAStar funcionen eficientment
- Arquitectura Escalable: RAT_KING + BABY_RATS és flexible
- Deadline Respectats: Totes les versions funcionals
Aquest projecte ha demostrat que els deadlines pressió no són enemics sinó motors de millora. La necessitat d'optimitzar em va forçar a implementar algorismes avançats i metodologies de programació. Les tres versions representen l'evolució del pensament: de senzill i explícit, a complex i implícit, a eficient i elegant.
- Introduction
- About the Competition
- General Architecture
- Project Versions
- Reflections and Learning
- Conclusions
Astro is a solution for the MIT BattleCode 2026 competition, an academic competition where teams develop AI for real-time strategic games. The project evolved through three progressive versions, each improving pathfinding algorithms and cooperation strategies.
BattleCode is an annual competition organized by MIT where teams compete with virtual robots on a 2D map. Robots must:
- Explore the map
- Gather resources (cheese)
- Fight rival enemies
- Communicate cooperatively with each other
The competition presents unique challenges:
- Bytecode Limitations: Each turn we have a limited bytecode budget (machine operations)
- Limited Communication: Communication between robots can be detected by enemies
- Unknown Map: Robots only "see" a limited radius
- Execution Time: Each turn must compute in milliseconds
Implementing efficient algorithms has been critical to the project's success:
- A Pathfinding*: Enables optimal navigation within bytecode budget
- Lightweight Maps: Efficient storage of information without exhausting memory
- Lazy Evaluation: Calculate only what's necessary per turn
- Smart Heuristics: Detect patterns and resources efficiently
Competition deadlines meant each iteration had to be viable:
- Version 1 (First Version): Basic implementation with exploration and communication
- Version 2 (A)*: Significant improvement in navigation with more complex algorithms
- Version 3 (ALazy): Final optimizations for performance
This pressure helped prioritize the most critical features and avoid over-engineering.
All versions follow a RAT_KING + BABY_RATS architecture:
┌─────────────────────────────────────┐
│ RAT_KING (Primary Unit) │
│ • Spawn BABY_RATS │
│ • Coordinate global actions │
│ • Defend the core │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
│ │
┌───▼──┐ ┌──▼──┐
│BABY │ ... │BABY │
│RAT 1 │ │RAT N│
└──────┘ └─────┘
Goal: Implement the fundamentals of cooperation and navigation
- Direct Exploration: Predefined movement patterns (PATTERN_NORTH, PATTERN_NORTHEAST, etc.)
- Wall Memory: Remember which directions are obstructed
- Basic Coordination: Communication via simple messages
- Resource Management: Identify available cheese mines
- Cooperative Defense: React to threats
graph TD
A["Turn Start"] --> B{Am I RAT_KING?}
B -->|Yes| C["Spawn BABY_RATS<br/>if needed"]
B -->|No| D["I am BABY_RAT"]
C --> E["Scan map"]
D --> F{Is there<br/>threat?}
F -->|Yes| G["Return to RAT_KING"]
F -->|No| H{Do I have<br/>goal?}
H -->|Yes| I["Follow movement<br/>pattern"]
H -->|No| J["Explore<br/>according to pattern"]
I --> K["Move"]
J --> K
G --> K
E --> L["Update<br/>communication"]
L --> M["End Turn"]
K --> M
- Rigid movement patterns
- Inefficient exploration (may revisit same area)
- Does not dynamically recalculate routes
Goal: Introduce smart navigation based on graphs
- Graph Data Structure: Representation of passable map
- A Algorithm*: Find optimal paths between two points
- Euclidean Heuristic: Guide the search
- Collaborative Pathfinding: Multiple BABY_RATS find optimal routes
- Dynamic Graph: Graph updates when walls are discovered
- Avoid Getting Stuck: If route blocks, recalculate
- Goal-Based Coordination: Each BABY_RAT has specific objectives
graph TD
A["Turn Start"] --> B{Need<br/>new route?}
B -->|Yes| C["Build Graph<br/>of map"]
C --> D["Execute A*<br/>to goal"]
D --> E["Save route"]
B -->|No| F["Use<br/>previous route"]
E --> G{Is path<br/>blocked?}
F --> G
G -->|Yes| H["Mark as blocked<br/>Recalculate"]
G -->|No| I["Follow next<br/>path step"]
H --> J["Move<br/>or find alternative"]
I --> J
J --> K["End Turn"]
Graph.java:
- Stores nodes (passable locations)
- Edges between adjacent locations
- Dynamic updates
AStar.java:
- Standard A* algorithm
- Open/Closed sets
- Euclidean distance heuristic
Edge.java and Vertex.java:
- Data structures for the graph
Goal: Optimize bytecode and memory for better performance
- LazyAStar: Does not precalculate entire graph, only expands necessary nodes
- LightweightMap: More efficient structure to store terrain information
- Lazy Evaluation: Calculate only what's necessary per turn
- Lazy Graph Generation: Expand nodes on demand
- Efficient Memory: Use bitmaps for terrain map
- Dynamic Prioritization: Emphasize exploration based on game context
- Optimized Cheese Collection: Detect mines with fewer scans
- Adaptive Patterns: Change strategy based on game context
graph TD
A["Turn Start"] --> B["Update<br/>LightweightMap"]
B --> C{Bytecode<br/>available?}
C -->|Low| D["Minimal actions<br/>basic exploration"]
D --> E["Move"]
C -->|Sufficient| F{Have<br/>route?}
F -->|Yes| G["Expand AStar<br/>incrementally"]
F -->|No| H["Start AStar<br/>with heuristic"]
G --> I{Path<br/>complete?}
H --> I
I -->|Yes| J["Follow route"]
I -->|No| K["Take one step<br/>towards goal"]
J --> E
K --> E
E --> L["Update<br/>local map"]
L --> M["End Turn"]
- Expanded Nodes: Keep list of nodes we've already explored
- Small Open Set: Prioritize most promising nodes
- Adaptive Heuristic: Adjust based on situation
- Bytecode Budget: Each decision weighted by cost
- Compact Memory: Use bit-flags and efficient structures
- Batch Processing: Process multiple BABY_RATS in parallel
The evolution of the three versions clearly demonstrates how proper algorithms exponentially improve performance:
- Version 1: Exploration = O(n) exhaustive
- Version 2: Pathfinding = O(n log n) with A*
- Version 3: Pathfinding = O(k) with lazy evaluation (k << n)
Competition deadlines forced us to:
- Prioritize critical features (pathfinding > visual graphics)
- Iterate quickly (versions every few weeks)
- Accept trade-offs (complexity vs. speed)
This pressure was beneficial because:
- Forces thinking about which algorithms are essential
- Motivates measuring performance (bytecode, memory)
- Prevents over-engineering minor details
Each version illustrates a different trade-off:
| Version | Memory | Speed | Flexibility |
|---|---|---|---|
| 1 | Low | Slow | Low |
| 2 | High | Medium | High |
| 3 | Medium | Fast | High |
- Clear Evolution: 3 versions showing progressive improvement
- Robust Algorithms: A* and LazyAStar work efficiently
- Scalable Architecture: RAT_KING + BABY_RATS is flexible
- Deadlines Met: All versions functional
This project has demonstrated that deadline pressure is not an enemy but an engine of improvement. The need to optimize forced me to learn advanced algorithms and disciplined programming methodologies. The three versions represent the evolution of thought: from simple and explicit, to complex and implicit, to efficient and elegant.
The 🤖 Astro is more than an agent for a game; it is a metaphor for fast, iterative development focused on results.
- Introducción
- Sobre la Competición
- Arquitectura General
- Versiones del Proyecto
- Reflexiones y Aprendizajes
- Conclusiones
Astro es una solución para la competición MIT BattleCode 2026, una competición académica donde los equipos desarrollan IA para juegos estratégicos en tiempo real. El proyecto evolucionó a través de tres versiones progresivas, cada una mejorando los algoritmos de navegación y las estrategias de cooperación.
BattleCode es una competición anual organizada por el MIT donde equipos compiten con robots virtuales en un mapa 2D. Los robots deben:
- Explorar el mapa
- Recopilar recursos (queso)
- Luchar contra enemigos rivales
- Comunicarse cooperativamente entre ellos
La competición presenta desafíos únicos:
- Limitaciones de Bytecode: Cada turno tiene un presupuesto limitado de bytecode (operaciones de máquina)
- Comunicación Limitada: La comunicación entre robots puede ser detectada por enemigos
- Mapa Desconocido: Los robots solo "ven" un radio limitado
- Tiempo de Ejecución: Cada turno debe calcularse en milisegundos
Implementar algoritmos eficientes ha sido crítico para el éxito del proyecto:
- A Pathfinding*: Permite navegación óptima dentro del presupuesto de bytecode
- Mapas Ligeros: Almacenamiento eficiente de información sin agotar memoria
- Lazy Evaluation: Calcular solo lo necesario por turno
- Heurísticas Inteligentes: Detectar patrones y recursos de forma eficiente
Los deadlines de la competición hacían que cada iteración tuviera que ser viable:
- Versión 1 (Primera Versión): Implementación básica con exploración y comunicación
- Versión 2 (A)*: Mejora significativa en navegación con algoritmos más complejos
- Versión 3 (ALazy): Optimizaciones finales para rendimiento
Esta presión ayudó a priorizar las funcionalidades más críticas y evitar sobre-ingeniería.
Todas las versiones siguen una arquitectura RAT_KING + BABY_RATS:
┌─────────────────────────────────────┐
│ RAT_KING (Unidad Principal) │
│ • Genera BABY_RATS │
│ • Coordina acciones globales │
│ • Defiende el núcleo │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
│ │
┌───▼──┐ ┌──▼──┐
│BABY │ ... │BABY │
│RAT 1 │ │RAT N│
└──────┘ └─────┘
Objetivo: Implementar los fundamentos de cooperación y navegación
- Exploración Directa: Patrones de movimiento predefinidos (PATTERN_NORTH, PATTERN_NORTHEAST, etc.)
- Memoria de Paredes: Recordar qué direcciones están obstruidas
- Coordinación Básica: Comunicación mediante mensajes simples
- Gestión de Recursos: Identificar minas de queso disponibles
- Defensa Cooperativa: Reaccionar ante amenazas
graph TD
A["Inicio de Turno"] --> B{¿Soy RAT_KING?}
B -->|Sí| C["Generar BABY_RATS<br/>si es necesario"]
B -->|No| D["Soy BABY_RAT"]
C --> E["Escanear mapa"]
D --> F{¿Hay<br/>amenaza?}
F -->|Sí| G["Volver a RAT_KING"]
F -->|No| H{¿Tengo<br/>objetivo?}
H -->|Sí| I["Seguir patrón<br/>de movimiento"]
H -->|No| J["Explorar<br/>según patrón"]
I --> K["Mover"]
J --> K
G --> K
E --> L["Actualizar<br/>comunicación"]
L --> M["Fin de Turno"]
K --> M
- Patrones de movimiento rígidos
- Exploración ineficiente (puede revisitar la misma zona)
- No recalcula rutas dinámicamente
Objetivo: Introducir navegación inteligente basada en gráficos
- Graph Data Structure: Representación del mapa transitable
- A Algorithm*: Encontrar caminos óptimos entre dos puntos
- Heurística Euclidiana: Guiar la búsqueda
- Pathfinding Colaborativo: Múltiples BABY_RATS buscan rutas óptimas
- Graph Dinámico: El gráfico se actualiza cuando se descubren paredes
- Evitar Atolladeros: Si la ruta se bloquea, recalcular
- Coordinación por Objetivos: Cada BABY_RAT tiene objetivos específicos
graph TD
A["Inicio de Turno"] --> B{¿Necesito<br/>nueva ruta?}
B -->|Sí| C["Construir Graph<br/>del mapa"]
C --> D["Ejecutar A*<br/>hasta objetivo"]
D --> E["Guardar ruta"]
B -->|No| F["Usar<br/>ruta anterior"]
E --> G{¿Está<br/>bloqueada?}
F --> G
G -->|Sí| H["Marcar como bloqueada<br/>Recalcular"]
G -->|No| I["Seguir siguiente<br/>paso de ruta"]
H --> J["Mover<br/>o buscar alternativa"]
I --> J
J --> K["Fin de Turno"]
Graph.java:
- Almacena nodos (ubicaciones transitables)
- Aristas entre ubicaciones adyacentes
- Actualizaciones dinámicas
AStar.java:
- Algoritmo A* estándar
- Conjuntos Open/Closed
- Heurística de distancia euclidiana
Edge.java y Vertex.java:
- Estructuras de datos para el gráfico
Objetivo: Optimizar bytecode y memoria para mejor rendimiento
- LazyAStar: No precalcula todo el gráfico, solo expande nodos necesarios
- LightweightMap: Estructura más eficiente para almacenar información del terreno
- Lazy Evaluation: Calcular solo lo necesario por turno
- Generación Lazy del Graph: Expandir nodos bajo demanda
- Memoria Eficiente: Usar bitmaps para el mapa del terreno
- Priorización Dinámica: Enfatizar exploración basada en contexto del juego
- Recopilación de Queso Optimizada: Detectar minas con menos escaneos
- Patrones Adaptativos: Cambiar estrategia según contexto del juego
graph TD
A["Inicio de Turno"] --> B["Actualizar LightweightMap"]
B --> C{¿Bytecode disponible?}
C -->|Poco| D["Acciones mínimas exploración
básica"]
D --> E["Mover"]
C -->|Suficiente| F{¿Ya tengo ruta?}
F -->|Sí| G["Expandir AStar incrementalmente"]
F -->|No| H["Iniciar AStar con heurística"]
G --> I{¿Ruta completa?}
H --> I
I -->|Sí| J["Seguir ruta"]
I -->|No| K["Dar un paso hacia objetivo"]
J --> E
K --> E
E --> L["Actualizar mapa local"]
L --> M["Fin de Turno"]
- Nodos Expandidos: Mantener lista de nodos ya explorados
- Open Set Pequeño: Priorizar nodos más prometedores
- Heurística Adaptativa: Ajustar según la situación
- Presupuesto de Bytecode: Cada decisión ponderada por costo
- Memoria Compactada: Usar bit-flags y estructuras eficientes
- Procesamiento en Lote: Procesar múltiples BABY_RATS en paralelo
La evolución de las tres versiones demuestra claramente cómo los algoritmos adecuados mejoran exponencialmente el rendimiento:
Los deadlines de la competición obligaban a:
- Priorizar funcionalidades críticas (pathfinding > gráficos visuales)
- Iterar rápidamente (versiones cada pocas semanas)
- Aceptar compromisos (complejidad vs. velocidad)
Esta presión fue beneficiosa porque:
- Fuerza a pensar qué algoritmos son esenciales
- Motiva a medir rendimiento (bytecode, memoria)
- Previene sobre-ingeniería en detalles menores
Cada versión ilustra un compromiso diferente:
| Versión | Memoria | Velocidad | Flexibilidad |
|---|---|---|---|
| 1 | Baja | Lenta | Baja |
| 2 | Alta | Media | Alta |
| 3 | Media | Rápida | Alta |
- Evolución Clara: 3 versiones que muestran mejora progresiva
- Algoritmos Robustos: A* y LazyAStar funcionan eficientemente
- Arquitectura Escalable: RAT_KING + BABY_RATS es flexible
- Deadlines Respetados: Todas las versiones funcionales
Este proyecto ha demostrado que la presión de los deadlines no es un enemigo sino un motor de mejora. La necesidad de optimizar me obligó a aprender algoritmos avanzados y metodologías de programación disciplinada. Las tres versiones representan la evolución del pensamiento: de lo simple y explícito, a lo complejo e implícito, a lo eficiente y elegante.
MIT BattleCode 2026 - Proyecto Astro