1 package foo;
2
3 import robocode.*;
4 import robocode.util.Utils;
5 import java.awt.Color;
6 import java.lang.Math;
7 import java.util.Random;
8
9 /**
10
11 *
12 * Foobar est un bot écrit pour robocode à l'occasion du concours
13 * organisé par l'Institut Paul Lambin (http://robocode.ipl.be).
14 * Pour l'utiliser, référez vous à la documentation de robocode
15 * (http://robocode.sourceforge.net)
16 *
17 * Ce bot a été testé écrit sous un système entièrement libre, à l'aide
18 * d'outils tels que vim, doxygen, openjdk, …
19 * et fonctionne correctement avec OpenJDK 1.5 (version de javac 1.6)
20 * sur robocode 1.7.1
21 *
22 * @section Fonctionnement
23 * Le robot adopte plusieurs comportements en fonction de divers facteurs.
24 *
25 * @subsection Techniques
26
27 * @subsubsection Dodge
28 * Fait d'éviter les balles en observant l'énergie
29 * de l'ennemi.
30
31 * Inconvénients :
32 * @li La classe @c Robot ne se déplace pas assez vite contrairement
33 * à la classe @c AdvancedRobot. On peut corriger cela en évitant les
34 * balles si le robot se trouve à une certaine distance (@c 150
35 * semble raisonnable)
36 * @li L'@c Inactivity @c Time étant fort bas pour ce concours
37 * (30), cela peut nous induire en erreur. En effet, l'énergie du
38 * robot chute un peu à chaque tour. Cale se corrige en n'évitant
39 * pas les tirs en dessous d'une certaine puissance (@c 1.0 semble
40 * raisonnable)
41 *
42 * Néanmoins, nous n'utiliserons pas cette technique, qui s'est
43 * révélée peu efficace après quelques tests.
44
45 * @subsubsection Mouvement
46 * En 1v1, le robot se placera perpendiculairement et se déplacera de
47 * façon circulaire. Lorsqu'il touchera un mur, il change de
48 * direction. Le robot à tendance à aller se bloquer dans les coins à
49 * cause de ce type de déplacement, quand il est bloqué, il tourne
50 * alors en angle droit et avance, ce qui le sort du coin (si pas du
51 * premier coup, la seconde fois le robot ne sera plus dans le coin).
52 *
53 * En melée, on scanne jusqu'à ce qu'on trouve un robot assez près
54 * pour tirer dessus avec succès, si on n'en trouve pas, on tire sur
55 * le dernier robot scanné.
56 *
57 * Dans les deux cas, si on se trouve très proche d'un autre robot, on
58 * lui fonce dessus si on a une chance de le tuer afin de récolter
59 * plus de points, sinon on lui tire simplement dessus et on s'en
60 * éloigne.
61 *
62 * @subsubsection Visée
63 * Comme technique de visée, nous combinons le @c Linear @c
64 * Targeting et une méthode de visée basique.
65 * Si on tire des balles puissantes (et donc lentes), on essaie
66 * d'estimer la position qu'aura l'ennemi à l'impact, en considérant
67 * qu'il se déplace dans la même direction à la même vitesse.
68 * Dans l'autre cas, on tire simplement des balles plus rapides (et
69 * moins puissantes), directement là où se situe l'ennemi.
70 *
71 * @section Vocabulaire
72 * La documentation contient du vocabulaire propre à robocode
73 * (les termes anglais francisés), décrits ici.
74 *
75 * @li @c rammer : le fait de foncer dans un autre joueur pour lui
76 * faire perdre de la vie, et gagner beaucoup de points.
77 */
78
79 /**
80 * Le robot.
81 */
82 public class Foobar extends Robot {
83
84 /* Variables (elles ne sont pas mis en private pour avoir les
85 * commentaires dans la documentation doxygen. Elles ne sont pas
86 * censées être accessibles à partir d'autre part que des méthodes
87 * de la classe Foobar) */
88
89 /**
90 * Le robot qu'on est en train de rammer
91 * (vide si personne)
92 */
93 String m_sRamming = "";
94
95 /**
96 * Le bearing du robot qu'on ramme
97 */
98 double m_dRamBearing = 0.;
99 /**
100 * L'énergie du robot qu'on ramme
101 */
102 double m_dRamEnergy = 0.;
103 /**
104 * Le temps dernier ram effectué
105 */
106 long m_lLastRam = 0;
107
108 /**
109 * Le nombre de tir ratés
110 */
111 int m_iUnhit = 0;
112 /**
113 * Le nombre de fois qu'on est touché sans avoir bougé
114 */
115 int m_iHittedWithoutMove = 0;
116
117 /**
118 * La direction dans laquelle on se dirige .
119 * 1 si on se déplace en avant, -1 si on se déplace en arrière.
120 */
121 int m_iDirection = 1;
122
123 /**
124 * Le nombre de robot scannés (remis à zéro quand on arrive au
125 * nombre total de robot)
126 */
127 int m_iScanned = 0;
128
129 /* Constantes (ne sont pas en private pour la même raison que les
130 * variables) */
131 /**
132 * Distance à partir de laquelle un robot est trop loin pour
133 * qu'on lui tire dessus avec succès
134 */
135 static final double FarAway = 400.;
136 /**
137 * Discance à partir de laquelle le robot est considéré comme
138 * très proche, assez proche pour pouvoir rammer
139 */
140 static final double ReallyNear = 150.;
141 /**
142 * Énergie restante d'un robot pour qu'il soit considéré comme
143 * presque mort, et donc qu'on peut foncer dedans.
144 */
145 static final double AlmostDead = 25.;
146 /**
147 * Le nombre de tirs ratés avant de se déplacer
148 */
149 static final int MaxUnhit = 5;
150 /**
151 * Le nombre de tirs encaissés avant de se déplacer
152 */
153 static final int MaxHits = 3;
154 /**
155 * À chaque déplacement normal, on avance de cette distance
156 */
157 static final double MoveDistance = 70;
158
159 /**
160 * Fonction appelée au début du programme pour lancer le bot.
161 * Si on a rien à faire, on tourne le radar,
162 * si on était en train de rammer un autre robot, on continue à le
163 * rammer, tout en vérifiant qu'on peut continuer
164 */
165 public void run() {
166 looksGood();
167 while (true) {
168 if (m_sRamming == "") {
169 turnRadarRight(360);
170 }
171 else if (getTime() - m_lLastRam > 15 ||
172 !ramIfYouCan(m_sRamming, m_dRamBearing, m_dRamEnergy))
173 m_sRamming = "";
174 }
175 }
176
177 /**
178 * Fonction appelée lorsqu'un robot est scanné par le radar
179 */
180 public void onScannedRobot(ScannedRobotEvent e) {
181 /**
182 * On garde en mémoire le nombre d'ennemis scannés,
183 * afin de savoir si on a tout scanné (et de faire quelquechose au
184 * dernier moment si il n'y a pas eu d'occasion intéressante
185 */
186 m_iScanned = (m_iScanned+1) % getOthers();
187
188 /**
189 * Ennemi éloigné : on ne fait rien et on essaye de trouver
190 * un autre ennemi, sauf si c'est le seul ennemi
191 * ou qu'il est immobile
192 */
193 if (e.getDistance() > FarAway) {
194 if (getOthers() == 1 || e.getVelocity() == 0 || m_iScanned == 0) {
195 shootRobot(e.getVelocity(), e.getHeading(), e.getBearing(),
196 e.getDistance(), e.getEnergy());
197 m_sRamming = "";
198 turnRight(Utils.normalRelativeAngleDegrees(e.getBearing()+90));
199 ahead(MoveDistance*m_iDirection);
200 }
201 }
202 /**
203 * Ennemi très proche : on ramme si c'est à notre avantage
204 * sinon on tire dessus
205 */
206 else if (e.getDistance() < ReallyNear) {
207 if (!ramIfYouCan(e.getName(), e.getBearing(), e.getEnergy())) {
208 shootRobot(e.getVelocity(), e.getHeading(), e.getBearing(),
209 e.getDistance(), e.getEnergy());
210 }
211 else {
212 m_dRamBearing = e.getBearing();
213 m_dRamEnergy = e.getEnergy();
214 }
215 }
216 else {
217 if (getOthers() == 1) {
218 /**
219 * Sinon, on tire sur le robot et on se place
220 * perpendiculairement à lui à condition que ça soit le seul
221 * robot (1v1)
222 */
223 shootRobot(e.getVelocity(), e.getHeading(), e.getBearing(),
224 e.getDistance(), e.getEnergy());
225 turnRight(Utils.normalRelativeAngleDegrees(e.getBearing()+90));
226 ahead(MoveDistance*m_iDirection);
227 }
228 else if (m_iScanned == 0) {
229 /**
230 * Si c'est le dernier robot scanné, on lui tire dessus
231 */
232 shootRobot(e.getVelocity(), e.getHeading(), e.getBearing(),
233 e.getDistance(), e.getEnergy());
234 turnRight(Utils.normalRelativeAngleDegrees(e.getBearing()+90));
235 ahead(MoveDistance*m_iDirection);
236 }
237 }
238 }
239
240 /**
241 * Fonction appelée lorsqu'un tir est raté.
242 * On fuit si on rate trop de tirs
243 */
244 public void onBulletMissed(BulletMissedEvent e) {
245 m_iUnhit += 1;
246 if (m_iUnhit > MaxUnhit)
247 runAway();
248 }
249
250 /**
251 * Fonction appelée lorsqu'un tir touche un joueur
252 */
253 public void onBulletHit(BulletHitEvent e) {
254 m_iUnhit = 0;
255 }
256
257 /**
258 * Fonction appelée lorsqu'on est touché par une balle.
259 * On fuit si on est trop touché sans bouger
260 */
261 public void onHitByBullet(HitByBulletEvent e) {
262 m_iHittedWithoutMove += 1;
263 if (m_iHittedWithoutMove > MaxHits)
264 runAway();
265 }
266
267 /**
268 * Fonction appelée lorsqu'on se cogne contre un mur.
269 * On change la direction du déplacement
270 */
271 public void onHitWall(HitWallEvent e) {
272 m_iDirection = -m_iDirection;
273
274 /* Si on est dans un coin */
275 if ((getX() < 150 || getX() > getBattleFieldWidth() - 150) &&
276 (getY() < 150 || getY() > getBattleFieldHeight() - 150)) {
277 turnRight(90);
278 runAway();
279 }
280 }
281
282
283 /**
284 * Fonction appelée lorsqu'on est cogné par un autre robot.
285 * Si son énergie est supérieur à la notre, on lui tire dessus
286 * et on fuit.
287 * Si son énergie est inférieure à la notre, et que c'est le
288 * seul ennemi restant, on le ramme.
289 * Si il y a plus d'un ennemi, on le ramme si son énergie est
290 * très petite.
291 */
292 public void onHitRobot(HitRobotEvent e) {
293 if (e.getEnergy()-20 > getEnergy()) {
294 m_sRamming = "";
295 shootRobot(.0, .0, e.getBearing(), .0, e.getEnergy());
296 runAway();
297 }
298 else if (e.getEnergy() < AlmostDead) {
299 if (!ramIfYouCan(e.getName(), e.getBearing(), e.getEnergy())) {
300 shootRobot(.0, .0, e.getBearing(), .0, e.getEnergy());
301 runAway();
302 }
303 else {
304 m_dRamBearing = e.getBearing();
305 m_dRamEnergy = e.getEnergy();
306 }
307 }
308 else if (getOthers() == 1) {
309 if (!ramIfYouCan(e.getName(), e.getBearing(), e.getEnergy())) {
310 shootRobot(.0, .0, e.getBearing(), .0, e.getEnergy());
311 runAway();
312 }
313 else {
314 m_dRamBearing = e.getBearing();
315 m_dRamEnergy = e.getEnergy();
316 }
317 }
318 }
319
320 /**
321 * Fonction appelée à la mort d'un autre robot.
322 * Si c'est le robot qu'on rammait, on arrête de rammer
323 */
324 public void onRobotDeath(RobotDeathEvent e) {
325 if (e.getName() == m_sRamming)
326 m_sRamming = "";
327 }
328
329
330 /**
331 * Fonction appelée lorsqu'on a gagné.
332 * Petite danse de victoire
333 */
334 public void onWin(WinEvent e) {
335 //setAdjustGunForRobotTurn(false);
336 //setAdjustRadarForRobotTurn(false);
337 turnGunRight(720);
338 }
339
340 /**
341 * Tire sur le robot.
342 * Si on tire une balle de puissance proche de 3, comme les balles
343 * sont fort lentes, on estime où le robot se situera à l'impact, et
344 * on tire à cet endroit.
345 * Dans las autre cas, on tire directement là où se trouve le robot
346 * @param velocity
347 * @param heading
348 * @param bearing
349 * @param distance
350 * @param energy
351 */
352 public void shootRobot(double velocity, double heading, double bearing,
353 double distance, double energy) {
354 /* On ne bouge le canon que si on pourra tirer */
355 if (getGunHeat() == 0) {
356 double power = powerForMaxDamage(energy, distance);
357 double absoluteBearing = getHeading() + bearing;
358
359 if (power + 0.3 > 3 || velocity - 0.01 > 0) {
360 turnGunRight(Utils.normalRelativeAngleDegrees(
361 absoluteBearing -
362 (getGunHeading() +
363 velocity *
364 Math.sin(Math.toRadians(getHeading() -
365 absoluteBearing)/13))));
366 }
367 else
368 turnGunRight(Utils.normalRelativeAngleDegrees(
369 bearing - (getGunHeading() - getHeading())));
370
371 fire(power);
372 }
373 }
374
375 /**
376 * Ramme le robot si c'est avantageux
377 @param name
378 * @param bearing
379 * @param energy
380 * @return
381 */
382 public boolean ramIfYouCan(String name, double bearing, double energy) {
383 if (energy < AlmostDead || getOthers() == 1
384 || energy - 20< getEnergy()) {
385 double gunAngle;
386 m_sRamming = name;
387 m_lLastRam = getTime();
388
389 gunAngle =
390 Utils.normalRelativeAngleDegrees(getHeading()-getGunHeading());
391
392 setAdjustRadarForRobotTurn(false);
393 if (bearing > 90 || bearing < -90) {
394 turnLeft(Utils.normalRelativeAngleDegrees(180-bearing));
395 turnGunLeft(Utils.normalRelativeAngleDegrees(180-gunAngle));
396 }
397 else {
398 turnRight(bearing);
399 turnGunRight(gunAngle);
400 }
401
402 fire(powerForMaxDamage(energy, .0));
403
404 if (bearing > 90 || bearing < -90)
405 back(100);
406 else
407 ahead(100);
408
409 return true;
410 }
411 else {
412 m_sRamming = "";
413 return false;
414 }
415 }
416
417 /**
418 * Le robot doit fuire, il s'éloigne de la position d'où il est
419 */
420 public void runAway() {
421 m_iHittedWithoutMove = 0;
422 boolean ahead = true;
423
424 if (m_sRamming != "") {
425 m_sRamming = "";
426 ahead = false;
427 }
428
429 /* Si on se dirige vers un bord */
430 if ((getX() < 100 && between(getHeading(), 180, 360)) ||
431 (getX() > getBattleFieldWidth() - 100 &&
432 between(getHeading(), 0, 180)) ||
433 (getY() < 100 && between(getHeading(), 90, 270)) ||
434 (getY() > getBattleFieldHeight() - 100 &&
435 (getHeading() > 270 || getHeading() < 90)))
436 ahead = false;
437
438 if (ahead)
439 ahead(200);
440 else
441 back(200);
442 }
443
444 /**
445 * Calcule la puissance d'une balle pour tuer l'ennemi.
446 * Dans le cas où l'ennemi est éloigné, on ne tire pas des balles
447 * puissantes, les balles moins légères étant plus rapides, et nous
448 * font perdre moins d'énergie
449 * @param energy
450 * @param distance
451 * @return
452 */
453 public double powerForMaxDamage(double energy, double distance) {
454 /* API de robocode :
455 * The bullet will do (4 * power) damage if it hits another robot.
456 * If power is greater than 1, it will do an additional 2 * (power
457 * - 1) damage
458 */
459 if (energy < 16.0) {
460 /* On a pas de bonus, mais on tire quand même, pour l'achever */
461 if (energy/4 < 1.1)
462 return energy/4;
463 else
464 return energy/6 + 1/3;
465 }
466 else if (distance > FarAway) {
467 return 1.1; /* Juste pour avoir le bonus de dégats */
468 }
469 else
470 return 3.0;
471 }
472
473 /**
474 * Change les couleurs du robot.
475 */
476 public void looksGood() {
477 Random gen = Utils.getRandom();
478 setBodyColor(new Color(gen.nextFloat(), gen.nextFloat(),
479 gen.nextFloat()));
480 setBulletColor(new Color(gen.nextFloat(), gen.nextFloat(),
481 gen.nextFloat()));
482 setGunColor(new Color(gen.nextFloat(), gen.nextFloat(),
483 gen.nextFloat()));
484 setRadarColor(new Color(gen.nextFloat(), gen.nextFloat(),
485 gen.nextFloat()));
486 }
487
488 /**
489 * Affiche des informations sur la sortie standard
490 @param str
491 */
492 public void debug(String str) {
493 System.out.println("Debug : " + str);
494 }
495
496 /**
497 * Si un nombre est situé entre deux nombres
498 @param x
499 * @param a
500 * @param b
501 * @return
502 */
503 public boolean between(double x, double a, double b) {
504 return a <= x && b >= x;
505 }
506 }
507