Está en la página 1de 38

INTELIGENCIA EN REDES DE COMUNICACIONES

ROBOCODE

Jorge Carrasco Troitiño NIA 100029724


Elisabeth Pérez Gracía NIA 100029638
INTRODUCCIÓN:
Robocode es un entorno de simulación de guerras de robots, desarrollado por
Alphaworks de IBM, con Mathew Nelson a la cabeza, en el que se pueden programar
tanques de combate en Java para pelear en el campo de batalla contra tanques
programados por otros jugadores. Existen dos modos de juego: batalla individual, en el
que cada robot lucha contra todos los demás, y batalla en equipo, en el que un ejército
de robots lucha por la victoria de modo colaborativo.

Se trata de un divertido juego de programación que permite aprender Java


creando robots Java propios, que no son más que objetos Java reales que combaten en
pantalla contra otros robots. Mediante la programación en Robocode se puede aprender
Java, manejar eventos….Utiliza un gestor de seguridad particular que permite a clases
Java particulares escritas por cualquiera ejecutarse de manera segura en nuestro sistema.

Todas las clases generadas en Robocode extienden de la clase


robocode.Robot, con métodos que permiten interactuar con el juego.

En Robocode hay dos elementos principales, los robots y los combates. Los
combates se llevan a cabo en el campo de batalla, entre los robots, que juegan por sí
mismos. En cuanto a los robots, un robot Robocode típico tendrá el siguiente aspecto:

a simple vista podemos observar la presencia de un cañón giratorio sobre el robot, y un


radar, también giratorio sobre el cañón. Tanto el vehículo, como el cañón, como el radar
pueden rotar independientemente (siempre y cuando así lo indiquemos a través de
métodos como setAdjustGunForRobotTurn(false)), gracias a una serie de métodos
básicos (turnRight, ahead, turnGunmRight, turnRadarRight…). Toda la información
acerca de nuestro Robot y su situación en la partida podrá ser accedida fácilmente (a
través de métodos como getX, getHeading, getGunHeading, getRadarHeading), así
como las características físicas del campo de batalla (getBattleFieldWidth/Height).

Al ser el objetivo del juego derrotar a los otros robots, Robocode contará
también con multitud de métodos para gestionar los disparos y los ataques a nuestros
enemigos.

Al principio de cada combate todo robot comienza con un nivel de energía por
defecto, y se considera destruido el robot cuando su nivel de energía cae hasta cero,
existiendo varias situaciones en la que la energía de un robot puede disminuir, como son
que te alcance un disparo enemigo, que te choques con un enemigo, que te choques
contra una pared, además de por la energía liberada en el disparo (cuanta más energía se
imprima a un disparo más daño inflingirá en el robot enemigo en caso de alcanzarlo
pero también más nos restará a nosotros), por ejemplo. Pero no todo son pérdidas de
energía, también se recupera energía cuando se alcanza a algún enemigo o de forma
constante por “enfriamiento de los cañones”.

Durante la batalla se generan “eventos”, estos eventos son situaciones que se


producen a partir de las cuales el robot puede decidir hacer unas cosas u otras. Un robot
básico tiene por defecto manejadores para varios eventos, que consisten en no hacer
nada cuando se produzcan, pero al ser todo robot heredero de la clase Robot, estos
métodos se pueden sobrescribir y así añadir funcionalidad a los robots cuando dichos
eventos se produzcan. Algunos de los eventos usados más frecuentes son:

ScannedRobotEvent: Se produce cuando el radar detecta un robot a su paso


por una zona angular.
HitByBulletEvent: Se produce cuando el robot es alcanzado por una bala de
uno de los cañones enemigos.
HitRobotEvent: Se produce cuando nuestro robot alcaza con un disparo a
uno de los enemigos.
HitWallEvent: Se produce cuando el robot choca contra un muro de los que
limitan el campo de batall.

A la hora de programar uno de estos robots tendremos que tener en cuenta cuatro
áreas fundamentales del código:

- un área en la que se definan las variables de clase, disponibles dentro del


método run(), así como en el resto implementados.

- el propio método run(), que es llamado por el gestor de combate para comenzar
la vida del robot y típicamente se divide en dos áreas: un en la que se definen las
cosas que sólo se harán una vez por cada instancia del robot, y una segunda parte
dentro de un while infinito en la que se define la acción repetitiva en la que se
verá envuelto el robot.

- Métodos auxiliares para usar por el robot dentro de su método run. En esta
zona también se ponen los manejadores de eventos que se quieran implementar.

Cualquier persona puede crear subclases de Robot y añadir nuevas


funcionalidades que pueden ser usadas para construir robots. Robocode proporciona una
subclase a Robot: AdvancedRobot, que permite realizar las llamadas del API de
manera asíncrona, es decir, cambiar las acciones asociadas al robot en cada turno (los
métodos son básicamente los mismos, añadiendo “set” delante de sus nombres), que en
Robocode reciben el nombre de “tick”(relativo a un frame gráfico plasmado en el
campo de batalla). Cuando visualizamos combates en Robocode, la máquina de
simulación está sacando por pantalla múltiples frames gráficos estáticos de manera
secuencial, durante cada uno de estos frames nuestro código puede retomar el control y
decidir qué hacer con el robot, y dada la velocidad de los ordenadores actuales, esto nos
permitirá ejecutar gran cantidad de instrucciones durante uno de estos frames o ticks.
En general, AdvancedRobot proporciona tres nuevas habilidades: Llevar a cabo
múltiples movimientos simultáneamente, decidir la acción o la estrategia del robot a
cada tick de reloj y definir y gestionar “custom events”.

En definitiva, nosotros lo que queremos hacer es un robot competitivo y que sea


capaz de derrotar a los adversarios y para ello tendremos que tener en cuenta múltiples
factores. Deberemos anticiparnos a las posiciones que los robots objetivos de nuestros
disparos tendrán antes de que nos encaremos y vayamos hacia ellos(predictive
targeting). También debemos tener presente en todo momento la detección y evasión de
los disparos enemigos. Sería bueno asimismo basar nuestra estrategia en la cantidad de
energía restante en cada momento.

Arquitectura Robocode:

Veamos brevemente un esquema de cómo sería la arquitectura de la máquina de


simulación de Robocode :

Se podría ver esta máquina de simulación como un programa de simulación que


toma versiones particulares de los robots durante el tiempo de ejecución, este conjunto
de robots hace uso de una serie de librerías suministradas (API robocode.Robot).
Físicamente cada robot es un hilo independiente de Java y el método run() contiene la
lógica que será ejecutada por el hilo.

El hilo gestor de combates gestiona los robots, las armas, y la distribución


espacial en el campo de batalla. En un turno típico, el hilo gestor de combates despierta
a cada hilo de robot y espera a que el robot complete su turno. Este intervalo dura
típicamente decenas de milisegundos (los robots más complejos tienden a usar solo 1 o
2 ms para estrategia y cálculos).
ESTRATEGIA INDIVIDUAL
Para realizar el robot de lucha individual tuvimos que emplear varias estrategias
que conjuntamente nos dieran un robot efectivo. Hay varias incógnitas a resolver:

Cuándo disparar
Con qué potencia
Dónde apuntar
A qué distancia colocarme
Cómo nos movemos

Para ello nuestro robot irá almacenando ciertos valores que le harán falta para
optimizar la elección de las incóginitas anteriores. Para ello almacenaremos las
variables más actuales:

La posición de nuestro robot (Point2D posicion )


La posición del robot enemigo (Point2D posicionEnemigo )
La distancia entre ambos (double distanciaEnemigo)
La velocidad del robot enemigo (double velocidadEnemigo)
El ángulo relativo entre nuestra posicion y la del contrario (double
anguloEnemigo)

También almacenaremos algunos valores del anterior “escaneo” del robot, esto
es;
La posición anterior de nuestro robot (Point2D posicionAnt )
La posición anterior del robot enemigo (Point2D posicionEnemigoAnt )

Estos valores se actualizan cada vez que detecta un robot, esto es, en el método
onScannedRobot.

Además de estos valores relativos a la posición, tomaremos medidas de las


energías ganadas y perdidas de ambos robots. Como estas medidas las emplearemos en
calcular la distancia óptima a la que colocarnos respecto del adversario, cada una de
estas variables será un array de tamaño el número de distancias muestreadas. Pero,
ahora nos crea un dilema de cuántas distancias quiero como muestras, esto es, de la
distancia máxima posible entre ambos robots (la diagonal del campo de batalla) ¿de qué
distancias tomo valores? Si tomamos todas las posibles distancias no obtendríamos
ninguna conclusión ya que, a parte de ser computacionalmente más costoso, las medidas
tomadas en cada distancia serían muy pobres. Por lo cual, decidimos escoger distancias
de tamaño 50. Entonces generaremos cuatro arrays:

private static double[] miEnergiaGanada, miEnergiaPerdida, enemigoEnergiaGanada,


enemigoEnergiaPerdida;

que tendrán de tamaño distanciaMaxima/50.

También iremos almacenando una variable que nos permitirá analizar el


movimiento del adversario. La llamamos anguloMovimiento que promedia el ángulo
que se mueve nuestro adversario entre dos escaneos. Para ello en onScannedRobot
calculamos el ángulo que se ha movido respecto al anterior escaneo, y promediamos con
los valores anteriores. Debido a que el adversario puede haber cambiado de tipo de
movimiento no tiene sentido hacer una media aritmética donde los valores actuales
tengan la misma importancia que los valores muy antiguos. Para ello realizamos una
“rolling average” que consiste en hacer un promedio tal que se tengan más en cuenta los
valores actuales, pero sin tampoco olvidar los valores muy antiguos. Esta técnica se
utiliza bastante en robocode y hemos decidido implementarla también nosotros. En
nuestro programa la función se denomina media y hace lo siguiente:

actual × n + nuevaEntrada × prima


media =
n + prima

donde:

• actual es la media actual la cual vamos a ponderar.


• nuevaEntrada es el valor leído recientemente el cual vamos a incluir en la
media.
• n es el número que representa la profundidad de la historia reciente.
• prima es el peso o importancia de esta lectura, permite a una nueva entrada tener
mayor o menor efecto.

Esta función devolverá la media alterada por el nuevo valor de manera que
“simula” guardar una lista con todas las lecturas con longitud n y devolviendo la media
de los valores de esta lista. Hay que tener en cuenta que los valores antiguos nunca son
olvidados, simplemente juegan un menor papel en el cálculo de la media actual.

Todas las variables anteriores serán estáticas ya que almacenaremos los valores
de una ronda a otra.

Al comenzar el método run() en primer lugar nuestro robot definirá un margen


respecto al campo de batalla, dicho margen marcará un límite del cual el robot no
deberá salir. Sería como un campo dentro del campo de batalla. Esto lo hacemos para no
topar con los bordes y perder por ello energía. Tomamos un valor de 20 que nos parece
después de varias pruebas un buen valor. Además activa que el cañón y el radar se
mantegan en la misma dirección mientras gira el robot con las funciones
setAdjustGunForRobotTurn(true) y setAdjustRadarForGunTurn(true);

Dentro de este método run() después de inicializar el robot entraremos en un bucle


infinito que controlará el movimiento básico de nuestro robot. Dentro de ese bucle lo q
se hacemos es:

1. Definir la velocidad máxima de nuestro robot


2. Controlar que no nos salgamos del margen establecido
3. Movernos en la dirección y la distancia calculadas
4. Mover el radar si no acabamos de detectar un robot
5. Ejecutar todo lo anterior ya que fueron llamadas no bloqueantes y tenemos que
pasarle el control al robocode para que las ejecute (execute()).

Planteamos ahora las incógnitas nombradas al principio:


¿Cuando disparar?

Existen varias condiciones que podemos tener en cuenta a la hora de disparar. Las
primeras que tenemos en cuenta son:

• Que el cañón esté parado (haya terminado de girar)


getGunTurnRemaining()==0
• Que el cañón esté frio: getGunHeat() == 0

Pero estas condiciones son necesarias pero no estratégicas. Buscaremos entonces


alguna condición que optimice el momento a disparar. Definimos entonces que nuestro
robot dispará sólo cuando la distancia a nuestro adversario sea menor que 500, ya que
una distancia mayor la consideraremos como muy lejana. Además consideraremos una
condición en la que si mi energía es muy pequeña sólo disparará a distancias más cortas.
La condición considerada será que además de las dos condiciones se cumpla:

(getEnergy() > 0.2 || distanciaEnemigo < 100)

Esto es, que cuando mi energía sea menor que 0.2 sólo disparará si mi distancia
al enemigo es menor de 100, así aseguramos que cuando estamos apunto de morir si
disparamos, tener mayor probabilidad de acertar.

Para gestionar esto utilizaremos la posibilidad que nos dan los AdvancedRobot
de utilizar condiciones. Para ello añadimos un evento propio mediante addCustomEvent
pasándole como parámetro una nueva condición donde su función test() controle estas
condiciones. Cuando esta función test() devuelva true (esto es, todas las condiciones se
han cumplido) se lanzará un CustomEvent y ejecutará el método onCustomEvent, que
disparará setFireBullet con la potencia estimada (explicado a continuación).

¿Con qué potencia disparar?

Conservar la energía de nuestro robot es una buena manera de sobrevivir en una batalla.
Hay muchas formas de conseguir esto, por ejemplo:

• Disparar con misiles de baja potencia a largas distancias.


• No disparar nunca misiles con una potencia mayor a la necesaria para matar al
enemigo.
• Reducir la potencia de los misiles (o no disparar) cuando nuestro robot tenga una
energía muy baja.

Escoger la correcta potencia de los misiles a disparar es una estrategia muy importante.

Lo primero, la probabilidad de acertar en el disparo está relacionada con la potencia del


disparo. Lo segundo es que hay que tener en cuenta que el enemigo nos está disparando,
entonces quizás la estrategia de mejor eficiencia de energía no es la mejor…

Podemos minimizar:

• La ganancia de energía por unidad de tiempo.


• La energía ganada por unidad disparada.
Hay varios factores que limitan la potencia de tiro. En primer lugar esta potencia deberá
estar comprendida entre 1 y 3. En segundo lugar, esta potencia la elegiremos según sea
la energía del enemigo, la distancia a éste y mi propia energía, de la siguiente forma:

• Cuando el enemigo tenga muy poca energía no dispararemos con potencia


superior que con aquella con la que pudiéramos derribarle. Al dispararle su nivel
de energía bajará según:

Disminución energía = máx{4*pot, 4*pot + 2*(pot-1)}

Como el mínimo daño que le vamos a causar va a ser una disminución de


energía de 4*pot entonces, la máxima potencia con la que deberíamos disparar
es de: energía del enemigo / 4.

• La distancia hacia el enemigo también delimitará la potencia con la que disparar,


ya que a mayor distancia no conviene disparar con gran potencia ya que nuestro
disparo tardará más en llegar y le habrá dado tiempo a moverse, por lo tanto
tendremos menos probabilidad de acertar.

Pero… ¿para una distancia dada cómo modelamos la potencia con la que
disparar? Consideramos la máxima distancia entre los dos robots que será
cuando cada uno esté en una esquina del campo en la diagonal mayor. Tomamos
como una distancia de referencia la mitad de esta distancia máxima, por lo que
la máxima potencia con la que deberíamos disparar estaría promediada por:
(Dmax/2) / Distancia al enemigo.

• El último factor a considerar es la energía que le queda a mi robot. Será


importante este factor cuando me quede poca energía ya que deberé de tener
cuidado con los disparos que hagamos. Promediamos por el valor de mi energía.

Así, escogeremos la potencia de la bala con la que disparar como el mínimo de


los factores anteriores.

¿A qué distancia colocarme?

La distancia a la que esté del robot enemigo determinará en gran medida los
resultados de la batalla. Si nos colocamos muy lejos conseguiremos que nuestro
enemigo falle más si nos movemos deprisa, pero también nosotros fallaremos más, por
lo que procuraremos no disparar si estamos muy lejos. La distancia óptima a la que
situarnos dependerá del robot adversario contra el que nos enfrentemos, por lo que la
calculamos dinámicamente. Como tomamos valores de las energías ganadas y perdidas
de ambos robots para una serie de distancias podemos definir una función de ventaja
para cada punto donde definimos. Entonces para una de las distancias muestreadas
promediamos la ventaja que obtenemos a partir de una relación entre nuestro beneficio
(la energía que gano y la energía que pierde el otro) y nuestra penalización (la energía
que perdemos y la que gana el otro). Esta relación tomara valor máximo 1 cuando no
obtengamos nada de penalización, el valor -1 cuando no obtengamos nada de beneficio
y 0 cuando obtengamos el mismo beneficio que penalización.
Por ello para calcular la distancia óptima a la que colocarme será evaluando la
ventaja obtenida para cada una de las distancias.

¿Dónde apuntar?

Cada vez que ejecutamos onScannedRobot() deberemos apuntar nuestro cañón


hacia el enemigo, teniendo en cuenta el ángulo de movimiento que vamos midiendo en
cada escaneo para predecir el movimiento del robot adversario. Para ello, investigando
en las diversas páginas sobre robocode descubrimos muchas estrategias y decidimos
implementar una de ellas. Se trata de los “cañones virtuales”. Consiste en detectar el
movimiento del robot, si se suele mover hacia la derecha o hacia la izquierda y en qué
medida. Para ello tomamos 3 cañones virtuales, uno recto, otro a la izquierda y otro a la
derecha. Definimos por tanto para cada cañón virtual una variable estática que
determina un promedio del ángulo de error del cañón. Esto es, cada vez que disparamos,
se genera una nueva condición cuya función de test() controlará cuando el tiro sea
fallido. En tal caso, calcula la diferencia entre el ángulo donde está el enemigo (dónde
debía nuestro robot haber disparado) y el ángulo donde estaba el misil cuando mi
distancia a éste es aproximadamente mi distancia al enemigo. Esta diferencia de ángulos
me dará el ángulo de error. Este ángulo de error se promediará con los anteriores del
cañón correspondiente mediante la función media explicada anteriormente.

Así, para cada cañón virtual disponemos de un ángulo donde se estima que se
suele encontrar el robot adversario cuando se mueve en esa dirección. Por lo que a la
hora de apuntar nuestro cañón, lo primero es elegir el cañón virtual correspondiente en
función del ángulo movido:
cañon virtual Izquierdo si anguloMovimiento < - 0.3
cañon virtual Derecho si anguloMovimiento > 0.3
cañon virtual Central si anguloMovimiento está en (-0.3,0.3)

Al ángulo en que se encuentra el robot enemigo le sumaremos el valor promedio del


ángulo de error cometido para el cañón correspondiente.

¿Cómo nos movemos?


Al arrancar nuestro robot con el método run() inicializamos una variable
llamada unidadMov la cual indica la máxima velocidad que podemos alcanzar y por
tanto la distancia que nos vamos moviendo. Este valor será aleatorio y variará cuando
un misil nos alcance, esto es, en el método onHitByBullet. Además variaremos el
sentido de nuestro robot (+1, -1) también aleatoriamente. Esto permitirá que el robot
contrario pueda analizar nuestro movimiento, y, sobre todo, cuando consiga
dispararnos.
SOLUCIÓN - IRCBot

Pero justamente para poder probar nuestro robot generamos varios casos para
ver como reaccionaba éste. Observamos que para un robot que cambiara constantemente
de sentido, nuestro IRCeitor era poco eficiente. Para probarlo generamos el IRCBot, un
robot con estrategia diferente al IRCeitor, capaz de ganarle la mayoría de veces. Su
estrategia la explicaremos mostrando los comentarios en el código que adjuntamos:

package ircPackage;
import robocode.*;

import java.awt.Color;
import java.util.Enumeration;
import java.util.Hashtable;

/*
* IRCBot.java
* El movimiento de este robot impide que se choque contra los muros, de
* manera que no pierde energía en ese sentido.
* Asimismo evita las balas y los enemigos. Todo ello es posible gracias a la
* "Evasion", para la que hay implementada una clase. Dependiendo del tipo de
* evasion en el que nos encontremos en cada momento(que depende de
* los eventos y situaciones que se van dando en el juego) así será el
* movimiento del robot.
*
* Los potencia de los disparos se lleva a cabo de manera aleatoria pero
* siempre a partir de nuestra energía y de la distancia a la que se
* encuentre el enemigo al que pretendemos disparar.
*
* Targeting: si no tenemos ya fijado un enemigo, se fija como enemigo el
* primero que detecte nuestro radar y otra serie de factores. se calculará de
* manera ciertamente aleatoria la orientación del cañón para el disparo,
* pero teniendo en cuenta el movimiento de dicho oponente, para así poder
* adelantarnos a él y darle de lleno.
*
*/

public class IRCBot extends AdvancedRobot{


/* Para fijar la velocidad adecuada en cada momento */
static double e_velocity;
/* Objetivo detectado al realizar el scaneo con el radar */
ScannedRobotEvent target;
/* Para saber si merece la pena una confrontacion directa con un enemigo */
double energy;
/* Objeto de tipo Evasor, que nos permitirá escapar de los distintos
peligros dependiendo del estado en el que se encuentre */
Evasor evasor;
/* Direccion de avance, +1 hacia donde ibamos, -1 en sentido contrario */
int dir;
/* Lista de objetivos */
Hashtable targets;
/* Variable de tiempo y de orientacion que nos ayudarán a no chocarnos
contra los muros */
double tiempoChoque;
double orientacionChoque;
/* Para saber cuanto tiempo llevamos sin ver a un enemigo: */
double lastsighted;

/*
* En este método se implementarán las operaciones que tiene que hacer el
* robot una vez por instancia (parte de código fuera del do-while)
* y las operaciones que se realizarán en cada turno del robot
* (parte de código contenida dentro del do-while).
*/
public void run(){
inicializacion();
//Del disparo se encargará el método onScannedRobot, aqui lo unico que
//hacemos es definir el movimiento en caso de que queden más robots
// o marcarnos un bailecito en caso de que no queden más robots,
// es decir, que hayamos ganado.
do{
if(getOthers() > 0)
mover();
else
bailecito();
execute();
} while(true);
}

/*
* Constructor de IRCBot donde se inicializan algunos de sus atributos:
*/
public IRCBot(){

target = null;
/*Inicializamos un objeto de tipo Evasor, que nos gestionará la evasión
de las situaciones de peligro.*/
evasor = new Evasor();
dir = 1;

targets = new Hashtable();


orientacionChoque = 0.0;
}

/*
* Método para inicializar el modo de operacion del robot
*/
public void inicializacion(){
setColors(Color.yellow, Color.blue, Color.black);
//Hacemos que tanto el cañón como el radar giren de manera independiente
al robot:
setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
//Y hacemos un scaneo inicial
setTurnRadarRight(360);
}

/*
* El movimiento de este robot funciona de la siguiente manera:
* En primer lugar hacemos un movimiento dependiendo del tipo de evasion en
* la que nos encontremos.
* En segundo lugar tratamos de evitar los muros
* Y también atendemos al caso en el que estemos solos contra otro robot.
*/
public void mover(){
//No haremos nada hasta que fijemos un objetivo:
if(target == null)
return;

//Si tenemos un objetivo con el que cebarnos....


if(getOthers() > 1){
setTurnRadarRight(360);
circleGrav();
}
else{
setMaxVelocity(Math.random() + 7);
setTurnRight(target.getBearing() + 90 + -((double)dir *
evasor.ang));
}

//Distancias a los muros que limitan el escenario de combate, que los


//tenemos que evitar ya que un choque contra estos nos resta energía
//vital:
double N = getBattleFieldHeight() - getY();
double S = getY();
double E = getBattleFieldWidth() - getX();
double W = getX();

//No nos vamos a chocar con los muros, ya que cuando estemos a menos
//de una cierta distancia de estos daremos media vuelta.

if(N < 70 && getHeading() > 270 && getHeading() < 360)
dir = -1;
else
if(N < 70 && getHeading() > 0.0 && getHeading() < 90)
dir = -1;
else
//Aquí estamos en el caso de que estemos en zona próxima al muro
// norte pero nuestro sentido de avance es tal que nos aleja de
//este peligro.
if(N < 70)
dir = 1;

if(S < 70 && getHeading() > 90 && getHeading() < 270)


dir = -1;
else
if(S < 70)
dir = 1;

if(E < 70 && getHeading() > 0.0 && getHeading() < 180)
dir = -1;
else
if(E < 70)
dir = 1;

if(W < 70 && getHeading() > 180 && getHeading() < 360)
dir = -1;
else
if(W < 70)
dir = 1;

//Si estamos en las zonas de peligro:


if(N < 70 || S < 70 || E < 70 || W < 70){
evasor.movetime = getTime();
evasor.sub_tipoEvasion = 1;
orientacionChoque = -dir * 10 - 90;
tiempoChoque = getTime();
}
else{
orientacionChoque = 0.0;
}

if(Evasor.tipoEvasion == 6){

if((double)getTime() - Evasor.type6time > 60){


Evasor.tipoEvasion++;
Evasor.type6time = getTime();

if(Evasor.tipoEvasion > 3){


Evasor.tipoEvasion = 1;
}
}
}

if(getOthers() > 1){


circleGrav();
}
//Solo hay un enemigo:
else{
switch(Evasor.tipoEvasion){

case 1:
mov1();
break;
case 2:
mov2();
break;
case 3:
mov3();
break;
case 4:
mov1();
mov2();
mov3();
break;
default:
mov1();
}
}
}

/*
* Movimiento que seguimos cuando tenemos más de un robot acechándonos:
*/
public void circleGrav() {
double orientacion = 10000;
double closest = 10000;

//Buscamos el enemigo más cercano:


for(Enumeration e = targets.elements(); e.hasMoreElements();){
Enemigo en = (Enemigo)e.nextElement();
if((en.estaVivo = true) && en.distancia < closest) {
closest = en.distancia;
//Establecemos una futura orientacion del robot enemigo aleatoria,
//basándonos en la que teníamos almacenada suya:
orientacion = en.orientacion + 180 + (Math.random() * 60 - 30);
if(getOthers() < 4)
orientacion -= 90; }
}

if(getOthers() >= 4)
orientacion += orientacionChoque;

if((double)getTime() - tiempoChoque > 10)


dir = 1;

setAhead(dir * 100);
setTurnRight(NormalRelativeAngle(orientacion));
}

/*
* A partir de aqui definimos tres tipos de movimiento distintos para
* cuando solo quede otro robot, para ser más efectivos en ese caso.
* Con esto lo que hacemos es que al adversario que está contra nosotros
* le resulte muy complicado preveernos
*/

/*
* En este primer movimiento, si ya se ha acabado el movimiento
* anteriormente definido,cambiamos el sentido y angulo de nuestro
* movimiento, así como el tiempo que va a durar este.
*/
public void mov1(){
if((double)getTime() - evasor.movetime > evasor.time){
dir *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 25;
evasor.time = Math.random() * 7 + 15;
}
setAhead(dir * 50);
}

/*
* Definimos otro movimiento de manera completamente distinta:
* Utilizando incluso un subtipo del evasor.
*/
public void mov2(){
if(target.getDistance() < 400)
evasor.time = Math.min(target.getDistance() / 12, 24);
else
evasor.time = 28;

//Como poco el tiempo del siguiente movimiento será 12 ticks


evasor.time = Math.max(evasor.time, 12);

if((double)getTime() - evasor.movetime > evasor.time){


evasor.sub_tipoEvasion *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 20;
}

//A su vez dentro de este movimiento tenemos dos variantes: ir hacia


delante o hacia atrás
if(evasor.sub_tipoEvasion == 1)
setAhead(dir * 100);
else
setAhead(dir * -5);
}

public void mov3(){


evasor.ahead = (int)(Math.random() * 30 + 30);
evasor.ang = Math.random() * 15;
}

/*
* Cuando nuestro robot muere ahí no se queda la cosa,
* aprende un poco y pone el estado en el que ha de comenzar el evasor
* en la siguiente ronda.
*/
public void onDeath(DeathEvent e){
switch(Evasor.tipoEvasion){
case 1:
Evasor.tipoEvasion = 2;
break;
case 2:
Evasor.tipoEvasion = (int)(Math.random()*3.9900000000000002)+3;
break;
default:
Evasor.tipoEvasion = 1;
}

/*
* En este método se implementan las acciones a realizar cuando nos alcance
* una bala enemiga
*/
public void onHitByBullet(HitByBulletEvent e){
if(Evasor.tipoEvasion == 5){
Evasor.tipoEvasion++;

if(Evasor.tipoEvasion > 3){


Evasor.tipoEvasion = 1;
}
}
}

/*
* Este método se ejecuta cuando muere uno de los robots de la batalla,
* evento que es recogido por RobotDeathEvent.
* De entre los robots que teníamos como posibles objetivos, pasamos a
* considerar como muerto al robot que acaba de morir, y si dicho robot
* coincidía con el objetivo actual, a través de decir que lastsighted es
* cero dejará de ser nuestro objetivo.
*/
public void onRobotDeath(RobotDeathEvent e){

Enemigo en = (Enemigo)targets.get(e.getName());
en.estaVivo = false;
targets.put(e.getName(), en);
if(e.getName() == target.getName())
lastsighted = 0.0;
}

/*
* En este método se implememtan aquellas acciones que hay que realizar
* cuando al paso del scanner se detecta la presencia de un robot, no
* detectado antes o sí
*/
public void onScannedRobot(ScannedRobotEvent e){

Enemigo en;

//Vemos si el robot detectado ya lo habíamos visto antes o no


if(targets.containsKey(e.getName())){
en = (Enemigo)targets.get(e.getName());
}
else{
en = new Enemigo();
targets.put(e.getName(), en);
}
en.nombre = e.getName();
en.distancia = e.getDistance();
en.orientacion = e.getBearing();
en.estaVivo = true;//si lo hemos visto...

if(Evasor.tipoEvasion == 3){
energy -= e.getEnergy();
if(energy <= (double)3 && energy > 0.0)
setAhead((double)dir * evasor.ahead);
energy = e.getEnergy();
}

/*
Si se cumple una o más de estas cosas:
- Actualmente no tenemos objetivo(porque se haya muerto el que teníamos p ej)
- El objetivo es el mismo que acabamos de detectar
- El robot recién detectado está mucho más cerca del que tenemos fijado como
enemigo prioritario
- Hace más de ocho ticks que no vemos un robot
entonces vamos a por el.
*/
if(target == null || e.getName() == target.getName() ||
e.getDistance() < target.getDistance() - 50 || (double)getTime() - lastsighted
> 8){
target = e;
lastsighted = getTime();
//comienza a contar de nuevo el tiempo desde el que vimos al ultimo enemigo.
double abs_orientacion = e.getBearingRadians() +
getHeadingRadians();
setTurnRadarRightRadians(Math.asin(Math.sin(abs_orientacion -
getRadarHeadingRadians())));

// Elegimos la potencia de nuestro disparo en función de la energía


// que nos quede y de la distancia a la que se encuentra nuestro
// enemigo.
double firepower = Math.min(getEnergy(), 99 / e.getDistance() +
Math.random() * (double)2);
double bullet = 20 - (double)3 * firepower;

e_velocity = e_velocity * 0.98999999999999999 +


target.getVelocity() * 0.01;
double velocity = e_velocity * Math.random();

if(Math.random() > 0.5 || getOthers() > 1)


velocity = e.getVelocity();

if(getOthers() > 1 && Math.random() > 0.5)


velocity = 0.0;

//Enfocamos el arma y disparamos:


setTurnGunRightRadians(Math.asin(Math.sin((abs_orientacion -
getGunHeadingRadians()) + Math.asin((velocity / bullet) *
Math.sin(target.getHeadingRadians() - abs_orientacion)))));

setFire(firepower);
}

mover();
}

/*
* Movimientos para celebrar la victoria:
*/
public void bailecito(){
setTurnLeft(36000);
setTurnGunRight(36000);
setAhead(50);
setBack(50);
}

/*
* Método para que todos los ángulos que utilicemos estén entre -180 y 180
* grados.
*/
public double NormalRelativeAngle(double angle){

if (angle > -180 && angle <= 180)


return angle;
while (angle <= -180)
angle += 360;
while (angle > 180)
angle -= 360;
return angle;
}
}
ESTRATEGIA EN EQUIPO
Una de las opciones que ha incluido recientemente Robocode ha sido el juego
por equipos. En el modo de juego por equipos, mejor que diseñar un solo robot se
diseña un equipo completo de robots diferentes, donde, para cada equipo, se pueden
usar todas las clases java que sean necesarias o tener muchos robots instancia de la
misma clase.

Algunas de las opciones que ofrece el juego por equipos son:

Podemos designar a un robot como el líder del equipo(el primero que


añadamos al equipo). Los líderes tienen energía extra al comienzo de la
batalla, 200 unidades en total. Si el líder del equipo muere, todos los
miembros del equipo sufren un descenso de 30 unidades de energía cada
uno.
Los robots pertenecientes a un equipo tienen la capacidad de enviarse
mensajes unos a otros.

Estos robots pertenecientes a un equipo extenderán de la clase TeamRobot, que


a su vez es una subclase de la clase AdvancedRobot, pudiendo además implementar la
interfaz Droid.

Un Droid no tiene radar y por tanto no puede hacer un escaneo. Sin embargo,
todo Droid tiene 20 puntos extra de vida respecto a aquellos robots que no son Droid.

Para la implementación de nuestros Robots de equipo hemos elegido realizar


equipos de robots de la misma índole, es decir, el robot líder (que es designado
simplemente por ser la primera instancia que se incluye al equipo) y el resto de robots
de nuestro equipo pertenecerán a la misma clase Java. Hemos tomado esta decisión al
considerar que puede ser mejor que todos los robots tengan inteligencia propia (donde
los radares juegan un papel fundamental) a que sólo uno de ellos, el líder, escanee el
terreno y los otros se limiten a moverse y disparar.

De esta manera, cada uno de los robots será capaz de determinar donde están sus
enemigos (lo que servirá para moverse de manera inteligente), cuál es el enemigo más
débil y dónde están sus compañeros. A todo esto le añadiremos una cierta coordinación
entre los miembros del equipo que hará que su táctica de combate sea más eficaz.

En la implementación y búsqueda de buenos robots de equipo, hemos


implementado dos tipos de robots de combate en grupo, uno de ellos, IRCTeamBot es
una derivación del IRCBot, que ha sufrido algunas modificaciones para soportar el
modo de combate en grupo, y el otro Teamy, robot de equipo basado en una máquina de
estados. Pasamos a explicar el funcionamiento y modo de operación de estos dos robots:
IRCTeamBot:
Cuando nos enfrentamos al diseño de un robot de lucha y estrategia en grupo
debemos tener en cuenta dos tipos de acciones, las individuales y las colectivas. Dentro
de las individuales podemos considerar el movimiento del robot, la búsqueda de
enemigos o la potencia de disparo. Dentro de las colectivas tenemos que atender a cosas
tales como saber la posición de nuestros compañeros para no atacarles a ellos o saber
cuáles son los enemigos del grupo para ir acabando con ellos.

En el aspecto individual, al ser este robot, como hemos dicho, un derivado de


IRCBot, su comportamiento será similar al de aquel.

En el movimiento de IRCTeamBot (moving) intervienen varias cosas:

Tendremos que evitar confrontaciones directas con los enemigos.


Tendremos que evitar chocarnos contra los límites del escenario de combate,
evento que como sabemos también resta energía.
Tendremos que evitar que nos alcancen las balas enemigas.
Tendremos que evitar chocarnos contra robots de nuestro equipo, evento que
sería fatal para los intereses del grupo.

Para el control de todo este tipo de situaciones se llevan a cabo numerosas


acciones, que pasamos a detallar:

En primer lugar hacemos uso de una clase Evasor, que ya utilizábamos en


nuestro IRCBot y que sirve básicamente para que, dependiendo de la situación del
juego, escapemos de una forma o de otra de situaciones de peligro y consecuentemente
nuestro comportamiento sea menos predecible. Así, habrá asociado un objeto de este
tipo a cada robot del grupo, objeto que será de carácter estático, para así poder cambiar
el tipo de movimiento con el que nos zafamos del peligro, de manera que lo que
aprendan nuestros enemigos acerca de nuestro movimiento les servirá de más bien poco
en posteriores rondas.

El tipo de movimiento de huida de los peligros dependerá del valor en el que se


encuentre el atributo tipoEvasion del objeto evasor, que cambiará en situaciones tales
como cuando nos enfrentamos a un robot solo (de manera que este se vuelva un poco
loco al intentar darnos), cuando morimos(para movernos de forma distinta a la siguiente
ronda) o cuando chocamos con otro robot por ejemplo.

Otra parte importante en el control de nuestros movimientos es la tendencia


“anti gravitatoria” que seguimos, tomando como puntos de atracción a los enemigos.
Esta tendencia viene implementada en la función circleGrav() y está basada en que cada
vez que un robot va a moverse, si hay más de un enemigo, se calcula las distancias a
todos los enemigos, eligiendo el más cercano, y en función de su movimiento calcula
otro para que nos alejemos de la influencia de este enemigo más cercano.

En el caso de que hubiera solamente un enemigo, lo que hacemos es una


sucesión de movimientos distintos, que nos van acercando a dicho contrincante, y que
dada su naturaleza y continua variación, hacen de nuestro IRCTeamBot un gran robot
cuerpo a cuerpo(característica que ya tenía IRCBot).
En cuanto a la evasión del choque contra los muros, dentro de la función
mover() tenemos un fragmento de código dedicado a ello, en el que calculamos la
distancia a las paredes y si estamos demasiado cerca de alguna(menos de 70 píxeles) y
nuestro movimiento nos conduce a la colisión, daremos media vuelta.

En cuanto a la evasión de las balas enemigas, este robot no utiliza gran


sofisticación (como podría ser la empleada por nuestro IRCeitor, en el que se detectan
los cambios en la energía de los enemigos para determinar quién nos dispara y evitarlo),
simplemente cambia su modus operandi a la hora de moverse para que el robot enemigo
tenga más difícil la tarea de apuntar y darnos.

Para evitar el choque con miembros de nuestra cuadrilla hay dos fragmentos de
código dedicados a ello, el primero de ellos se encuentra en la función onScannedRobot,
en la que al detectar otro robot, comprobamos si es un compañero, en cuyo caso
calculamos la distancia a la que se encuentra de nosotros y si dicha distancia supone un
riesgo de colisión inminente giramos nuestro sentido de avance para evitar ese choque.
La segunda medida tomada al respecto se encuentra en el método onHitRobot, que se
ejecuta cuando se produce un onHitRobotEvent, es decir, cuando ya nos hemos chocado
contra otro robot. Esta situación puede producirse ya que el movimiento de un robot
está sujeto a varios factores y puede que estos lleven a una colisión antes de que
podamos detectar su posibilidad y evitarlo. En este caso, lo que se hace es generar un
movimiento aleatorio, de manera que lo más probable sea que cada robot, al calcularlo
por separado, lo haga de una manera distinta, y así al detectar los dos robots el choque
contra un compañero, los dos calculan por separado un movimiento aleatorio, que al
llevarlo a cabo lo más probable es que les aleje.

Como apunte, decir que en este método onHitRobot hay dos posibilidades, que
el robot contra el que chocamos sea amigo o sea enemigo, el primer caso ya lo hemos
explicado, mientras que en el segundo estamos ante una estupenda oportunidad de
disparar con todas nuestras fuerzas al enemigo, que al estar pegado a nosotros recibiría
un gran impacto. Mientras tanto nosotros también estamos perdiendo energía al estar en
contacto con él, pero al estarle disparando con la máxima potencia, recuperaremos parte
de esa energía y lo más probable es que aniquilemos a ese enemigo a costa de descender
un poco nuestra energía vital.

Pasemos a explicar cómo elegimos nuestros objetivos (targeting).

Al haber elegido la opción de implementar equipos en los que todos los robots
tienen bastante autonomía guardando cierta coordinación para ser más letales, cada uno
de los robots estará continuamente escaneando en busca de nuevos enemigos o en busca
de actualizar la información sobre los ya existentes.

Los robots enemigos se almacenarán en una estructura de tipo Hashtable, en


donde como sabemos se pueden añadir, acceder, modificar y borrar elementos de su
interior. Está estructura será de carácter estático para la clase IRCTeamBot, de manera
que todas las instancias de esta clase la compartirán, es decir, en todo momento todos
los robots de nuestro equipo considerarán los mismos enemigos.
Todos los robots, al detectar un robot ajeno (onScannedRobotEvent), lo que
harán será consultar si es compañero de escuadra o no. En caso de no pertenecer al
equipo, se consulta la tabla Hashtable, de nombre targets, para ver si dicho robot está
considerado como enemigo. Pueden ocurrir dos cosas:

- que esté registrado como enemigo anteriormente, en cuyo caso se actualizan


sus parámetros asociados, para que siempre haya una información
actualizada de los enemigos.
- que no se encuentre en targets, en cuyo caso se creará una nueva instancia de
la clase Enemigo y se introducirá en la tabla con los parámetros asociados
actuales.

Vemos como todos los robots del grupo contribuyen de esta manera a que la
tabla de enemigos esté siempre actualizada con las últimas posiciones y parámetros de
los mismos. Este comportamiento colectivo se traduce un control exhaustivo de los
enemigos para un ataque más eficaz.

De la misma manera, cuando muere un enemigo, dado que se ejecutará el código


presente en el método onRobotDeath, provocado por la aparición de un
RobotDeathEvent, se comprobará en este método si el robot fallecido es un enemigo(no
es un compañero) en cuyo caso lo eliminamos de la tabla de enemigos. Esta eliminación
hará que de aquí en adelante la búsqueda de enemigos para disparar sea más rápida, al
no tener que pararnos a comprobar si está vivo o no, simplemente no está.

Si el robot fallecido fuese un compañero lo que hacemos es decrementar el


entero que lleva la cuenta de robots en mi equipo, para así conocer aspectos del juego
durante el mismo que van en función del número de robots que queden en el
escenario(como por ejemplo determinar cuando nuestro equipo ha alcanzado la victoria
o incluso para funciones propias).

¿Cómo llevamos a cabo los disparos? (firing):

Cuando, al paso de nuestro scanner detectamos un robot enemigo, hay que


dispararle, para ello el cálculo del ángulo en el que tenemos que poner nuestro cañón y
la potencia del disparo son calculados de la misma forma que en IRCBot, es decir, el
ángulo de cañón lo calculamos a partir de la orientación, dirección de avance y
velocidad tanto enemigas como propias, y la potencia de disparo a partir de las energías
y las distancias.

Finalmente, pueden ocurrir dos cosas, o que el robot muera y termine muriendo
el equipo entero o que salgamos victoriosos. En el primero de los casos, se transmitirá
algo de información a la siguiente ronda, que en realidad es más bien un cambio
aleatorio en ciertos parámetros del movimiento para ser menos predecibles. En el
segundo de los casos se invocará nuestro método “bailecito()” en el que haremos una
serie de movimientos indicando nuestra alegría por la victoria.
Código de IRCTeamBot:
package ircPackage;
import robocode.*;

import java.awt.Color;
import java.util.*;
import java.io.*;

/**
* IRCTeamBot: Robot que extiende de TeamRobot y que por tanto está
* implementado con la intención de que pertenezca a un grupo de ataque de
* robots de la misma clase.
*/
public class IRCTeamBot extends TeamRobot{

static double e_velocity;


double energy;
int dir;
int numAmigosVivos;

//Hacemos el robot objetivo estático para que lo compartan todas las


instancias
static ScannedRobotEvent target;
Evasor evasor;
static Hashtable targets; //Tabla en la que almacenamos todos los
objetivos del grupo.
//Tambien la hacemos estatica para que los robots del grupo compartan
objetivos

double tiempoChoque;
double orientacionChoque;
double ultimaVezVisto;

public void run(){

inicializacion();
do{
if(getOthers() > numAmigosVivos){
setTurnRadarRight(360);
mover();
}
else
bailecito();

execute();
} while(true);
}

public IRCTeamBot(){

target = null;
targets = new Hashtable();

//Inicializamos un objeto de tipo Evasor, que nos gestionará la evasión


de las situaciones de peligro.
evasor = new Evasor();
dir = 1;
numAmigosVivos=3;
ultimaVezVisto = 0.0;
orientacionChoque = 0.0;
}
public void inicializacion(){

setColors(Color.yellow, Color.blue, Color.black);


setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
setTurnRadarRight(360);
}

/*
* En primer lugar hacemos un movimiento dependiendo del tipo de evasion
* en la que nos encontremos
* En segundo lugar tratamos de evitar los muros
* Y también atendemos al caso en el que estemos solos contra otro robot.
*/

public void mover(){

//No haremos nada hasta que fijemos un objetivo:


if(target==null)
return;

if(getOthers() > numAmigosVivos + 1){


setTurnRadarRight(360);
circleGrav();
}
else{
setMaxVelocity(Math.random() + 7);
setTurnRight(IRCTeamBot.target.getBearing() + 90 + -((double)dir *
evasor.ang));
}

//Distancias a los muros que limitan el escenario de combate, que los


tenemos que evitar ya que un choque contra estos nos resta energía vital:
double N = getBattleFieldHeight() - getY();
double S = getY();
double E = getBattleFieldWidth() - getX();
double W = getX();

//No nos vamos a chocar con los muros, ya que cuando estemos a menos
de una cierta distancia de estos daremos media vuelta.
if(N < 70 && getHeading() > 270 && getHeading() < 360)
dir = -1;
else
if(N < 70 && getHeading() > 0.0 && getHeading() < 90)
dir = -1;
else
//Aquí estamos en el caso de que estemos en zona próxima al muro
norte pero nuestro sentido de avance es tal que nos aleja de este peligro.
if(N < 70)
dir = 1;

if(S < 70 && getHeading() > 90 && getHeading() < 270)


dir = -1;
else
if(S < 70)
dir = 1;

if(E < 70 && getHeading() > 0.0 && getHeading() < 180)
dir = -1;
else
if(E < 70)
dir = 1;

if(W < 70 && getHeading() > 180 && getHeading() < 360)
dir = -1;
else
if(W < 70)
dir = 1;
//Si estamos en las zonas de peligro:
if(N < 70 || S < 70 || E < 70 || W < 70){
evasor.movetime = getTime();
evasor.sub_tipoEvasion = 1;
orientacionChoque = -dir * 10 - 90;
tiempoChoque = getTime();
}
else{
orientacionChoque = 0.0;
}

if(getOthers() > numAmigosVivos+1){


circleGrav();
}
else{
switch(Evasor.tipoEvasion){
case 1:
mov1();
break;
case 2:
mov2();
break;
case 3:
mov3();
break;
case 4:
mov1();
mov2();
mov3();
break;
default:
mov1();
}
}
}

/* Movimiento que seguimos cuando tenemos más de un robot acechándonos: */

public void circleGrav() {

double orientacion = 10000;


double closest = 10000;

for(Enumeration e = targets.elements(); e.hasMoreElements();){

Enemigo en = (Enemigo)e.nextElement();
//Calculamos nuestra orientacion de giro en funcion del enemigo
mas cercano:
if((en.estaVivo = true) && en.distancia < closest) {
closest = en.distancia;

orientacion = en.orientacion + 180 + (Math.random() * 60 - 30);

if(getOthers() < 4 + numAmigosVivos)


orientacion -= 90;
}
}

if(getOthers() >= 4 + numAmigosVivos)


orientacion += orientacionChoque;

if((double)getTime() - tiempoChoque > 10)


dir = 1;

setAhead(dir * 100);
//Metemos esa aleatoriedad en el giro porque sino miembros del mismo
equipo tienden a agruparse:
setTurnRight(NormalRelativeAngle(orientacion + Math.random()*20));
}

/* En este primer movimiento, si ya se ha acabado el movimiento


* anteriormente definido,
* cambiamos el sentido y angulo de nuestro movimiento, así como el tiempo
* que va a durar este.
*/
public void mov1(){

if((double)getTime() - evasor.movetime > evasor.time){


dir *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 25;
evasor.time = Math.random() * 7 + 15;
}
setAhead(dir * 50);
}

/* Definimos otro movimiento de manera completamente distinta:


* Utilizando incluso un subtipo del evasor. */

public void mov2(){

if(target.getDistance() < 400)


evasor.time = Math.min(target.getDistance() / 12, 24);
else
evasor.time = 28;

//Como poco el tiempo del siguiente movimiento será 12 ticks


evasor.time = Math.max(evasor.time, 12);

if((double)getTime() - evasor.movetime > evasor.time){


evasor.sub_tipoEvasion *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 20;
}

//A su vez dentro de este movimiento tenemos dos variantes: ir hacia


delante o hacia atrás
if(evasor.sub_tipoEvasion == 1)
setAhead(dir * 100);
else
setAhead(dir * -5);
}

public void mov3(){

evasor.ahead = (int)(Math.random() * 30 + 30);


evasor.ang = Math.random() * 15;
}

/* Al morir no lo hacemos en balde, segun el tipo de evasion que


* tuvieramos,
* generamos otra parala ronda siguiente, ¿No se trata de desconcertar?*/

public void onDeath(DeathEvent e){

switch(Evasor.tipoEvasion){
case 1:
Evasor.tipoEvasion = 2;
break;
case 2:
Evasor.tipoEvasion=(int)(Math.random()*3.9900000000000002) + 3;
break;
default:
Evasor.tipoEvasion = 1;
}
}

/* Si ha muerto un enemigo dejamos de considerarle, y si ha muerto un amigo


tambien hay que tenerlo en cuenta para saber cuando hemos ganado y cuando no*/

public void onRobotDeath(RobotDeathEvent e){

String nombreFallecido = e.getName();

if(!isTeammate(nombreFallecido)){
Enemigo en = (Enemigo)targets.get(nombreFallecido);
if(target.getName().equals(nombreFallecido))
ultimaVezVisto = 0.0;

targets.remove(nombreFallecido);
}
else
numAmigosVivos--;
}

/*
* Si chocamos con un robot de nuestro propio equipo nos movemos de
* inmediato con un movimiento aleatorio, de manera que si el otro
* también detecta que se ha chocado con nosotros se moverá, pero al ser
* aleatorio lo más probable es que lo haga de otra forma y así podamos
* separarnos.
*/

public void onHitRobot(HitRobotEvent e) {

if(isTeammate(e.getName())){
double tpRnd = Math.random() * 10;
int rndInt = (int) Math.ceil(tpRnd);
tpRnd = tpRnd % 3;
switch (rndInt) {
case 0: turnLeft(90-e.getBearing());
back(100);
break;
case 1: turnRight(90-e.getBearing());
ahead(100);
break;
case 2: turnLeft(90);
back(200);
}
}
else{
setTurnGunRightRadians(Math.asin(Math.sin(e.getBearingRadians() +
getHeadingRadians() - getGunHeadingRadians())));
setFire(3);//Si nos chocamos con un enemigo le freimos
}
}

/* En este método se implememtan aquellas acciones que hay que realizar


* cuando al paso del scanner se detecta la presencia de un robot, no
* detectado antes o sí */

public void onScannedRobot(ScannedRobotEvent e){

Enemigo en;
double abs_orientacion = e.getBearing() + getHeading();
double enemyX = getX() + e.getDistance() *
Math.sin(Math.toRadians(abs_orientacion));
double enemyY = getY() + e.getDistance() *
Math.cos(Math.toRadians(abs_orientacion));
double dx = enemyX - getX();
double dy = enemyY - getY();

if(!isTeammate(e.getName())){

//Vemos si el robot detectado ya lo habíamos visto antes o no


if(targets.containsKey(e.getName())){
en = (Enemigo)targets.get(e.getName());
}
else{
en = new Enemigo();
en.nombre = e.getName();
en.distancia = e.getDistance();
en.orientacion = e.getBearing();
en.estaVivo = true;
targets.put(e.getName(), en);
}
en.nombre = e.getName();
en.distancia = e.getDistance();
en.orientacion = e.getBearing();
en.estaVivo = true;

if(target==null || e.getName().equals(target.getName()) ||
e.getDistance() < target.getDistance() - 50 || (double)getTime() -
ultimaVezVisto > 8){

target = e;

ultimaVezVisto = getTime();
//comienza a contar de nuevo el tiempo desde el que vimos al
ultimo enemigo.
double theta = Math.toDegrees(Math.atan2(dx,dy));

setTurnRadarRight(NormalRelativeAngle(abs_orientacion -
getRadarHeading()));

double firepower = Math.min(getEnergy(), 99/e.getDistance() +


Math.random() * (double)2);
double bullet = 20 - (double)3 * firepower;
e_velocity = e_velocity * 0.98999999999999999 + e.getVelocity()
* 0.01;
double velocity = e_velocity * Math.random();

if(getOthers() > (numAmigosVivos+1))


velocity = e.getVelocity();

//Si el robot esta quieto le disparamos y punto:


if(e.getVelocity()==0)
setTurnGunRight(NormalRelativeAngle(theta -
getGunHeading()));
else
setTurnGunRight(NormalRelativeAngle(theta - getGunHeading())
+ Math.asin((velocity / bullet) * Math.sin(e.getHeadingRadians())));

setFire(firepower);
}
}//Fin del if isTeammate
else{
//Si nos estamos acercando mucho a un compañero cambiamos de
direccion:
double distanciaComp = e.getDistance();
if(distanciaComp<100){
setTurnLeft(90-e.getBearing());
}
//y vamos en busca de charlies
setTurnRadarRight(360);
}
mover();
}
public void bailecito(){

setTurnLeft(36000);
setTurnGunRight(36000);
setAhead(50);
setBack(50);
}

public double NormalRelativeAngle(double angle){

if (angle > -180 && angle <= 180)


return angle;
while (angle <= -180)
angle += 360;
while (angle > 180)
angle -= 360;
return angle;
}
}

Teamy:
En busca de un mejor robot de grupo también se ha implementado este otro
robot de simpático nombre. Su funcionamiento y manera de hacer las cosas es diferente
al anterior y éste no es ninguna derivación / adaptación de ningún otro robot.

El principal atractivo de este robot es su implementación a través de una sencilla


máquina de estados y su mecanismo de puntería y disparo, sobre todo ante enemigos
cuasi estáticos o de movimientos más lineales, algo que no es ni mucho menos una
minusvalía ya que multitud de robots se implementan para llevar a cabo un movimiento
de dicha índole.

Antes de pasar a explicar la máquina de estados y las acciones llevadas a cabo


en cada uno de ellos vamos a comentar algunos de los atributos de este robot:

En primer lugar vemos que, al igual que en el IRCTeamBot, contamos con una
Hashtable enemigos donde se almacenan los enemigos que los robots de nuestro equipo
van captando, también aquí es estática para que todos los miembros del grupo vean y
consideren a los mismos enemigos.

A continuación vemos enemigoActual, que contiene la estructura de tipo


Enemigo sobre la que estamos abriendo fuego, así como su nombre, nombreEn, que
almacenamos aparte para no generar accesos a null cuando ya se halla muerto y
queramos borrarlo de la tabla de enemigos.

Aquí también contamos con un objeto de tipo Evasor, pues dado el buen
resultado en el movimiento del anterior robot implementado hemos decidido que era
una buena opción mantener esta funcionalidad.

Vemos también una serie de constantes: GUN_TURNING_RATE, START_HIT_TIME,


TOL, MAX_ITERATIONS y BULLET_POWER, que servirán para los cálculos relativos
a disparar hacia la dirección y con la fuerza adecuadas.
Y como parte fundamental de esta implementación, encontramos la matriz de
double’s enemyParameters:

public double[] enemyParameters = new double[4];


//enemyParameters[0]: Distancia lineal a la que se encuentra nuestro enemigo
//enemyParameters[1]: Distancia angular a la que se encuentra nuestro enemigo
//enemyParameters[2]: Direccion de avance del enemigo(heading)
//enemyParameters[3]: Velocidad del enemigo

que nos servirá para afinar nuestros disparos, para calcular nuestros movimientos en
función de los enemigos...

La ejecución de nuestro robot será más sencilla y ordenada que en el caso del
robot anterior, ya que en cada iteración del método run() lo que se hará será
sencillamente invocar una función operación(), en la que dependiendo del estado de la
máquina de estados en el que nos encontremos invocará unas funciones u otras, ara una
vez hecho esto, invocar al método que se encarga de nuestro movimiento, todo ello
realizado de forma “paralela” debido a la utilización de métodos de tipo setXXX que
permiten la realización de múltiples tareas no bloqueantes las unas con las otras, todas
ejecutadas al llamar a la función execute(), hecho que se da nada más regresar de la
función operación() (método run()).

La máquina de estados sobre la que funciona este robot tiene cuatro estados:

INICIO:

Estado en el que comienza la ejecución del robot y al que no volverá hasta una
nueva llamada al método run() del robot. En este estado se determina el número de
compañeros que hay luchando a nuestro lado, se establece la velocidad del robot y se da
paso al segundo estado, el de búsqueda de objetivos.

*Previo paso a este estado se da la ejecución de un método inicio en el que se inicializan


algunas características de nuestro robot, más orientado a lo que sería un método
constructor.

TARGETING:

Dado que en este estado lo que se pretende es adquirir objetivos, lo que se hará
aquí será básicamente generar giros completos del radar, para que cuando se detecte
algún robot, el método onScannedRobot haga el resto.

En onScannedRobot lo que se hará será, al detectar un robot ajeno, comprobar


en primer lugar si pertenece a nuestro equipo, en cuyo caso gestionamos la evasión de
un choque contra él y en caso de ser un robot enemigo, recogeremos todos sus
parámetros en la matriz enemyParameters previamente mencionada, para después ver si
ya conocíamos a este enemigo de antes (se encuentra en la matriz enemigos). Si ya
sabíamos de su existencia actualizamos su información, y si no, lo añadimos a la tabla
de enemigos.
Además llegados a este punto, se comprueba si el robot tiene enemigo asignado
(enemigoActual != null). Si no tiene ninguno asignado, bien porque acabe de comenzar
la partida, bien porque el que tenía ya ha sido aniquilado, se le asigna el recientemente
escaneado y se pasa al estado de FIRING. En caso de tener ya enemigo, si es el que
hemos detectado lo actualizamos, y si no, no hacemos nada.

FIRING:

Este es el estado en el que se lleva el cálculo del ángulo con el que disparar, la
dirección y la fuerza del disparo, así como el disparo en sí.

Para ello tenemos implementado el método disparar(), en el que se lleva a cabo


todo eso que hemos dicho. En este método, con la inestimable ayuda de los datos
contenidos en la matriz enemyParameters y con otros que calculamos aquí a partir de
aquellos, teniendo en cuenta tanto nuestro movimiento y situación como el movimiento
y situación del enemigo, llegamos a un ángulo de giro de nuestro cañón y una potencia
de disparo, que al aplicarse deberían dar en el blanco.

Se distingue el caso en el que la velocidad del enemigo sea nula (puede que se
haya quedado atascado en una esquina, es carne de cañón) en cuyo caso apuntamos
hacia él directamente sin ningún tipo de cálculo previo, del caso en el que el enemigo se
está moviendo, caso en el que intervienen una serie de cálculos e iteraciones de
optimización de los parámetros implicados.

CELEBRATING:

Este es el estado al que se desea ganar en toda partida, aquel en el que todos los
enemigos han sido derrotados y hacemos nuestro baile de la victoria. A este estado se
llega cuando ha muerto un robot (RobotDeathEvent) y se ha comprobado que sólo
quedan robots aliados (en onRobotDeath).

Una vez ejecutada la máquina de estados, en nuestro método operacion()


invocamos el método mover(), que determinará el avance de nuestro robot. Este
movimiento es básicamente el mismo que el desarrollado por el IRCTeamBot, con su
tendencia anti-gravitacional, su evasión de los muros, y su movimiento especial en el
caso de la presencia de un solo robot enemigo para mejorar en el cuerpo a cuerpo.

Asimismo en Teamy también se ha implementado la prevención de choques con


compañeros, de la misma forma que en IRCTeamBot.

De entre los métodos que recogen eventos, la principal diferencia se encuentra


en el método onRobotDeath, en el cual, dada la máquina de estados implementada,
tendrá que devolver a nuestro robot a estado TARGETING cuando el robot fallecido sea
el enemigo que tuviese nuestro robot en ese momento, y llevar a nuestro robot al
siempre agradable estado de CELEBRATING cuando ya no queden más robots
contrincantes.
Hay una ligera variación en la forma de enfocar y disparar al robot enemigo en
el onHitRobot, y en onRobotDeath se sigue comunicando a la siguiente ronda una
variación aleatoria en el movimiento para que los enemigos no puedan aprender de
nosotros.

Código de Teamy:
package ircteam;
import robocode.*;
import java.util.*;
import java.awt.Color;

/*****************************************************************************
* Teamy:robot implementado por Jorge Carrasco y Elisabeth Pérez
*
*Este robot utiliza una máquina de estados y esta implementado para funcionar
*dentro de un equipo de robots de la misma indole.
****************************************************************************/

public class Teamy extends TeamRobot{

static Hashtable enemigos;//La tabla de enemigos va a ser comun a todos.


Enemigo enemigoActual;
String nombreEn;
Evasor evasor;
public int estadoActual;

public int numAmigosVivos;


public int dir;

double tiempoChoque;
double orientacionChoque;

//Constantes para controlar el disparo a objetivos moviles:


private static final double GUN_TURNING_RATE = 40.0/180.0*Math.PI;
private static final double START_HIT_TIME = 20;
private static final double TOL = 0.5;
private static final double MAX_ITERATIONS = 10;
private static final double BULLET_POWER = 1.5;

//Parametros para calcular el disparo certero:


public double[] enemyParameters = new double[4];

//enemyParameters[0]: Distancia lineal a la que se encuentra nuestro enemigo


//enemyParameters[1]:Distancia angular a la que se encuentra nuestro enemigo
//enemyParameters[2]: Direccion de avance del enemigo(heading)
//enemyParameters[3]: Velocidad del enemigo

//Para controlar los estados por los que pasa el robot.


public final int INICIO = 0;
public final int TARGETING = 1;
public final int FIRING = 2;
public final int CELEBRATING = 3;

public void run() {


inicio();
while(true) {
operacion();
execute();
}
}//Fin del metodo run
//Método inicio(), donde se inicializan los colores y las reglas de
movimiento de las partes integrantes del robot.

public void inicio(){

setColors(Color.red, Color.blue, Color.yellow);


setAdjustGunForRobotTurn(true);
setAdjustRadarForRobotTurn(true);
setAdjustRadarForGunTurn(true);
setTurnRadarRight(360);

dir = 1;
enemigos = new Hashtable();
evasor = new Evasor();
enemigoActual = null;
estadoActual = INICIO;
}

public void operacion(){

switch(estadoActual){
case INICIO:
if(getTeammates()==null) numAmigosVivos = 0;
else numAmigosVivos = getTeammates().length;
setMaxVelocity(Math.random() + 7);
estadoActual = TARGETING;
break;
case TARGETING:
setTurnRadarRight(360);
break;
case FIRING:
setTurnRadarRight(360);
disparar();
break;
case CELEBRATING:
bailecito();
return;
}
mover();
}

public void mover(){

if(enemigoActual==null){
estadoActual = TARGETING;
return;
}

if(getOthers() > numAmigosVivos + 1){


setTurnRadarRight(360);
circleGrav();
}
else{
setTurnRight(enemyParameters[1] + 90 -((double)dir * evasor.ang));
}

//Evitaremos chocarnos contra los muros:


double N = getBattleFieldHeight() - getY();
double S = getY();
double E = getBattleFieldWidth() - getX();
double W = getX();

if(N < 70 && getHeading() > 270 && getHeading() < 360)
dir = -1;
else
if(N < 70 && getHeading() > 0.0 && getHeading() < 90)
dir = -1;
else
//Aquí estamos en el caso de que estemos en zona próxima al muro
norte pero nuestro sentido de avance es tal que nos aleja de este peligro.
if(N < 70)
dir = 1;

if(S < 70 && getHeading() > 90 && getHeading() < 270)


dir = -1;
else
if(S < 70)
dir = 1;

if(E < 70 && getHeading() > 0.0 && getHeading() < 180)
dir = -1;
else
if(E < 70)
dir = 1;

if(W < 70 && getHeading() > 180 && getHeading() < 360)
dir = -1;
else
if(W < 70)
dir = 1;

//Si estamos en las zonas de peligro:


if(N < 70 || S < 70 || E < 70 || W < 70){
evasor.movetime = getTime();
evasor.sub_tipoEvasion = 1;
orientacionChoque = -dir * 10 - 90;
tiempoChoque = getTime();
}
else{
orientacionChoque = 0.0;
}

if(getOthers() > numAmigosVivos+1){


circleGrav();
}
else{
switch(Evasor.tipoEvasion){
case 1:
mov1();
Evasor.tipoEvasion++;
break;
case 2:
mov2();
Evasor.tipoEvasion++;
break;
case 3:
mov3();
Evasor.tipoEvasion++;
break;
case 4:
mov1();
mov2();
mov3();
Evasor.tipoEvasion++;
break;
default:
mov1();
Evasor.tipoEvasion++;
}
}
}
/* Movimiento que seguimos cuando tenemos más de un robot acechándonos: */

public void circleGrav() {

double orientacion = 10000;


double closest = 10000;
double distancia;
double orientacionEnemiga;
double alpha;

//Dependiendo de la posicion del enemigo mas cercano asi nos moveremos,


para no acercarnos peligrosamente a uno de ellos.
for(Enumeration e = enemigos.elements(); e.hasMoreElements();){

Enemigo en = (Enemigo)e.nextElement();
distancia = enemyParameters[0];
orientacionEnemiga = Math.toDegrees(enemyParameters[1]);

if(distancia < closest) {


closest = distancia;
//Al sumarle 180 lo que hacemos es alejarnos del enemigo
orientacion = orientacionEnemiga + 180 + (Math.random() * 60 -
30);
if(getOthers()<4+numAmigosVivos) orientacion-=90;
}
}
if(getOthers() >= 4 + numAmigosVivos)
orientacion += orientacionChoque;

if((double)getTime() - tiempoChoque > 10)


dir = 1;

setAhead(dir * 100);
setTurnRight(NormalRelativeAngle(orientacion)+ Math.random()*20);
}

/* En este primer movimiento, si ya se ha acabado el movimiento


* anteriormentedefinido, cambiamos el sentido y angulo de nuestro
* movimiento, así como el tiempo que va a durar este.
*/

public void mov1(){

if((double)getTime() - evasor.movetime > evasor.time){


dir *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 25;
evasor.time = Math.random() * 7 + 15;
}
setAhead(dir * 50);
}

/* Definimos otro movimiento de manera completamente distinta:


* Utilizando incluso un subtipo del evasor. */

public void mov2(){

if(enemyParameters[0] < 400)


evasor.time = Math.min(enemyParameters[0]/12, 24);
else
evasor.time = 28;

//Como poco el tiempo del siguiente movimiento será 12 ticks


evasor.time = Math.max(evasor.time, 12);

if((double)getTime() - evasor.movetime > evasor.time){


evasor.sub_tipoEvasion *= -1;
evasor.movetime = getTime();
evasor.ang = Math.random() * 20;
}

//A su vez dentro de este movimiento tenemos dos variantes: ir hacia


delante o hacia atrás
if(evasor.sub_tipoEvasion == 1)
setAhead(dir * 100);
else
setAhead(dir * -5);
}

public void mov3(){

evasor.ahead = (int)(Math.random() * 30 + 30);


evasor.ang = Math.random() * 15;
}

public void disparar(){

double[] posicion = enemigoActual.getPosicion();


double x,y,fire_length,fire_time;
double firepower = Math.min(getEnergy(), 99/enemyParameters[0] +
Math.random()*(double)2);

double bulletSpeed = 70.0 - 3.0*firepower;


// La velocidad del cañon se recalcula cada vez. Es innecesario pero
nos permitirá modificarlo para variar la potencia dle cañón si queremos

double ett = amountToTurn(enemyParameters[1])/GUN_TURNING_RATE;


// Tiempo estimado de giro

double hit_time = START_HIT_TIME;


double actual_time = -1.0;
double fire_angle;
int count = 0;

if(enemyParameters[3]==0){
//Calculamos el angulo a girar para apuntar al enemigo
double theta = Math.toDegrees(Math.atan2(posicion[0]-
getX(),posicion[1]-getY()));
//Apuntamos
setTurnGunRight(NormalRelativeAngle(theta - getGunHeading()));
}
else{
do {
if (count != 0)
hit_time = actual_time;

count++;
x = enemyParameters[0] * Math.sin(enemyParameters[1]);
y = enemyParameters[0] * Math.cos(enemyParameters[1]);

//Tomando el movimiento del enemigo en cuenta:


x+=hit_time*enemyParameters[3] * Math.sin(enemyParameters[2]);
y+=hit_time*enemyParameters[3] * Math.cos(enemyParameters[2]);

//Tomando nuestro movimiento en cuenta:


y -= ett * this.getVelocity()/2;

fire_length = Math.sqrt((x * x) + (y * y)); //Distancia


fire_time = fire_length / bulletSpeed;
fire_angle = Math.atan(x / y);
if (y < 0)
fire_angle += Math.PI;
ett = amountToTurn(fire_angle)/GUN_TURNING_RATE;
actual_time = fire_time + ett;
}
while (Math.abs(actual_time - hit_time) > TOL && count <
MAX_ITERATIONS);
setTurnGunRightRadians(amountToTurn(fire_angle));

setFire(firepower);
}

/*********** METODOS PARA CONTROLAR LOS EVENTOS ROBOCODE ***************/

public void onScannedRobot(ScannedRobotEvent e) {

String nombreDetectado = e.getName();


Enemigo enemigoDetectado;

double X = getX() + e.getDistance() *


Math.sin(Math.toRadians(getHeading()+e.getBearing()));
double Y = getY() + e.getDistance() *
Math.cos(Math.toRadians(getHeading()+e.getBearing()));

if(isTeammate(nombreDetectado)){

double distanciaComp = calcularDistancia(getX(),getY(),X,Y);


if(distanciaComp<100){
setTurnLeft(90-e.getBearing());
}
//y vamos en busca de charlies
setTurnRadarRight(360);
}
else{
enemyParameters[0]=e.getDistance(); // Distancia al enemigo
enemyParameters[1]=e.getBearingRadians();//Angulo hasta el enemigo
enemyParameters[2]=e.getHeadingRadians()-this.getHeadingRadians();
// Direccion del enemigo
enemyParameters[3]=e.getVelocity(); // Velocidad del enemigo

if(enemigos.containsKey(nombreDetectado)){
enemigoDetectado = (Enemigo)enemigos.get(nombreDetectado);
enemigoDetectado.posX = X;
enemigoDetectado.posY = Y;
enemigoDetectado.energia = e.getEnergy();
}
else{
enemigoDetectado=new Enemigo(nombreDetectado,X,Y,e.getEnergy());

}
enemigos.put(nombreDetectado, enemigoDetectado);

//Con esto asignamos enemigo a nuestro robot, priorizando ir a por el


lider del equipo contrario:
if(enemigoActual == null){
enemigoActual = enemigoDetectado;
nombreEn = nombreDetectado;
estadoActual = FIRING;
}
else{
//De la siguiente manera actualizamos la posicion almacenada de
nuestro enemigo:
if((estadoActual== FIRING)&&(nombreEn.equals(nombreDetectado)))
enemigoActual = (Enemigo)enemigos.get(nombreDetectado);
}
}
}
public void onRobotDeath(RobotDeathEvent e){

String nombreFallecido = e.getName();


if(!isTeammate(nombreFallecido)){
Enemigo en = (Enemigo)enemigos.get(nombreFallecido);
if(nombreEn.equals(nombreFallecido)){
enemigoActual = null;
nombreEn = "";
estadoActual = TARGETING;
}
enemigos.remove(nombreFallecido);
}
else
numAmigosVivos--;

if(getOthers()-numAmigosVivos == 0){
estadoActual = CELEBRATING;
operacion();
}
}

/**
* onHitByBullet: Turn perpendicular to the bullet
*/

public void onHitByBullet(HitByBulletEvent e) {

// Evasive action
setTurnLeft(90 - e.getBearing());
Evasor.tipoEvasion++;
}

//Aqui meteremos lo que queramos guardar para la ronda que viene:

public void onDeath(DeathEvent e){

switch(Evasor.tipoEvasion){
case 1:
Evasor.tipoEvasion = 2;
break;
case 2:
Evasor.tipoEvasion =(int)(Math.random()*3.9900000000000002)+ 3;
break;
default:
Evasor.tipoEvasion = 1;
}
}

public void onHitRobot(HitRobotEvent e) {

if(isTeammate(e.getName())){
double tpRnd = Math.random() * 10;
int rndInt = (int) Math.ceil(tpRnd);
tpRnd = tpRnd % 3;
switch (rndInt) {
case 0: turnLeft(90-e.getBearing());
back(100);
break;
case 1: turnRight(90-e.getBearing());
ahead(100);
break;
case 2: turnLeft(90);
back(200);
}
}
else{
double bearingFromGun=
NormalRelativeAngle(getHeading()+e.getBearing()-getGunHeading());
turnGunRight(bearingFromGun);
setFire(Math.min(3-Math.abs(bearingFromGun),getEnergy()-.1));
//Si nos chocamos con un enemigo le freimos
}
}

/*************** METODOS AUXILIARES ******************/

public void bailecito(){

setTurnLeft(36000);
setTurnGunRight(36000);
setAhead(50);
setBack(50);
}

public double calcularDistancia(double x1,double y1,double x2,double y2)


{

return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
}

private double amountToTurn(double heading) {

double res = heading + getHeadingRadians() - getGunHeadingRadians();


while (res > Math.PI) res -= 2 * Math.PI;
while (res < -Math.PI) res += 2 * Math.PI;
return res;
}

public double NormalRelativeAngle(double angle){

if (angle > -180 && angle <= 180)


return angle;
while (angle <= -180)
angle += 360;
while (angle > 180)
angle -= 360;
return angle;
}

}
RESULTADOS:
Como comentario final decir que hemos puesto a luchar a los dos equipos para
evaluar sus prestaciones uno contra el otro y estos han sido los resultados
proporcionados por robocode:

De estos resultados podemos sacar la conclusión de la superioridad del equipo


formado por robots IRCTeamBot contra el formado por Teamy’s, pero también nos
gustaría decir que estos resultados también dependen mucho de los contrincantes, ya
que unos equipos están mejor preparados por sus estrategias utilizadas para derrotar a
equipos con ciertas características. En este sentido el ejemplo lo obtuvimos al
comprobar que, a pesar de ganar los dos equipos, si los poníamos a luchar por separado
contra el equipo de ejemplo que suministra el paquete Robocode, el equipo de Teamy´s
sufría muchas menos bajas a la larga que el equipo de IRCTeamBot’s.

REFERENCIAS:

Guía de aprendizaje robocode:


http://www.ug.cs.usyd.edu.au/~csled/biwf/robocode/guide.html

Para el juego de equipo:


http://www-106.ibm.com/developerworks/java/library/j-robocode2/?loc=j

CS 3230 Robocode project:


http://www.codepoet.org/~markw/weber/java/robocode/

RoboHome
http://robowiki.net/cgi-bin/robowiki

Robocode FAQ :
http://robocode.alphaworks.ibm.com/help/robocode.faq.txt

Robocode API
http://robocode.alphaworks.ibm.com/docs/robocode/index.html

También podría gustarte