Basilisk/Code

From Robowiki
Jump to navigation Jump to search

Version 2.35

package slugzilla;
import robocode.*;
import java.awt.geom.*;
import robocode.util.*;
import java.awt.*;
import static robocode.util.Utils.normalRelativeAngleDegrees;
import robocode.BulletHitEvent;
import robocode.HitByBulletEvent;

public class Basilisk extends AdvancedRobot {

	private static double lateralDirection;
	private static double lastEnemyVelocity;
	static double enemyFirePower;
	static double enemyBulletVelocity;
    double enemyDistance;
    double enemyAbsoluteBearing;
	double enemyVelocity;
	static double direction = 1;
	static int movementMode;
	static double hits;
	private static double enemyEnergy;
	static double deltaT;
	static double lastDeltaT;
	static double lastReverseTime;
	static double firePower;
    public void run() {
        setAdjustRadarForGunTurn(true);
 		setAdjustGunForRobotTurn(true);
		setBodyColor(new Color(40, 100, 100));
		setGunColor(new Color(34, 50, 50));
		setRadarColor(new Color(0, 255, 0));
		setScanColor(new Color(0, 255, 0));
	 	turnRadarRightRadians(Double.POSITIVE_INFINITY);
        do {
            scan();
        } while (true);
    }
 
    public void onScannedRobot(ScannedRobotEvent e) {
		//radar ↓
		double radarTurn = getHeadingRadians() + e.getBearingRadians() - getRadarHeadingRadians();
    	setTurnRadarRightRadians(Utils.normalRelativeAngle(radarTurn));
		//movement ↓
       	
		double v2; 
		double offset;
		double theta = 0.5952*(20D - 3D*enemyFirePower)/enemyDistance;

		if (movementMode >= 1) {
			offset = 2 + 100/e.getDistance();
		} else {
			//offset = 1.5 + 100/e.getDistance();
			offset = 2 + (int)(100/e.getDistance());
		}
		
        enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
		double v1 = enemyAbsoluteBearing;
        enemyDistance = e.getDistance();
		enemyVelocity = e.getVelocity();
		enemyBulletVelocity = 20 - 3 * enemyFirePower;
		
		if (enemyEnergy > e.getEnergy() && enemyEnergy - 3 <= e.getEnergy()) {
			enemyFirePower = enemyEnergy - e.getEnergy();
		}
		while(!new Rectangle2D.Double(18, 18, 764, 564).
			contains(getX() + 160 * Math.sin(v2 = v1 + direction * (offset -= .02)), getY() + 160 * Math.cos(v2)));

		if (offset < 0.75) {
			direction = -direction;
			lastDeltaT = deltaT;
			lastReverseTime = getTime();
		}
		setTurnRightRadians(Math.tan(v2 -= getHeadingRadians()));
		deltaT = getTime() - lastReverseTime;
		
		if(movementMode >= 1) {
			setAhead(1000 * Math.cos(v2));
			if (Math.random() > Math.pow(theta, theta) && enemyDistance > 100) {
				if (deltaT >= lastDeltaT +2 || deltaT <= lastDeltaT -2) {
					direction = -direction;
					lastDeltaT = deltaT;
					lastReverseTime = getTime();
				}
			}
		} else {
			double energyDrop = enemyEnergy - e.getEnergy();
			if ((int)(100/enemyDistance) + energyDrop > 0) {
				setAhead(((3 + (int)(energyDrop * 1.999999)) << 3 ) * Math.signum(Math.cos(v2)));
			}		
		}
		enemyEnergy = e.getEnergy();
		//gun ↓
		double enemyLateralVelocity = e.getVelocity() * Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing);
		if (enemyVelocity != 0) {
			lateralDirection = GFTUtils.sign(enemyVelocity * Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));
		}
		GFTWave wave = new GFTWave(this);
		
		wave.bulletPower = Math.min(Math.min(e.getEnergy()/4, getEnergy()/8), Math.min(0.05 * Math.round((1.8+100/enemyDistance)/0.05), 3));
		firePower =  wave.bulletPower;
		wave.MAX_ESCAPE_ANGLE = (Math.asin(8/(20 - 3 * wave.bulletPower)));
		wave.BIN_WIDTH = wave.MAX_ESCAPE_ANGLE / wave.MIDDLE_BIN;
		wave.gunLocation = new Point2D.Double(getX(), getY());
		GFTWave.targetLocation = GFTUtils.project(wave.gunLocation, enemyAbsoluteBearing, enemyDistance);
		wave.lateralDirection = lateralDirection;
		wave.setSegmentations(enemyDistance, enemyVelocity, lastEnemyVelocity, enemyLateralVelocity);
		lastEnemyVelocity = enemyVelocity;
		wave.bearing = enemyAbsoluteBearing;
		
		setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + wave.mostVisitedBearingOffset()) + ((Math.random()/500)-0.002));
		if (getEnergy() > 0.4) {
		//fire at will! ↓
			setFire(wave.bulletPower);
		}
		
		if (getEnergy() >= wave.bulletPower) {
			addCustomEvent(wave);
		}
	
    }
	//movement ↓
	public void onHitByBullet(HitByBulletEvent e) {	
		//enemyEnergy += 3 * enemyFirePower;
		enemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
		if ((hits += (4.25 / enemyBulletVelocity)) > getRoundNum() + 1) {
			movementMode++;
		}
    }
	public void onBulletHit(BulletHitEvent e) {
		//enemyEnergy -= 4 * firePower + firePower > 1 ? 2 * firePower - 1  : 0;
		enemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
	}
}
//gun ↓
class GFTWave extends Condition {
	static Point2D targetLocation;
	double bulletPower;
	Point2D gunLocation;
	double bearing;
	double lateralDirection;
	// 1000 is the maximum space away in a 800 by 600 battlefield
	private static final double MAX_DISTANCE = 1000;
	//segment into 5 sections ↓
	private static final int DISTANCE_INDEXES = 5;
	private static final int VELOCITY_INDEXES = 5;
	private static final int ACCELERATION_INDEXES = 3;
	private static final int LATERAL_VELOCITY_INDEXES = 5;

	static final int BINS = 25;
	static final int MIDDLE_BIN = (BINS - 1) / 2;
	static double MAX_ESCAPE_ANGLE;
	static double BIN_WIDTH;
	private static double[][][][][][] statBuffers = new double[DISTANCE_INDEXES][VELOCITY_INDEXES][VELOCITY_INDEXES][ACCELERATION_INDEXES][LATERAL_VELOCITY_INDEXES][BINS];
 
	private double[] buffer;
	private AdvancedRobot robot;
	private double distanceTraveled;
 
	GFTWave(AdvancedRobot _robot) {
		this.robot = _robot;
	}
	public boolean test() {
		advance();
		//remove passed waves
		if (hasArrived()) {
			//buffer[currentBin()]++;
		//	value = buffer[currentBin()]
		//	newValue = .9*value +1
		//	buffer[currentBin()] = newValue
			
			for (int i = 0; i < BINS; i++) {
				buffer[i] = 0.95 * buffer[i];
			}
			buffer[currentBin()] += 1;
			robot.removeCustomEvent(this);
		}
		return false;
	}
	double mostVisitedBearingOffset() {
		return (lateralDirection * BIN_WIDTH) * (mostVisitedBin() - MIDDLE_BIN);
	}
	void setSegmentations(double distance, double velocity, double lastVelocity, double lateralVelocity) {
		int distanceIndex = (int)(distance / (MAX_DISTANCE / DISTANCE_INDEXES));
		int velocityIndex = (int)Math.abs(velocity / 2);
		int lastVelocityIndex = (int)Math.abs(lastVelocity / 2);
		int deltaTime;
		if (velocityIndex < lastVelocityIndex) {
			deltaTime = 0;
		} else if (velocityIndex > lastVelocityIndex) {
			deltaTime = 1;
		} else {
			deltaTime = 2;
		}
		int accelerationIndex = deltaTime; 
		int lateralVelocityIndex  = (int)Math.abs(lateralVelocity / 2);
	
		buffer = statBuffers[distanceIndex][velocityIndex][lastVelocityIndex][accelerationIndex][lateralVelocityIndex];
	}
	private void advance() {
		distanceTraveled += GFTUtils.bulletVelocity(bulletPower);
	}
	private boolean hasArrived() {
		return distanceTraveled > gunLocation.distance(targetLocation) - 18;
	}
	private int currentBin() {
		int bin = (int)Math.round((
									(Utils.normalRelativeAngle(GFTUtils.absoluteBearing(gunLocation, targetLocation) - bearing)) 
									/ (lateralDirection * BIN_WIDTH)
								) + MIDDLE_BIN);
		return GFTUtils.minMax(bin, 0, BINS - 1);
	}
	private int mostVisitedBin() {
		int mostVisited = MIDDLE_BIN;
		for (int i = 0; i < BINS; i++) {
			if (buffer[i] > buffer[mostVisited]) {
				mostVisited = i;
			}
		}
		return mostVisited;
	}	
}
//helpful utilities ↓
class GFTUtils {
	static double bulletVelocity(double power) {
		return 20 - 3 * power;
	}
	static Point2D project(Point2D sourceLocation, double angle, double length) {
		return new Point2D.Double(sourceLocation.getX() + Math.sin(angle) * length,
				sourceLocation.getY() + Math.cos(angle) * length);
	}
	static double absoluteBearing(Point2D source, Point2D target) {
		return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
	}
	static int sign(double v) {
		return v < 0 ? -1 : 1;
	}
	static int minMax(int v, int min, int max) {
		return Math.max(min, Math.min(max, v));
	}
}