Prolog 2
Prolog 2
Prolog 2
ROBOCODE
INDICE
Pag.
Introduccin ..
El Campo de juego y los robots . 4 Estrategia. 6 Como movernos?.................................................................... . 6 Cuando, como y con que potencia disparar?........................... 7 Como localizar a nuestro enemigo?.......................................... 8 Cdigo 9 Conclusiones16 Bicliografia. 17
-2-
Introduccin
Robocode es un simulador de combate donde tanques programados en Java luchan en un escenario preparado para ello, siendo el vencedor aquel que quede vivo. Este proyecto fue creado por Mathew Nelson, ingeniero de IBM, en un intento de convencer a la comunidad de que Java est preparado para ser utilizado en juegos y adems, ser una metodologa adictiva de aprendizaje del lenguaje. Se trata de un juego en el que se puede aprender dicho lenguaje y el manejo de los eventos de java programando tu propio robot mediante el propio lenguaje y mtodos de java. Ayudndote de los mtodos que se te proporcionan en el API especfico para Robocode, el cual te permite ejecutar el programa y tu robot en cualquier sistema o PC, puedes implementar o sobrescribir mtodos de dicho API para conseguir un robot ms eficiente. Este API, parte de la clase Robot, Bullet, Condition y Event y se apoya en algunas otras clases de java como java.io.OutputStream y java.io.Writer. Adems, el API cuenta con una interfaz llamada Droid. En Robocode el programador debe de encargarse de elegir la mejor estrategia para su robot e implementarla mediante desplazamientos, giros, controlando el radar y can del robot as como hacindole disparar cuando considere apropiado. Tambin tiene que controlar los eventos que se producen mientras combate (impacto contra un muro, de una bala.) Todas las batallas entre robots constan de uno o varios combates, en los cuales los robots parten de una posicin inicial aleatoria y tienen que luchar entre ellos tanto individualmente como por equipos. El objetivo principal del juego es obtener ms puntos que los robots contrarios al final de la batalla, par ello se debe destruir al robot contrincante aunque tambin se pueden obtener puntos por diversas actuaciones. Hay dos tipos de combates: los combates individuales, y los combates en equipos, en el cual, un conjunto de robot luchan contra otro para conseguir la destruccin del equipo entero contrario. Dentro de los combates individuales, hay dos modalidades, una consistente en un todos contra todos en el cual solo debe quedar uno o los combates one2one, es decir uno contra otro, en los cuales combaten nicamente dos robots. Nosotros nos centraremos en este tipo de combates.
-3-
Como se ha comentado anteriormente, al principio del combate, todas las partes de las que se compone el tanque estn alineadas y forman una unidad, es decir, cuando un tanque
-4-
gira, el can y el radar harn el mismo movimiento a no ser que se le indique lo contrario. Esto viene del hecho de que el can est montado sobe el cuerpo y el radar sobre el can. Debido a esto, si giro el chasis de nuestro tanque para en una direccin en concreto, tanto el can como el radar, giraran tambin hacia esa direccin en concreto. Si giro el can, el cuerpo no se mueve, pero si lo hace el radar. Y por ltimo, si muevo el radar, no se mueve nada ms.
-5-
Estrategia
A la hora de elegir una estrategia en concreto para nuestro robot, se vio que tenamos que concretar estrategias para diversos puntos, en concreto: Como movernos Cuando, como y con que potencia disparar. Como localizar a nuestro enemigo
Tras consultar diversas paginas y tutoriales de Internet y realizar simulaciones de los robots bsicos que se adjuntaban con el Robocode, decidimos que la mejor estrategia podra ser seguir la pared al igual que hacia el tanque walls, adems, se podran realizar diversas mejoras para que nuestro robot tardar menos en escasear, disparara mas eficientemente y esquivara las balas del enemigo. Por todo ello, se opto por una estrategia de evasin, ya que al estar pegado a la pared, si se realizaran los movimientos correctamente, podramos conseguir que nuestro enemigo fuera perdiendo energa poco a poco debido a que no nos acertara en los disparos. Centrndonos en cada una de las estrategias seguidas para cada uno de los tres puntos enunciados anteriormente, tenemos:
Como movernos?
Para elegir esta opcin, lo primero que tuvimos que hacer es consultar que mtodos del API de Robocode nos ayudaran a realizar los movimientos y por donde moveramos a nuestro robot: Para que tanto el radar, como el can se muevan independientemente del chasis del tanque, se necesitan: void setAdjustGunForRobotTurn(boolean newAdjustGunForRobotTurn) sirve para tener un movimiento del can independiente del cuerpo void setAdjustRadarForGunTurn(boolean newAdjustRadarForGunTurn) sirve para tener un movimiento del radar independiente del can. void setAdjustRadarForRobotTurn(boolean newAdjustRadarForRobotTurn) sirve para tener un movimiento del radar independiente del robot.
Para obtener las dimensiones del campo se pueden utilizar los siguientes mtodos: double getBattleFieldHeight() nos indica la altura mxima del campo de batalla. double getBattleFieldWidth() nos indica la anchura mxima del campo de batalla. Sabiendo que el campo de batalla, tiene el origen de coordenadas en la esquina inferior izquierda, durante cualquier instante del combate, se puede obtener la posicin en el campo de batalla del robot mediante:
double getX() nos indica la posicin en la coordenada X de nuestro robot. double getY() nos indica la posicin en la ordenada Y de nuestro robot. Para mover en si el tanque, estn los siguientes mtodos: void setAhead(double distance) sirve para mover el robot hacia adelante void setBack(double distance) sirve para mover el robot hacia atrs void turnLeft(double degrees) sirve para girar a la izquierda void turnRight(double degrees) sirve para girar a la derecha de igual modo, existen mtodos para mover tanto el radar como el escner: void setTurnGunLeft(double degrees) void setTurnGunRight(double degrees) void setTurnRadarLeft(double degrees) void setTurnRadarRight(double degrees) con todos estos mtodos, podemos conseguir que nuestro robot mueva independientemente el escner y el can del chasis, y por tanto, podemos ir moviendo el tanque siguiendo las paredes mientras que escaneamos y disparamos independientemente de nuestro movimiento. De esta manera, si nos movemos mucho y deprisa, sin que el movimiento sea predecible por los robots enemigos, ellos iran gastando poco a poco su energa disparndonos. Con lo que habremos implementado nuestra estrategia de evasin. El principal problema de esto, es que al movernos tan cerca de la pared, tenemos que controlar constantemente que no se choca con ella ya que estaramos perdiendo energa intilmente. Y de igual modo, tenemos que tener cuidado con el robot que est cercano a la pared, o como nosotros la siguen para que no colisionemos, y en caso de que lo hagamos, salgamos beneficiados de ello.
distancia 0 - 50 50 - 100 100 - 150 150 200 200 250 250 300
En resumen, solo dispararamos al contrincante si tiene nuestra misma energa o menos y se encuentra cerca de nosotros, sino, preferimos esquivarle para que siga perdiendo energa disparndonos.
Cdigo
package dmm; import robocode.*; import java.awt.Color; /** * Definitivo by dmm y pa */ public class Definitivo extends AdvancedRobot { double movimiento; // cuanto nos movemos double movMaxHor; //movimiento maximo horizontal double movMaxVer; //movimiento maximo vertical double potencia; // potencia de disparo //variables de estado para saber el movimiento siguiendo las paredes int pared = 2; // variable final para tener el valor de 180 final double PI = Math.PI; Enemigo target;// referencia a un objeto de la clase enemigo // varibles que nos ayudan a saber en que sentido va el escaner y el robot int direction = 1; int sentidoEscaner = 1; /** * run: Mtodo principal de nuestro Robot */ public void run() { //Coloreamos nuestro robot setColors(Color.black,Color.black,Color.yellow); //inicializamos variables del entorno de combate y nos creamos el enemigo target = new Enemigo(); target.distance = 100000; movMaxHor = getBattleFieldWidth(); movMaxVer = getBattleFieldHeight();
//Iniciamos movimiento hacia la pared superior y giramos para empezar //el movimiento evasivo turnLeft(getHeading()); movimiento = movMaxVer - getY() -25; ahead(movimiento); turnRight(90);
//activamos las funciones del caon y radar para que sean independientes setAdjustGunForRobotTurn(true); setAdjustRadarForGunTurn(true); setAdjustRadarForRobotTurn(true);
//Bucle principal de funcionamiento while (true) { //nos movemos y actualizamos la pared en la que estamos pared = mover(); // escaneamos en busca del objetivo, y si cumple las especificaciones de // cercania disparamos escanear(); calcularTiro(); if(calcularPotencia()) fire(potencia); //Disparamos execute(); } }
/** * mover: Metodo que define el movimiento general de nuestro Robot */ public int mover(){ // varible que contiene el movimiento maximo que puede realizar el robot double movimientoMax = 100000; // variable local que nos indica en que pared estamos int isInWall; isInWall = pared; // el movimiento esta comprendido entre 300 y 0, siendo mas o menos aleatorio movimiento = Math.random()*300; //si nos ha salido un movimiento muy pequeo, lo cambiamos if (movimiento < 50) movimiento = 50; switch (isInWall){ //actualizamos el movimiento maximo en funcion de la pared en la que //estemos y la situacion del robot actual case 1: movimientoMax = movMaxVer - getY() - 25; break; case 2: movimientoMax = movMaxHor - getX() - 25; break; case 3: movimientoMax = getY() - 25; break; case 4: movimientoMax = getX() - 25; break; default: movimientoMax = 25; pared = 4; } // si el movimiento es justo para llegar al fin de esa pared, giramos, sino simplemente nos movemos hacia adelante
10
if (movimiento > movimientoMax &&( pared == 1 || pared == 2 || pared == 3 || pared == 4 )) { if (pared == 4 && movimientoMax == 25){isInWall = 4;} else {isInWall = ++pared %4; movimiento = movimientoMax; ahead(direction*movimiento); turnRight(90); } } else {setAhead(movimiento);} return isInWall; } /** * onHitRobot: Si chocamos con un adversario, retrocedemos o avanzamos segun *donde este y cambiamos el sentido de la marcha. */ public void onHitRobot(HitRobotEvent e) { direction = direction*-1; // Si esta delante nuestra, echamos para atrs if (e.getBearing() > -90 && e.getBearing() < 90) setBack(100); // Si est detrs nos desplazamos en sentido contrario else setAhead(100); } /** * calcularPotencia(): Calculamos la potencia optima basado en la distancia al objetivo */ boolean calcularPotencia() { /*variables auxiliares donde se almacenara la distancia al enemigo y los valores maximos y minimos que acotan cada potencia de disparo*/ double distancia, min, max; distancia = target.distance; /*si la distancia esta fuera del rango asignado, no se dispara, con lo que se pone el poder de disparo a 0 y se devuelve false*/ if (distancia > 300 || distancia < 0) potencia = 0; else{ min = 250; max = 300; potencia = 3;
11
// calculamos la potencia de tiro en funcion de la distancia a la que este while(!(distancia > min && distancia < max)){ potencia = potencia -0.5; min= min -50; max = max -50; if (distancia > 300 || distancia < 0){potencia = 0; break;} } // si esta en el rango, devolvemos true, con lo que se disparara if (potencia != 0) return true; } // en caso de no devolver true, es porque no esta en el rango, con lo que no se disparara. return false; } /** * escanear(): Realizamos un scanner */ void escanear() { double radarOffset; //Si hace tiempo que no localizamos a nadie if (getTime() - target.ctime > 4) { radarOffset = 360;//lo rotamos 360 } else { //Calculamos el giro necesario del radar para seguir al objetivo radarOffset = getRadarHeadingRadians() absbearing(getX(),getY(),target.x,target.y); //Calculamos el offset debido al seguimiento del objetivo //para no perderlo if (radarOffset < 0) radarOffset -= PI/8; else radarOffset += PI/8; } //giramos el radar setTurnRadarLeftRadians(NormaliseBearing(radarOffset)); } /** * calcularTiro(): Realizamos los clculos de balstica */ void calcularTiro() {
12
// Primera estimacin: calculamos el tiempo que tardara en llegar el proyectil a la posicion actual del objetivo long time = getTime() + (int)(target.distance/(20-(3*potencia))); // calculamos el offset de giro segn una estimacin de movimiento lineal double gunOffset = getGunHeadingRadians() absbearing(getX(),getY(),target.guessX(time),target.guessY(time)); //giramos el caon setTurnGunLeftRadians(NormaliseBearing(gunOffset)); }
//ajustamos el ngulo si no se encuentra de -pi a pi double NormaliseBearing(double ang) { if (ang > PI) ang -= 2*PI; if (ang < -PI) ang += 2*PI; return ang; } //Si no se encuentra de 0 a 2pi lo modificamos para que sea el ngulo ms corto double NormaliseHeading(double ang) { if (ang > 2*PI) ang -= 2*PI; if (ang < 0) ang += 2*PI; return ang; } //Calcula la distancia entre dos puntos x e y public double getrange( double x1,double y1, double x2,double y2 ) { double xo = x2-x1; double yo = y2-y1; double h = Math.sqrt( xo*xo + yo*yo ); return h; } //Calcula el ngulo entre dos puntos public double absbearing( double x1,double y1, double x2,double y2 ) { double xo = x2-x1; double yo = y2-y1; double h = getrange( x1,y1, x2,y2 ); if( xo > 0 && yo > 0 ) { return Math.asin( xo / h ); } if( xo > 0 && yo < 0 )
13
{ return Math.PI - Math.asin( xo / h ); } if( xo < 0 && yo < 0 ) { return Math.PI + Math.asin( -xo / h ); } if( xo < 0 && yo > 0 ) { return 2.0*Math.PI - Math.asin( -xo / h ); } return 0; }
/** * onScannedRobot: Mtodo que se ejecuta cuando el scanner encuentra un robot */ public void onScannedRobot(ScannedRobotEvent e) { //Si encontramos un robot ms cercano al objetivo actual if ((e.getDistance() < target.distance)||(target.name == e.getName())) { //Calcular apuntamiento double absbearing_rad = (getHeadingRadians()+e.getBearingRadians())%(2*PI); //actualizamos las variables del nuevo objetivo target.name = e.getName(); target.x = getX()+Math.sin(absbearing_rad)*e.getDistance(); target.y = getY()+Math.cos(absbearing_rad)*e.getDistance(); target.bearing = e.getBearingRadians(); target.head = e.getHeadingRadians(); target.ctime = getTime(); target.speed = e.getVelocity(); target.distance = e.getDistance(); } }
/** * onRobotDeath: Mtodo que corre cuando muere un adversario */ public void onRobotDeath(RobotDeathEvent e) { if (e.getName() == target.name) target.distance = 10000; //actualizamos la variable de distancia } }
14
/* * Clase Enemigo. La utilizamos para almacenar la informacin acerca de nuestros adversarios * Posicin, velocidad, disparos, etc */ class Enemigo { String name; public double bearing; public double head; public long ctime; public double speed; public double x,y; public double distance; //metodo que intenta adivinar la coordenada X del enemigo en function de los datos que tenemos de el public double guessX(long when) { long diff = when - ctime; return x+Math.sin(head)*speed*diff; }
//metodo que intenta adivinar la coordenada Y del enemigo en function de los datos que tenemos de el public double guessY(long when) { long diff = when - ctime; return y+Math.cos(head)*speed*diff; } }
15
Conclusiones
La primera de las conclusiones es que ha sido extremadamente difcil depurar el cdigo ya que no contbamos con ninguna manera de poder mostrar mensajes por pantalla con lo que nunca hemos tenido a ciencia cierta si el robot hacia lo que nosotros pensbamos o no. Esto se ha debido a que los mtodos del paquete java.io que tenamos en el API de Robocode daban errores de compilacin. Este problema ha sido determinante en la construccin del robot ya que no hemos podido implementar muchas de las mejoras y estrategias que tenamos pensadas en el robot, teniendo que simplificar el cdigo con lo que nuestro robot se ha hecho mucho ms vulnerable. El comportamiento, aun as del robot ha sido satisfactorio en las pruebas realizadas con los robots de ejemplo suministrados con Robocode con los cuales siempre se ha obtenido la victoria para cada uno de ellos.
el siguiente ejemplo muestra otra batalla con otro de los robots de muestra, se ha probado con todos los robots tanto en 10 como en 5 asaltos y siempre se ha obtenido la victoria.
No se ha podido implementar totalmente la estrategia de disparo ni de movimiento por el motivo anteriormente mencionado, pero ajustando el disparo al un valor por defecto, tambin se han conseguido buenos resultados con los robots de prueba. La experiencia ha sido pese a todo, entretenida y muy divertida.
16
Bibliografa
http://www.it.uc3m.es/jvillena/irc/ http://www.robocode.ie/index.html http://robocode.sourceforge.net/ http://www.codepoet.org/~markw/robocode/ http://www.ibm.com
17