BasicBulletShielder
Jump to navigation
Jump to search
package wiki;
import java.awt.Color;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayDeque;
import robocode.AdvancedRobot;
import robocode.BulletHitEvent;
import robocode.HitByBulletEvent;
import robocode.Rules;
import robocode.ScannedRobotEvent;
import robocode.util.Utils;
public class BasicBulletShielder extends AdvancedRobot {
public void run() {
setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
setAllColors(Color.WHITE);
/* Setup Radar */
turnRadarRightRadians(Double.POSITIVE_INFINITY);
while(true) scan();
}
ArrayDeque<Double> pastAngles = new ArrayDeque<Double>();
static final double BULLET_START_POWER = 0.15;
double lastEnemyEnergy = 100;
double lastMoveDistance = 0;
double aimAngle = 0;
double firePower = BULLET_START_POWER;
long fireOnTick = 0;
public void onScannedRobot(ScannedRobotEvent e) {
double angleToEnemy = getHeadingRadians() + e.getBearingRadians();
pastAngles.addFirst(angleToEnemy);
if(pastAngles.size() > 3)
pastAngles.removeLast();
long time = getTime();
/* Radar */
setTurnRadarRightRadians(1.9*Utils.normalRelativeAngle(angleToEnemy - getRadarHeadingRadians()));
double enemyEnergy = e.getEnergy();
double deltaEnergy = lastEnemyEnergy - enemyEnergy;
lastEnemyEnergy = enemyEnergy;
if(deltaEnergy > 0 && deltaEnergy <= 3) {
/* Determine the angle to us 2 turns ago */
double bulletAngle = pastAngles.getLast() + Math.PI;
double bulletSpeed = Rules.getBulletSpeed(deltaEnergy);
Point2D enemyPosition = project(getPosition(),angleToEnemy,e.getDistance());
/* how much time till the next time they can fire? */
int minTimeTillNextFire = (int)(Rules.getGunHeat(deltaEnergy)/getGunCoolingRate());
/* we have to move there and back, so divide the time we have in half */
int maxTestDistance = getMaxMoveDistanceForTime(minTimeTillNextFire/2);
double bestDeviation = Double.POSITIVE_INFINITY;
double myBulletSpeed = Rules.getBulletSpeed(BULLET_START_POWER);
for(int move = -maxTestDistance; move <= maxTestDistance; ++move) {
/* calculate where we will be when we finish moving */
Point2D myFirePosition = getMoveEnd(move);
/* determine how long it will take to get there *
long timeToMove = TIME_TO_MOVE[Math.abs(move)];
/* the bullet position at the time time we start */
Point2D bulletStart = project(enemyPosition,bulletAngle,bulletSpeed*(timeToMove+1));
/* calculate our target position */
Point2D target = intercept(myFirePosition,myBulletSpeed,bulletStart,bulletAngle,bulletSpeed);
double eGoalTime = Math.ceil(bulletStart.distance(target) / bulletSpeed);
/* Update our target */
target = project(bulletStart, bulletAngle, (eGoalTime-0.5)*bulletSpeed);
double myGunAim = angleFromTo(myFirePosition,target);
/* determine if they intersect */
Line2D eLine = new Line2D.Double(
project(bulletStart,bulletAngle,bulletSpeed*(eGoalTime-1)),
project(bulletStart,bulletAngle,bulletSpeed*eGoalTime)
);
double myTargetTime = myFirePosition.distance(target) / myBulletSpeed;
double myGoalTime = Math.ceil(myTargetTime);
Line2D myLine = new Line2D.Double(
project(myFirePosition,myGunAim,myBulletSpeed*(myGoalTime-1)),
project(myFirePosition,myGunAim,myBulletSpeed*myGoalTime)
);
if(myLine.intersectsLine(eLine)) {
double dB = myTargetTime - (myGoalTime - 0.5);
double deviation = dB*dB;
/* determine the best match, moving is free, bullets cost energy */
if(deviation < bestDeviation) {
bestDeviation = deviation;
lastMoveDistance = move;
fireOnTick = time + timeToMove;
/* tweak our fire power so that our bullet is more likely to hit theirs */
double goalSpeed = myFirePosition.distance(target) / (myGoalTime-0.5);
firePower = (20-goalSpeed)/3;
aimAngle = myGunAim;
setTurnRightRadians(0);
setAhead(lastMoveDistance);
}
}
} /* for(int move ... */
} /* if(deltaEnergy ... */
if(time == fireOnTick) {
setFire(firePower);
}
if(time == fireOnTick + 1) {
setAhead(-lastMoveDistance);
}
if(time > fireOnTick && getDistanceRemaining() == 0) {
aimAngle = angleToEnemy;
setTurnRightRadians(Utils.normalRelativeAngle(angleToEnemy + Math.PI/2.0 - Math.PI/4.0 - getHeadingRadians()));
}
setTurnGunRightRadians(Utils.normalRelativeAngle(aimAngle - getGunHeadingRadians()));
}
public void onBulletHit(final BulletHitEvent e) {
lastEnemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
}
public void onHitByBullet(final HitByBulletEvent e) {
lastEnemyEnergy += Rules.getBulletHitBonus(e.getPower());
}
static final int[] TIME_TO_MOVE = new int[] {0,1,2,2,3,3,4,4,4};
static final int[] MAX_MOVE_DIST = new int[] {0,1,3,5,8};
public int getMaxMoveDistanceForTime(int time) {
if(time >= MAX_MOVE_DIST.length)
return MAX_MOVE_DIST[MAX_MOVE_DIST.length-1];
return MAX_MOVE_DIST[time];
}
public Point2D getMoveEnd(double distance) {
return project(getPosition(),getHeadingRadians(),distance);
}
public Point2D getPosition() {
return new Point2D.Double(getX(),getY());
}
public Point2D project(Point2D start, double angle, double length) {
return new Point2D.Double(
start.getX() + Math.sin(angle) * length,
start.getY() + Math.cos(angle) * length
);
}
public Point2D intercept(Point2D pos, double vel, Point2D tPos, double tHeading, double tVel) {
double tVelX = Math.sin(tHeading)*tVel;
double tVelY = Math.cos(tHeading)*tVel;
double relX = tPos.getX() - pos.getX();
double relY = tPos.getY() - pos.getY();
double b = relX * tVelX + relY * tVelY;
double a = vel * vel - tVel * tVel;
b = (b + Math.sqrt(b * b + a * (relX * relX + relY * relY))) / a;
return new Point2D.Double(tVelX*b+tPos.getX(),tVelY*b+tPos.getY());
}
public double angleFromTo(Point2D a, Point2D b) {
return Math.atan2(b.getX() - a.getX(), b.getY() - a.getY());
}
}