Skip to content

Instantly share code, notes, and snippets.

@mmacedo
Last active September 8, 2015 22:10
Show Gist options
  • Save mmacedo/4dfaac48e7f12d72739b to your computer and use it in GitHub Desktop.
Save mmacedo/4dfaac48e7f12d72739b to your computer and use it in GitHub Desktop.
Trabalho de IA
package blackhawk;
import java.util.*;
import static robocode.util.Utils.normalRelativeAngleDegrees;
public class BH2 extends robocode.Robot {
private class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
public Point move(double angle, double distance) {
double x2 = x + distance * Math.sin(Math.toRadians((angle + 360.0) % 360.0));
double y2 = y + distance * Math.cos(Math.toRadians((angle + 360.0) % 360.0));
return new Point(x2, y2);
}
}
private class RobotSighting {
private Point position;
private double heading;
private long lastSeen;
private boolean movedSinceLastScan;
public RobotSighting(Point position, double heading, long lastSeen, boolean movedSinceLastScan) {
this.position = position;
this.heading = heading;
this.lastSeen = lastSeen;
this.movedSinceLastScan = movedSinceLastScan;
}
public Point getPosition() {
return this.position;
}
public double getHeading() {
return this.heading;
}
public long getLastSeen() {
return this.lastSeen;
}
public boolean hasMovedSinceLastScan() {
return this.movedSinceLastScan;
}
}
private abstract class Incident {
private Point position;
private long time;
protected Incident(Point position, long time) {
this.position = position;
this.time = time;
}
public Point getPosition() {
return this.position;
}
public long getTime() {
return this.time;
}
}
private class BulletHit extends Incident {
public BulletHit(Point position, long time) {
super(position, time);
}
}
private class TurnStandingStill extends Incident {
public TurnStandingStill(Point position, long time) {
super(position, time);
}
}
private final int ROBOT_SIZE = 36; // px
private final int PREFERRED_TILE_SIZE = 100; // px
private final int MIN_MOVEMENT = 100; // px
private final int ROBOT_SIGHTING_EXPIRATION = 150; // turnos
private final int BULLET_HIT_EXPIRATION = 75; // turnos
private final int TURN_STANDING_STILL_EXPIRATION = 25; // turnos
private final int TOO_OLD_TO_SHOOT_AT = (int)(120 / robocode.Rules.MAX_VELOCITY); // Turnos para se mover 120px na velocidade máxima
private final int ROBOT_SPEED_FOR_MOVEMENT_ESTIMATION = 6; // px/turno
// Listas de ocorrências para calcular o perigo de cada quadrilho
private Map<String, RobotSighting> robots = new HashMap<>();
private List<BulletHit> bullets = new ArrayList<>();
private List<TurnStandingStill> turns = new ArrayList<>();
// Dimensões da arena em número de quadrilhos
private int gridWidth;
private int gridHeight;
// Caso as dimensões da arena não sejam múltiplos de PREFERRED_TILE_SIZE
private double tileWidth;
private double tileHeight;
private double[][] calculateDangerMap() {
// Mapa de robôs avistados por ladrilho
int[][] robotsPerTile = new int[gridWidth][gridHeight];
Iterator<RobotSighting> robotSightIterator = robots.values().iterator();
while (robotSightIterator.hasNext()) {
RobotSighting robot = robotSightIterator.next();
// Se o robô não é visto há muito tempo
if ((getTime() - robot.getLastSeen()) > ROBOT_SIGHTING_EXPIRATION) {
robotSightIterator.remove();
continue;
}
int tileX = (int)(robot.getPosition().getX() / tileWidth);
int tileY = (int)(robot.getPosition().getY() / tileHeight);
++robotsPerTile[tileX][tileY];
}
// Mapa de tiros recebidos por ladrilho
int[][] hitsPerTile = new int[gridWidth][gridHeight];
Iterator<BulletHit> bulletHitIterator = bullets.iterator();
while (bulletHitIterator.hasNext()) {
BulletHit bullet = bulletHitIterator.next();
// Se o tiro já é muito antigo
if ((getTime() - bullet.getTime()) > BULLET_HIT_EXPIRATION) {
bulletHitIterator.remove();
continue;
}
int tileX = (int)(bullet.getPosition().getX() / tileWidth);
int tileY = (int)(bullet.getPosition().getY() / tileHeight);
++hitsPerTile[tileX][tileY];
}
// Mapa de turnos imóveis recebidos por ladrilho
int[][] turnsPerTile = new int[gridWidth][gridHeight];
Iterator<TurnStandingStill> turnIterator = turns.iterator();
while (turnIterator.hasNext()) {
TurnStandingStill turn = turnIterator.next();
// Se o tiro já é muito antigo
if ((getTime() - turn.getTime()) > TURN_STANDING_STILL_EXPIRATION) {
turnIterator.remove();
continue;
}
int tileX = (int)(turn.getPosition().getX() / tileWidth);
int tileY = (int)(turn.getPosition().getY() / tileHeight);
++turnsPerTile[tileX][tileY];
}
// Mapa de perigo por ladrilho
double[][] danger = new double[gridWidth][gridHeight];
double biggestScore = 1;
for (int x = 0; x < gridWidth; ++x) {
for (int y = 0; y < gridHeight; ++y) {
for (int x2 = 0; x2 < gridWidth; ++x2) {
for (int y2 = 0; y2 < gridHeight; ++y2) {
double distance = Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2));
danger[x2][y2] += robotsPerTile[x][y] * (100 / Math.pow(2, distance));
danger[x2][y2] += hitsPerTile[x][y] * (100 / Math.pow(4, distance));
danger[x2][y2] += turnsPerTile[x][y] * (100 / Math.pow(4, distance));
// Atualiza maior pontuação
biggestScore = Math.max(biggestScore, danger[x2][y2]);
}
}
}
}
// Normaliza pontuação de 0 a 1
for (int x = 0; x < gridWidth; ++x) {
for (int y = 0; y < gridHeight; ++y) {
danger[x][y] /= biggestScore;
}
}
return danger;
}
private Point leastDangerousPosition() {
double[][] danger = calculateDangerMap();
int leastDangerousX = 0, leastDangerousY = 0;
for (int x = 0; x < gridWidth; ++x) {
for (int y = 0; y < gridHeight; ++y) {
if (danger[x][y] < danger[leastDangerousX][leastDangerousY]) {
leastDangerousX = x;
leastDangerousY = y;
}
}
}
return new Point((leastDangerousX + 0.5) * tileWidth, (leastDangerousY + 0.5) * tileHeight);
}
private double getAngleTo(Point targetPosition) {
double angle = Math.toDegrees(Math.atan(Math.abs(targetPosition.getY() - getY()) / Math.abs(targetPosition.getX() - getX())));
// Q1
if (targetPosition.getX() >= getX() && targetPosition.getY() >= getY()) {
angle = 90 - angle;
// Q2
} else if (targetPosition.getX() >= getX() && targetPosition.getY() < getY()) {
angle = angle + 90;
// Q3
} else if (targetPosition.getX() < getX() && targetPosition.getY() < getY()) {
angle = 270 - angle;
// Q4
} else {
angle = 270 + angle;
}
return angle;
}
private void turnTo(Point targetPosition) {
double heading = getAngleTo(targetPosition);
turnRight(normalRelativeAngleDegrees(heading - getHeading()));
}
private void turnGunTo(Point targetPosition) {
turnGunTo(getAngleTo(targetPosition));
}
private void turnGunTo(double heading) {
turnGunRight(normalRelativeAngleDegrees(heading - getGunHeading()));
turnRadarRight(normalRelativeAngleDegrees(heading - getRadarHeading()));
}
private void shootClosestRobot() {
// Acha o robô mais próximo
RobotSighting closest = null;
double closestDistance = -1; // Valor inicial não é usado, mas java é dum-dum
for (RobotSighting robot : robots.values()) {
// Se faz muitos turnos que viu o robô, não adianta atirar nesse
if (getTime() - robot.getLastSeen() > TOO_OLD_TO_SHOOT_AT) {
continue;
}
double dx = robot.getPosition().getX() - getX();
double dy = robot.getPosition().getY() - getY();
double distance = Math.sqrt(dx * dx + dy * dy);
if (closest == null || distance < closestDistance) {
closest = robot;
closestDistance = distance;
}
}
// É possível que nenhum robô tenha sido escaneado recentemente
if (closest == null) {
return;
}
// Calcula posição atualiza do robô e mira lá
long elapsed = getTime() - closest.getLastSeen();
Point position = closest.getPosition().move(closest.getHeading(), elapsed * ROBOT_SPEED_FOR_MOVEMENT_ESTIMATION);
turnGunTo(closest.getPosition());
// Força escaneamento para atirar
scan();
}
private void iteration() {
// Corrige se o radar e a arma não estiverem alinhados
if (getGunHeading() - getRadarHeading() >= 1) {
turnRadarRight(normalRelativeAngleDegrees(getGunHeading() - getRadarHeading()));
}
Point destination = leastDangerousPosition();
double dx = destination.getX() - getX(), dy = destination.getY() - getY();
double distance = Math.sqrt(dx * dx + dy * dy);
// Se já estiver no lugar correto, adiciona ocorrência para forçar a se mover na próxima iteração
if (distance < MIN_MOVEMENT) {
turns.add(new TurnStandingStill(new Point(getX(), getY()), getTime()));
} else {
turnTo(destination);
// Se move apenas metade da distância para evitar ficar muito tempo sem recalcular
ahead(Math.max(MIN_MOVEMENT, distance / 2));
}
// Gira radar 360° para atualizar a posição dos robôs
turnRadarRight(360);
shootClosestRobot();
}
public void run() {
// Uniforme da equipe
setBodyColor(java.awt.Color.black);
setGunColor(java.awt.Color.black);
setRadarColor(java.awt.Color.gray);
setBulletColor(java.awt.Color.black);
setScanColor(java.awt.Color.black);
gridWidth = (int)(getBattleFieldWidth() / PREFERRED_TILE_SIZE);
tileWidth = getBattleFieldWidth() / gridWidth;
gridHeight = (int)(getBattleFieldHeight() / PREFERRED_TILE_SIZE);
tileHeight = getBattleFieldHeight() / gridHeight;
while (true) { iteration(); }
}
public void onHitByBullet(robocode.HitByBulletEvent e) {
bullets.add(new BulletHit(new Point(getX(), getY()), getTime()));
// Vira arma para quem atirou
turnGunTo(getHeading() + e.getBearing());
// Força escaneamento para atirar
scan();
}
private boolean isTeamMate(String name) {
return java.util.regex.Pattern.matches("blackhawk\\..*", name);
}
private void updateRobotPosition(String name, double bearing, double distance, double heading) {
// Atualiza posição do robô escaneado
Point pos = new Point(getX(), getY()).move(getHeading() + bearing, distance);
boolean movedSinceLastScan = false;
if (robots.containsKey(name)) {
RobotSighting sighting = robots.get(name);
movedSinceLastScan = Math.abs(pos.getX() - sighting.getPosition().getX()) > ROBOT_SIZE / 2 ||
Math.abs(pos.getY() - sighting.getPosition().getY()) > ROBOT_SIZE / 2;
}
robots.put(name, new RobotSighting(pos, heading, getTime(), movedSinceLastScan));
}
public void onHitRobot(robocode.HitRobotEvent e) {
// Se for do mesmo time, ignora
if (isTeamMate(e.getName())) {
return;
}
// Vira arma para o robô com quem pexamos
turnGunTo(getHeading() + e.getBearing());
// Força escaneamento para atirar
scan();
}
private Point positionForMovingTarget(double bulletPower, Point targetPosition, double targetHeading) {
double dx = targetPosition.getX() - getX(), dy = targetPosition.getY() - getY();
double targetDistance = Math.sqrt(dx * dx + dy * dy);
// Estima quanto o robô vai se mover até a bala atingir ele
double estimatedRobotMovement = (targetDistance / robocode.Rules.getBulletSpeed(bulletPower)) * ROBOT_SPEED_FOR_MOVEMENT_ESTIMATION;
// Atualiza a posição do robô que tem que mirar
Point estimatedPosition = targetPosition.move(targetHeading, estimatedRobotMovement);
// Corrige coordenada para não sair fora da arena
double boundedX = Math.max(ROBOT_SIZE / 2, Math.min(getBattleFieldWidth() - ROBOT_SIZE / 2, estimatedPosition.getX()));
double boundedY = Math.max(ROBOT_SIZE / 2, Math.min(getBattleFieldHeight() - ROBOT_SIZE / 2, estimatedPosition.getY()));
return new Point(boundedX, boundedY);
}
public void onScannedRobot(robocode.ScannedRobotEvent e) {
// Se for do mesmo time, ignora
if (isTeamMate(e.getName())) {
return;
}
// Atualiza posição do robô
updateRobotPosition(e.getName(), e.getBearing(), e.getDistance(), e.getHeading());
// Se tiver pouca energia, apenas foge
if (getEnergy() <= 5) {
return;
}
// Se o radar não estiver alinhado com a arma está fazendo giro de 360 graus
if(Math.abs(getRadarHeading() - getGunHeading()) > 1) {
return;
}
// Força do tiro dependendo da energia e da distância
double bulletPower = getEnergy() < 20 ? 1 : e.getDistance() < 100 ? 3 : 2;
if (robots.get(e.getName()).hasMovedSinceLastScan()) {
Point estimate = positionForMovingTarget(bulletPower, robots.get(e.getName()).getPosition(), e.getHeading());
// Se está se movendo então atira na frente do robô
turnGunTo(estimate);
} else {
// Se está parado, mira direto no robô
turnGunTo(getHeading() + e.getBearing());
}
fire(bulletPower);
// Força uma nova iteração para não ficar parado atirando
iteration();
}
}
@mmacedo
Copy link
Author

mmacedo commented Sep 8, 2015

Se move em direção ao local mais seguro da arena, gira 360 graus para recalcular as posições dos robôs e atira no robô mais próximo.

Para calcular o local mais seguro da arena, mantém uma lista de robôs escaneados e ocorrências de tiros recebidos e turnos sem se mover, calcula a posição mais vazia da arena, ou seja, mais longe de todos os robôs e ocorrências recentes. Ignora robôs da mesma equipe (package blackhawk).

Para atirar no robô mais próximo, ele vê se o robô mais próximo se moveu desde o último escaneamento. Se não se moveu, atira direto no robô, se o robô se moveu, estima a posição que o robô está sendo movendo e atira na frente dele. Ele não mira em robôs que foram escaneados pela última vez há mais de 15 turnos, ou seja, o suficiente para se mover 120 pixels na velocidade máxima.

Atira com força 3 se estiver a menos de 100 pixels, senão atira força 2. Se tiver menos de 20 de energia, atira sempre com força 1, se tiver até 5, nunca atira.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment