Prolog 2

October 13, 2022 | Author: Anonymous | Category: N/A
Share Embed Donate


Short Description

Download Prolog 2...

Description

 

 

Inteligenc Inteli gencia ia e en n Re Redes des d de e Comunicaciones

ROBOCODE

David de Miguel Medina 100029556 Pabl Pa blo o A ndreu nd reu barasoain baraso ain 1000 100027 2745 455 5

 

 INDICE Pag.

 Introducción ……………………… ………………………………………… ………………………………….. ………………..

3

 El Campo de juego y los robots …………………………… ………………………………………. …………. 4  Estrategia……………………………  Estrategia………… …………………………………… …………………………………. ………………. 6 ¿Como movernos?.................................................................... movernos?.................................................................... . 6 ¿Cuando, como y con que potencia disparar?....................... disparar?........................... .... 7

¿Como localizar a nuestro enemigo?.......................................... enemigo?.......................................... 8 Código…………………………………………………………………… 9 Conclusiones……………………………………………………………16  Bicliografia………………………  Bicliografia…… …………………………………… …………………………………… ………………….. 17 

 

-2-

 

 Introducción 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 además, ser una metodología 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 métodos de java. Ayudándot Ayudándotee de los métodos que se te proporcionan en el API específico para Robocode, el cual te permite ejecutar el  programa y tu robot en cualquier sistema o PC, puedes implementar o sobrescribir métodos de dicho API para conseguir un robot más eficiente. Este API, parte de la clase “Robot”, “Bullet”, “Condition” “Condition” y ““Event” Event” y ssee apoya en algunas otras clases de java com como o “java.io.OutputStream” y “java.io.Writer”. Además, 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 implementarla mediante desplazamientos, ggiros, iros, control controlando ando el radar y cañón del robot así como haciéndole disparar cuando considere apropiado. También 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 posición inicial aleatoria y tienen que luchar entre ellos tanto individualmente como por equipos. El objetivo principal del juego es obtener más puntos que los robots contrarios al final de la batalla, par ello se debe destruir al robot contrincante aunque también 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 destrucción del equipo entero contrario. contrario. Den Dentro tro de los com combates bates indi individuales, viduales, hhay ay 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-

 

 El Campo de juego y los robots Los combates se realizan en superficies rectangulares que oscilan entre las dimensiones de 400x400 hasta 5000x5000 y por las cuales el robot tiene total libertad para moverse y localizar a los robots contrarios. Para ello, el robot consta de tres partes básicas diferenciadas: el propio “chasis” del robot, el escáner y el cañón, los cuales se pueden mover conjunta o separadamente en función de la estrategia seguida por el jugador. Todos los movimientos que se le ordenen al tanque, deberán ir encaminados a mover el chasis, mientras que con el escáner, este podrá localizar a los enemigos para apuntarlos con el cañón, con el cual disparara. Los robots heredan de la clase “Robot” por defecto, aunque también pueden heredar de “AdvancedRobot” o “TeamRobot”. Las principales diferencias son que la clase “Robot” dispone únicamente de los métodos básicos para la implementación del tanque. “AdvancedRobot” hereda a su vez de la clase Robot y permite, además de todas las funciones básicas, otras más avanzadas que nos facilitaran el desarrollo de nuestro robot, así como el acceso a métodos mas eficientes y variados para controlarlo. Por último la clase ‘TeamRobot’ hereda de ‘AdvancedRobot’ y permite el intercambio de mensajes entre robots de un mismo equipo. Los robots que heredan de esta clase, suelen ser aquellos que compiten por equipos y que necesitan un paso de mensajes par comunicarse entre ellos. Cualquiera puede crear nuevas subclases de Robot y añadir nuevas funcionalidades que pueden ser usadas para construir robots. Como se acaba de explicar, Robocode  proporcionaa una subclase a “Robot”: “AdvancedRobot”, que permite realizar las llamadas  proporcion 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). Esto nos vendrá muy bien ya que podremos realizar diversos movimientos a la vez, decidir qué hacer en cada turno y tener acceso a la creación y gestión de eventos creados por nosotros mismos. Al principio del combate, combate, todos los robot parten de una energía inicial de de 100, dicha energía se ira reduciendo en función de los impactos que reciba así como de los disparos  perdidos que realice, de igual modo, si el robot consigue que sus balas impacten en el contrincante, su energía aumentara en la misma proporción que la del enemigo disminuya. También disminuyen la energía el hecho de impactar contra un muro del terreno de juego o contra otro robot. Cuando su energía sea cero, el robot entrara en un estado llamado disable, en el cual permanece quieto y no puede realizar ninguna acción, siendo destruido en el caso de que alguna bala impacte contra el. Debido a esto ultimo, se pueden implementar muchas estrategias, pero todas estarán  basadas en acertar los máximos disparos en el tanque enemigo mientras que nuestro robot no reciba ningún disparo ni choque contra las paredes o enemigos. Como se ha comentado anteriormente, al principio del combate, todas las partes de las que se compone el tanque están alineadas y forman una unidad, es decir, cuando un tanque

 

-4-

 

gira, el cañón y el radar harán el mismo movimiento a no ser que se le indique lo contrario. Esto viene del hecho de que el cañón está montado sobe el cuerpo y el radar sobre el cañón. Debido a esto, si giro el chasis de nuestr nuestroo tanque para en una dirección en concreto, concreto, tanto el cañón como el radar, giraran también hacia esa dirección en concreto. Si giro el cañón, el cuerpo no se mueve, pero si lo hace el radar. Y por último, si muevo el radar, no se mueve nada más.

 

-5-

 

 Estrategia A la hora de elegir una estrategia en concreto para nuestro robot, se vio que teníamos 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 básicos que se adjuntaban con el Robocode, decidimos que la mejor estrategia  podría ser seguir la pared al igual que hacia el tanque “walls”, además, se podrían 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 evasión, ya que al estar pegado a la pared, si se realizaran los movimientos correctamente, podríamos conseguir que nuestro enemigo fuera perdiendo energía poco a poco debido a que no nos acertaría en los disparos. Centrándonos en cada una de las estrategias seguidas para cada uno de los tres  puntos enunciados anteriormente, tenemos:

¿Como movernos? Para elegir esta opción, lo primero que tuvimos que hacer es consultar que métodos del API de Robocode nos ayudarían a realizar los movimientos y por donde moveríamos a nuestro robot: Para que tanto el radar, como el cañón se muevan independientemente del chasis del tanque, se necesitan: void setAdjustGunForRobotTurn(boolean newAdjustGunForRobotTurn)  sirve para

tener un movimiento del cañón independiente del cuerpo void setAdjustRadarForGunTurn(boolean newAdjustRadarForGunTurn)  sirve para

tener un movimiento del radar independiente del cañón.

void setAdjustRadarForRobotTurn(boo setAdjustRadarForRobotTurn(boolean lean newAdjustRadarForRobotTurn) newAdjustRadarForRobotTurn)  sirve para

tener un movimiento del radar independiente del robot. Para obtener las dimensiones del campo se pueden utilizar los siguientes métodos: double getBattleFieldHeight()  nos indica la altura máxima del campo de batalla. double getBattleFieldWidth()  nos indica la anchura máxima d del el 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 posición en el campo de batalla del robot mediante:

 

double getX() nos indica la posición en la coordenada X de nuestro robot. dee nuestro robot. double getY()  nos indica la posición en la ordenada Y d

Para mover en si el tanque, están los siguientes métodos: mover ver el rob robot ot hacia adelante void setAhead(double distance)  sirve para mo void setBack(double distance) sirve para mover el robot hacia atrás void turnLeft(double degrees)  sirve para girar a la izquierda void turnRight(double degrees) sirve para girar a la derecha de igual modo, existen métodos para mover tanto el radar como el escáner: void setTurnGunLeft(double degrees) void setTurnGunRight(double setTurnGunRight(double degrees) void setTurnRadarLeft(double setTurnRadarLeft(double degrees) void setTurnRadarRight(double setTurnRadarRight(double degrees)

con todos estos métodos, podemos conseguir que nuestro robot mueva independientemente el escáner y el cañón 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 no noss movemos mucho y deprisa, sin que el movimiento sea predecible por los robots enemigos, ellos iran gastando poco a poco su energía disparándonos. Con lo que habremos implementado nuestra estrategia de evasión. 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 estaríamos perdiendo energía inútilmente. 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.

¿Cuando, como y con que potencia disparar? Una vez decidida la estrategia del movimiento, y sabiendo que es independiente de los disparos, se nos planteo el problema de que el adversario se moviese mucho y estuviese muy lejos de nosotros. Ya que nosotros no nos acercamos a el, y no sabemos si el lo hará, decidimos que no salía rentable dispararle desde lejos ya que estaríamos utilizando nuestra propia técnica en nuestra contra, así que decidimos que si se encontraba a mas de 300 de distancia, no le dispararíamos. Aunque este a menos de 300 de distancia, si nos queda poca energía mientras que el enemigo tiene mas que nosotros, tampoco le disparamos, es preferible esquivarle para que siga perdiendo energía hasta que tenga aproximadamente la misma que nosotros. También, tenemos que pensar con que potencia de disparo lanzamos nuestra bala contra el robot contrincante. Tras decidir que a mas de 300 de distancia no le dispararíamos, y que la potencia de fuego puede variar entre 0 y 3 fue sencillo pensar que si estaba a menos de 50 le dispararíamos con la máxima energía, si estaba entre 50 y 100 con algo menos y así progresivamente tal y como muestra el cuadro siguiente:

 

7

 

0 - 50 50 - 100 100 - 150 150 – 200 200 – 250

Potencia de disparo 3 2.5 2 1.5 1

250 – 300

0.5

distancia

En resumen, solo dispararíamos al contrincante si tiene nuestra misma energía o menos y se encuentra cerca de nosotros, sino, preferimos esquivarle para que siga perdiendo energía disparándonos.

¿Como localizar a nuestro enemigo? Para localizar a nuestro enemigo, lo primero que tenemos que hacer es girar el radar 360 grados para obtener por primera vez su posición y demás datos necesarios para el disparo. Además, si llevamos un algún tiempo sin localizarle, también vendría bien volver a rotar el escáner para volver a localizarle cuanto antes. En caso de que le tengamos localizado, puesto que el radar estará siempre intentando apuntar hacia el enemigo, lo único qu quee tenemos que hacer para actualizar la posición del radar es obtener la posición relativa del otro robot y restarle el ángulo de diferencia que haya entre nuestro escáner y la posición de 0º de nuestro robot (el heading). Además, si se le añade un pequeño offset, se conseguirá corregir el desfase entre el momento en el que calculamos la situación del otro tanque y la que tendrá en el momento siguiente de actualizar el escáner. De esta forma estará siempre localizado y  puesto que solo nos tenemos que preocupar de un solo contrincante, no necesitaremos implementar muchas mejoras respecto a este movimiento de escáner. Para almacenar toda la información que necesitamos de nuestro enemigo, tendremos que crearnos una clase aparte de la del propio robot donde estén almacenadas todas las variables y métodos que tenemos que realizar sobre el cálculo de posición y disparo de nuestro enemigo.

 

8

 

 

Código  package dmm;  package dmm; import robocode.*; import import java.awt.Color;  java.awt.Color; /** * Definitivo – by dmm y pa */  public class Definitivo class Definitivo extends extends AdvancedRobot  AdvancedRobot { double movimiento movemos  double movimiento;; // cuanto nos movemos  double double movMaxHor;  movMaxHor; //movimiento maximo horizontal  horizontal  double movMaxVer; //movimiento maximo vertical double movMaxVer; vertical   double double potencia;  potencia; // potencia de disparo disparo   //variables de estado para saber el movimiento siguiendo las paredes int pared int  pared = 2; // variable final para tener el valor de 180º  180º   double PI = Math.PI; final double PI Enemigo target;// target;// referencia a un objeto de la clase enemigo // varibles que nos ayudan a saber en que sentido se ntido va el escaner y el robot int direction int  direction = 1; int sentidoEscaner int sentidoEscaner = 1; /** * run: Método principal de nuestro Robot */  public void  run()  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);  

9

 

  //activamos las funciones del cañon y radar para que sean independientes setAdjustGunForRobotTurn(true setAdjustGunForRobotTurn( true); ); setAdjustRadarForGunTurn(true setAdjustRadarForGunTurn( true); ); setAdjustRadarForRobotTurn(true setAdjustRadarForRobotTurn( true); ); //Bucle principal de funcionamiento while (true) 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(); (calcularPotencia()) fire(potencia); //Disparamos //Disparamos   if (calcularPotencia()) execute(); } }

/** * mover: Metodo que define el movimiento general de nuestro Robot */  public int mover(){ int mover(){ // varible que contiene el movimiento maximo que puede realizar el robot double movimientoMax double  movimientoMax = 100000; // variable local que nos indica en que pared estamos int isInWall; 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 pequeño, 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  25;  break ; case 2: movimientoMax = movMaxHor - getX() - 25; break  25;  break ; case 3: movimientoMax = getY() - 25; break  25;  break ; case 4: movimientoMax = getX() - 25; break  25;  break ; default: default: movimientoMax = 25; pared = 4; } // si nos el movimiento es justo para llegar al fin de esa pared, giramos, sino simplemente movemos hacia adelante

 

10

 

 (movimiento > movimientoMax &&( pared == 1 || pared == 2 ||   if  (movimiento  pared == 3 || pared == 4 )) { if   (pared == 4 && movimientoMax == 25){isInWall = 4;} else {isInWall = ++pared %4; else {isInWall movimiento = movimientoMax; ahead(direction*movimiento); turnRight(90); } } else {setAhead(movimiento);} else {setAhead(movimiento);} return isInWall; return isInWall; } /** * onHitRobot: onHitRobot: Si chocamos con un adversario, retrocedemos o avanzamos segun *donde este y cambiamos el sentido de la marcha. */  public void  onHitRobot(HitRobotEvent  onHitRobot(HitRobotEvent e) { direction = direction*-1; // Si esta delante nuestra, echamos para atrás if  (e.getBearing()  (e.getBearing() > -90 && e.getBearing() < 90) setBack(100); // Si está detrás nos desplazamos en sentido contrario else setAhead(100); } /** * calcularPotencia(): Calculamos la potencia optima basado en la distancia al objetivo */  boolean calcularPotencia() {  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, 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*/  false*/   if  (distancia  (distancia > 300 || distancia < 0) potencia = 0; else{{ else min = 250; max = 300;  potencia = 3;

 

11

 

// calculamos la potencia de tiro en funcion de la distancia a la que

  este

while(!(distancia while (!(distancia > min && distancia < max)){  potencia = potencia -0.5; min= min -50; max = max -50; if  (distancia  (distancia > 300 || distancia < 0){potencia = 0; break  0; break ;;}} } // si esta en el rango, devolvemos true, con lo que se disparara if (potencia != 0) return true; 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()  escanear() { double radarOffset; 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  (radarOffset < 0) radarOffset -= PI/8; else radarOffset += PI/8; } //giramos el radar setTurnRadarLeftRadians(NormaliseBearing(radarOffset)); } /** * calcularTiro(): Realizamos los cálculos de balística */ void  calcularTiro()  calcularTiro() {

 

12

-

 

  // Primera estimación: calculamos el tiempo que tardaría en llegar el  proyectil a la posicion actual del objetivo long time long  time = getTime() + (int)(target.distance/(20-(3*potencia))); // calculamos el offset de giro según una estimación de movimiento lineal double   double gunOffset = getGunHeadingRadians() absbearing(getX(),getY(),target.guessX(time),target.guessY(time)); //giramos el cañon setTurnGunLeftRadians(NormaliseBearing(gunOffset)); } //ajustamos el ángulo si no se encuentra de -pi a pi double NormaliseBearing( double  NormaliseBearing(double double ang)  ang) { if  (ang  (ang > PI) ang -= 2*PI; if (ang < -PI) ang += 2*PI; return return ang;  ang; } //Si no se encuentra de 0 a 2pi lo modificamos para que sea el ángulo más corto double NormaliseHeading( double  NormaliseHeading(double double ang)  ang) {  (ang > 2*PI) if  (ang ang -= 2*PI; if  (ang  (ang < 0) ang += 2*PI; return ang;  ang; return } //Calcula la distancia entre dos puntos x e y  public double getrange( double getrange( double  double x1,  x1,doubl doublee y1, double x2, double x2,double double y2  y2 ) { double double xo  xo = x2-x1; double yo double yo = y2-y1; double h double h = Math.sqrt( xo*xo + yo*yo ); return h; return h; } //Calcula el ángulo entre dos puntos  public double absbearing( double absbearing( double double x1,  x1,double double y1,  y1, double double x2,  x2,double double y2  y2 ) { double double xo  xo = x2-x1; double yo = y2-y1; double yo double h double h = getrange( x1,y1, x2,y2 ); if ( xo > 0 && yo > 0 ) { return Math.asin( xo / h ); return Math.asin( }if ( xo > 0 && yo < 0 )

 

13

 

 

{ return Math.PI return Math.PI - Math.asin( xo / h ); } if ( xo < 0 && yo < 0 ) { return Math.PI + Math.asin( -xo / h ); return Math.PI } if ( xo < 0 && yo > 0 ) { return 2.0*Math.PI - Math.asin( -xo / h ); return 2.0*Math.PI } return 0;  0; return } /** * onScannedRobot: Método que se ejecuta cuando el scanner encuentra un

robot */  public void  onScannedRobot(ScannedRobotEvent  onScannedRobot(ScannedRobotEvent e) { //Si encontramos un robot más cercano al objetivo actual if  ((e.getDistance()  ((e.getDistance() < target.distance)||(target.name == e.getName())) { //Calcular apuntamiento double   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: Método que corre cuando muere un adversario */  public void  onRobotDeath(RobotDeathEvent  onRobotDeath(RobotDeathEvent e) { if  (e.getName()  (e.getName() == target.name) target.distance = 10000; //actualizamos la variable de distancia distancia   } }

 

14

 

  /* * Clase Enemigo. La utilizamos para almacenar la información acerca de nuestros adversarios * Posición, velocidad, disparos, etc */ class Enemigo class  Enemigo { String name; String name; double bearing;  public double bearing;  public double head; double head; long ctime;  public long ctime; double speed;  public double speed;  public double x,y; double x,y; double distance;  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 double guessX(long when) { long long diff  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 double guessY(long when) { long long diff  diff = when - ctime; return y+Math.cos(head)*speed*diff;  y+Math.cos(head)*speed*diff; return } }

 

15

 

Conclusiones La primera de las conclusiones es que ha sido extremadamente difícil depurar el código ya que no contábamos con ninguna manera de poder mostrar mensajes por  pantalla con lo que nunca hemos te tenido nido a ciencia cierta si el robot hacia lo que nosotros  pensábamos o no. Esto se ha debido a que los métodos del paquete “java.io” que teníamos en el API de Robocode Robocode daban errores de compilación. Este problema ha sido determinante construcción del robot pensadas ya que noen hemos podido implementar muchas de las mejoraseny la estrategias que teníamos el robot, teniendo que simplificar el código con lo que nuestro robot se ha hecho mucho más 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, también se han conseguido buenos resultados con los robots de  prueba. La experiencia ha sido pese a todo, entretenida y muy divertida.

 

16

 

 Bibliografía http://www.it.uc3m.es/jvillena/irc/   http://www.it.uc3m.es/jvillena/irc/ http://www.robocode.ie/index.html http://www.robocode.ie/index.html   http://robocode.sourceforge.net/ http://robocode.sourceforge.net/   http://www.codepoet.org/~markw/robocode/   http://www.codepoet.org/~markw/robocode/ http://www.ibm.com  http://www.ibm.com 

 

17

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF