package wiki.mako;
import robocode.*;
import java.awt.geom.*;
import java.awt.Color;
import java.util.*;
// MakoHT - Prey sighted - prey eaten.
// Targeting by PEZ.
// Movement, HT (Hit This!) by Kawigi using SandboxFlattener movement.
//
// http://robowiki.dyndns.org/MakoHT
//
// This code is open source. Released under GPL-ish terms. Meaning you can use the
// code in your own robots. Meaning you should feel obliged to share any improvements.
// Share it back on MakoHT's RoboWiki home.
// $Id: MakoHT.java,v 1.1 2003/04/15 05:16:45 peter Exp $
public class MakoHT extends AdvancedRobot {
private static Point2D location = new Point2D.Double();
private static Point2D oldLocation = new Point2D.Double();
private static Point2D enemyLocation = new Point2D.Double();
private static Point2D oldEnemyLocation = new Point2D.Double();
private static Point2D impactLocation = new Point2D.Double();
private static Rectangle2D fieldRectangle;
private static boolean haveEnemy;
private static double enemyDistance;
private static double absoluteBearing;
private static double deltaBearing;
private static double meanAngularFactorLeft = 9.0;
private static double meanAngularFactorStraight = 1.0;
private static double meanAngularFactorRight = 9.0;
private static double meanOffsetFactorLeft = 0.0;
private static double meanOffsetFactorStraight = 0.0;
private static double meanOffsetFactorRight = 0.0;
private static double[] gunFactorLeft = new double[2];
private static double[] gunFactorStraight = new double[2];
private static double[] gunFactorRight = new double[2];
private static int wins;
//variables for flattener movement:
private static double[] gainedenergy, lostenergy, hisgainedenergy, hislostenergy, hits, shots, hishits, hisshots;
private double currentDirection=1, currentEnergy=100, currentVBound = 0;
private long nextTime = 0;
public void run() {
fieldRectangle = new Rectangle2D.Double(0, 0 , getBattleFieldWidth(), getBattleFieldHeight());
setColors(Color.gray, Color.gray, Color.yellow);
setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
addCustomEvent(new GunAimedCondition());
//movement initialization:
final double maxX = getBattleFieldWidth()-18;
final double maxY = getBattleFieldHeight()-18;
if (gainedenergy == null)
{
double maxDistance = Point2D.distance(18, 18, maxX, maxY);
gainedenergy = new double[(int)(maxDistance/50)+2];
lostenergy = new double[(int)(maxDistance/50)+2];
hisgainedenergy = new double[(int)(maxDistance/50)+2];
hislostenergy = new double[(int)(maxDistance/50)+2];
hits = new double[(int)(maxDistance/50)+2];
shots = new double[(int)(maxDistance/50)+2];
hishits = new double[(int)(maxDistance/50)+2];
hisshots = new double[(int)(maxDistance/50)+2];
}
currentEnergy = 100;
while (true) {
//set basic movement:
setMaxVelocity(currentVBound);
double futureX = currentDirection*currentVBound*5*Math.sin(getHeadingRadians())+getX();
double futureY = currentDirection*currentVBound*5*Math.cos(getHeadingRadians())+getY();
if (futureX < 18D || futureY < 18D || futureX > maxX || futureY > maxY)
currentDirection = -currentDirection;
setAhead(currentDirection*40);
if (!haveEnemy) {
setTurnRadarLeft(22.5);
}
haveEnemy = false;
execute();
}
}
//radians version of normalRelativeAngle:
private double normalize(double angle)
{
return Math.atan2(Math.sin(angle), Math.cos(angle));
}
public void onScannedRobot(ScannedRobotEvent e) {
double radarTurn;
oldLocation.setLocation(location);
location.setLocation(getX(), getY());
oldEnemyLocation.setLocation(enemyLocation);
absoluteBearing = getHeading() + e.getBearing();
enemyDistance = e.getDistance();
toLocation(absoluteBearing, enemyDistance, location, enemyLocation);
deltaBearing = rollingAvg(deltaBearing, absoluteBearing(oldLocation, enemyLocation) - absoluteBearing(oldLocation, oldEnemyLocation), 5, 1);
haveEnemy = true;
radarTurn = normalRelativeAngle(getHeading() + e.getBearing() - getRadarHeading()) * 1.6;
setTurnRadarRight(radarTurn);
//data collection for movement - detecting enemy bullet fire:
double denergy = currentEnergy-e.getEnergy();
if (denergy >= .095 && denergy <= 3)
{
hislostenergy[(int)(e.getDistance()/50)] += denergy;
hisshots[(int)(e.getDistance()/50)] ++;
if (getTime() >= nextTime)
{
double rand = Math.random()*40-20;
currentDirection = (rand < 0)?-1:1;
currentVBound = Math.abs(rand);
nextTime = getTime()+(int)(e.getDistance()/(20-3*denergy)/2);
}
}
else if (denergy != 0)
System.out.println(denergy);
currentEnergy -= denergy;
double mindist = findDistanceBracket(200, 550);
double rel;
if (e.getDistance() < mindist+100 && e.getDistance() > mindist-50 && distanceFromCorner() > 200)
rel = Math.PI/2;
else if ((e.getDistance() > mindist+100 || distanceFromCorner() < 200) == currentDirection > 0)
rel = Math.PI/3;
else
rel = 2*Math.PI/3;
setTurnRightRadians(normalize(e.getBearingRadians()-rel));
aimGun();
if (currentEnergy == 0.0 && getOthers() == 1 && getTime() > nextTime) {
goTo(enemyLocation);
}
}
public void onCustomEvent(CustomEvent e) {
Condition condition = e.getCondition();
if (condition instanceof GunAimedCondition) {
if (currentEnergy > 0.0) {
shots[(int)(enemyDistance/50)] ++;
lostenergy[(int)(enemyDistance/50)] += bulletPower(currentEnergy);
Bullet bullet = setFireBullet(bulletPower(currentEnergy));
if (bullet != null) {
addCustomEvent(new CheckVirtualGunsCondition(bullet));
}
}
}
}
//print stats about distance benefits:
public void onEnd()
{
System.out.println("dist | My gained | My lost | His gained | His lost | gained on him | hit rate | his hit rate");
java.text.DecimalFormat fmt = new java.text.DecimalFormat("0.000");
for (int i=0; i<gainedenergy.length; i++)
{
System.out.println(printFormatted(Integer.toString(i*50), 4) + " | "
+ printFormatted(fmt.format(gainedenergy[i]), 9) + " | "
+ printFormatted(fmt.format(lostenergy[i]), 7) + " | "
+ printFormatted(fmt.format(hisgainedenergy[i]), 10) + " | "
+ printFormatted(fmt.format(hislostenergy[i]), 8) + " | "
+ printFormatted(fmt.format(findBenefit(i)), 13) + " | "
+ printFormatted(fmt.format(hits[i]/shots[i]), 8) + " | "
+ printFormatted(fmt.format(hishits[i]/hisshots[i]), 12));
}
}
private String printFormatted(String string, int length)
{
if (string.length() > length)
string = string.substring(0, length-1)+".";
else
while (string.length() < length)
string += " ";
return string;
}
public void onWin(WinEvent e) {
System.out.println("Wins: " + ++wins);
onEnd();
}
public void onDeath(DeathEvent e) {
System.out.println("Wins: " + wins);
onEnd();
}
//update opponent energy and benefit tables
public void onHitByBullet(HitByBulletEvent e)
{
currentEnergy += e.getPower()*3;
hisgainedenergy[(int)(enemyLocation.distance(getX(), getY())/50)] += e.getPower()*3;
double damage = Math.max(4*e.getPower(), 4*e.getPower()+2*(e.getPower()-1));
lostenergy[(int)(enemyLocation.distance(getX(), getY())/50)] += damage;
hishits[(int)(enemyLocation.distance(getX(), getY())/50)] ++;
}
//update opponent energy and benefit tables
public void onBulletHit(BulletHitEvent e)
{
double power = e.getBullet().getPower();
double damage = Math.max(4*power, 4*power+2*(power-1));
gainedenergy[(int)(enemyLocation.distance(getX(), getY())/50)] += power*3;
hislostenergy[(int)(enemyLocation.distance(getX(), getY())/50)] += damage;
hits[(int)(enemyLocation.distance(getX(), getY())/50)] ++;
currentEnergy -= damage;
}
//distance-finding methods:
public double findDistanceBracket(double min, double max)
{
int bestindex = (int)(min/50+.5);
for (int i=(int)(min/50+1.5); i <= (int)(max/50+.5); i++)
{
if (2*findBenefit(i)+findBenefit(i+1)+findBenefit(i-1) > 2*findBenefit(bestindex)+findBenefit(bestindex+1)+findBenefit(bestindex-1))
bestindex = i;
}
return bestindex*50;
}
public double findBenefit(int index)
{
return (gainedenergy[index]-lostenergy[index]+hislostenergy[index]-hisgainedenergy[index])/(gainedenergy[index]+lostenergy[index]+hislostenergy[index]+hisgainedenergy[index]);
}
private double distanceFromCorner()
{
return Math.min(Math.min(Point2D.distance(getX(), getY(), 0, 0), Point2D.distance(getBattleFieldWidth(), getY(), getX(), 0)), Math.min(Point2D.distance(getX(), getBattleFieldHeight(), 0, getY()), Point2D.distance(getBattleFieldWidth(), getBattleFieldHeight(), getX(), getY())));
}
private double bulletPower(double enemyEnergy) {
double power = Math.min(enemyEnergy / 4, 3);
power = Math.min(power, getEnergy()/5);
power = Math.min(power, 1200/enemyDistance);
return power;
}
private static double guessedBearingAngular(double bearing, double delta, double diffFactor) {
return bearing + delta * diffFactor;
}
private static double guessedBearingOffset(double bearing, double offsetFactor) {
return bearing + offsetFactor;
}
private void aimGun() {
double absoluteBearing = absoluteBearing(location, enemyLocation);
double guessedDistance = location.distance(enemyLocation);
double meanAngularFactor = meanAngularFactorStraight;
double meanOffsetFactor = meanOffsetFactorStraight;
double[] gunFactor = gunFactorStraight;
if (deltaBearing < -0.3) {
meanAngularFactor = meanAngularFactorLeft;
meanOffsetFactor = meanOffsetFactorLeft;
gunFactor = gunFactorLeft;
}
else if (deltaBearing > 0.3) {
meanAngularFactor = meanAngularFactorRight;
meanOffsetFactor = meanOffsetFactorRight;
gunFactor = gunFactorRight;
}
double guessedBearing;
if (gunFactor[0] < gunFactor[1]) {
guessedBearing = guessedBearingAngular(absoluteBearing, deltaBearing, meanAngularFactor);
}
else {
guessedBearing = guessedBearingOffset(absoluteBearing, meanOffsetFactor);
}
toLocation(guessedBearing, guessedDistance, location, impactLocation);
translateInsideField(impactLocation, 1);
guessedBearing = absoluteBearing(location, impactLocation);
setTurnGunRight(normalRelativeAngle(guessedBearing - getGunHeading()));
}
private void goTo(Point2D point) {
double distance = location.distance(point);
double angle = normalRelativeAngle(absoluteBearing(location, point) - getHeading());
if (Math.abs(angle) > 90) {
distance *= -1;
if (angle > 0) {
angle -= 180;
}
else {
angle += 180;
}
}
setTurnRight(angle);
setAhead(distance);
}
private static double bulletVelocity(double power) {
return 20 - 3 * power;
}
private static long travelTime(double distance, double velocity) {
return (int)Math.round(distance / velocity);
}
private void translateInsideField(Point2D point, double margin) {
point.setLocation(Math.max(margin, Math.min(fieldRectangle.getWidth() - margin, point.getX())),
Math.max(margin, Math.min(fieldRectangle.getHeight() - margin, point.getY())));
}
private void toLocation(double angle, double length, Point2D sourceLocation, Point2D targetLocation) {
targetLocation.setLocation(sourceLocation.getX() + Math.sin(Math.toRadians(angle)) * length,
sourceLocation.getY() + Math.cos(Math.toRadians(angle)) * length);
}
private double absoluteBearing(Point2D source, Point2D target) {
return Math.toDegrees(Math.atan2(target.getX() - source.getX(), target.getY() - source.getY()));
}
private static double normalRelativeAngle(double angle) {
double relativeAngle = angle % 360;
if (relativeAngle <= -180 )
return 180 + (relativeAngle % 180);
else if ( relativeAngle > 180 )
return -180 + (relativeAngle % 180);
else
return relativeAngle;
}
public static double rollingAvg(double value, double newEntry, double n, double weighting ) {
return (value*n + newEntry*weighting)/(n + weighting);
}
class CheckVirtualGunsCondition extends Condition {
private long time;
private double bulletVelocity;
private double bulletPower;
private double bearingDelta;
private Point2D oldRLocation = new Point2D.Double();
private Point2D oldELocation = new Point2D.Double();
private double oldBearing;
public CheckVirtualGunsCondition(Bullet bullet) {
this.time = getTime();
this.bulletVelocity = bullet.getVelocity();
this.bulletPower = bullet.getPower();
this.bearingDelta = deltaBearing;
this.oldRLocation.setLocation(location);
this.oldELocation.setLocation(enemyLocation);
this.oldBearing = absoluteBearing(oldRLocation, oldELocation);
}
public boolean test() {
if (getOthers() == 0) {
return false;
}
double bulletDistance = bulletVelocity * (getTime() - time);
if (bulletDistance > location.distance(enemyLocation) - 10) {
if (bearingDelta > 0.05) {
double impactBearing = absoluteBearing(oldRLocation, enemyLocation);
double bearingDiff = normalRelativeAngle(impactBearing - oldBearing);
double factor = bearingDiff / bearingDelta;
double miss;
if (bearingDelta < -0.3) {
meanAngularFactorLeft = rollingAvg(meanAngularFactorLeft, factor, 50, bulletPower);
meanOffsetFactorLeft = rollingAvg(meanOffsetFactorLeft, bearingDiff, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingAngular(oldBearing, bearingDelta, meanAngularFactorLeft)));
gunFactorLeft[0] = rollingAvg(gunFactorLeft[0], miss, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingOffset(oldBearing, meanOffsetFactorLeft)));
gunFactorLeft[1] = rollingAvg(gunFactorLeft[1], miss, 50, bulletPower);
}
else if (bearingDelta > 0.3) {
meanAngularFactorRight = rollingAvg(meanAngularFactorRight, factor, 50, bulletPower);
meanOffsetFactorRight = rollingAvg(meanOffsetFactorRight, bearingDiff, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingAngular(oldBearing, bearingDelta, meanAngularFactorRight)));
gunFactorRight[0] = rollingAvg(gunFactorRight[0], miss, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingOffset(oldBearing, meanOffsetFactorRight)));
gunFactorRight[1] = rollingAvg(gunFactorRight[1], miss, 50, bulletPower);
}
else {
meanAngularFactorStraight = rollingAvg(meanAngularFactorStraight, factor, 50, bulletPower);
meanOffsetFactorStraight = rollingAvg(meanOffsetFactorStraight, bearingDiff, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingAngular(oldBearing, bearingDelta, meanAngularFactorStraight)));
gunFactorStraight[0] = rollingAvg(gunFactorStraight[0], miss, 50, bulletPower);
miss = Math.abs(normalRelativeAngle(impactBearing - guessedBearingOffset(oldBearing, meanOffsetFactorStraight)));
gunFactorStraight[1] = rollingAvg(gunFactorStraight[1], miss, 50, bulletPower);
}
}
removeCustomEvent(this);
}
return false;
}
}
class GunAimedCondition extends Condition {
public boolean test() {
if (getOthers() == 0) {
removeCustomEvent(this);
}
return (getGunHeat() == 0.0 && getGunTurnRemaining() == 0.0);
}
}
}