Last active
September 8, 2015 22:10
-
-
Save mmacedo/4dfaac48e7f12d72739b to your computer and use it in GitHub Desktop.
Trabalho de IA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.