[Home]BlackPearl/Code

Robo Home | BlackPearl | Changes | Preferences | AllPages

Showing revision 23
Code for version .83
/*
 * Created on Aug 4, 2003
 *
 */
package jekl.mini;
import java.awt.geom.*;
import java.io.*;
import java.util.zip.*;
import robocode.*;
import robocode.util.Utils;

/**
 *  BlackPearl is derived from Jekyl in many ways. It also serves as a proving ground for new concepts
 * that may one day find thier way into Jekyl. It is a pretty complete robot. If you use any of the code
 * contained in this bot I only ask that you let me see what it is you changed to make it better. Seeing 
 * your whole bot would be nice but is not a requirement. I would also appreciate it if you would help me
 * to learn from your experiences in return as well. If you use this please take the time to send me an 
 * email or an IM. -- jim
 */
public class BlackPearl extends AdvancedRobot {
	private static final double 	MAX_STAND_OFF_DISTANCE 	= 500D;
	private static final double 	MIN_STAND_OFF_DISTANCE 	= 400D;
	private static final double 	MOVEMENT_DISTANCE	= 21D;
	private static final double 	GUESS_FACTORS 		= 25D;
	private static final double	MAX_SHOT_POWER		= 3D;
	private static final double 	FIRE_POWER_NUMERATOR	= 1000D;
	private static final double	WALL_CURVE_DISTANCE	= 117D;		//pixels
	private static final double 	MIN_GRAZE_ANGLE		= 0.52359;	//30
	private static final double	MAX_GRAZE_ANGLE		= 2.61799;	//150
	//private static final double 	MIN_GRAZE_ANGLE		= 1.0479D;				//60 degrees in radians
	//private static final double 	MAX_GRAZE_ANGLE		= 2.0943D;				//120 degrees in radians
	private static final double	BEND_ANGLE		= 0.0698D;				//4 degrees in radians
	private static final int	CLOSE_IN		= -1;
	private static final int	BACK_OFF		= 1;
	private static Rectangle2D.Double field;
	private static int statBuffer[][][][];
	private static String targetName;
	private static boolean save;
	private static int hits;
	private int direction = 1;
	private int eDirection;
	private double eEnergy, eLastEnergy = 100D;
	private double eX, eY, eDelta, eAbsBearing, eVelocity, eDistance, eBearing, eLastVelocity;
	private boolean flatten = (hits > 2);
	private double eLastShot = 3;
	private long lastReverseTime;


	public void run() {
		//setColors(java.awt.Color.MAGENTA, java.awt.Color.LIGHT_GRAY, java.awt.Color.RED);
		field = new Rectangle2D.Double(18, 18, getBattleFieldWidth() - 36, getBattleFieldHeight() - 36);
		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);
		save = (getOthers() == 1);	//Only save data if we are in a 1-v-1 engagement, otherwise the data will be polluted
		do {
			turnRadarRightRadians(Double.POSITIVE_INFINITY);
		} while(true);
	}
	
	public void onScannedRobot(ScannedRobotEvent e) {
		//Gather target information
		targetName = e.getName();
		double latVel = (eVelocity = e.getVelocity()) * Math.sin(e.getHeadingRadians() - (eAbsBearing = Utils.normalRelativeAngle(getHeadingRadians() + (eBearing = e.getBearingRadians()))));
		eX = getX() + Math.sin(eAbsBearing) * (eDistance = e.getDistance());
		eY = getY() + Math.cos(eAbsBearing) * eDistance;
		double accelDiff = Math.round(Math.abs(eVelocity) - Math.abs(eLastVelocity));
		eDirection = (latVel >= 0 ? 1 : -1);

		//Set the firePower
		double firePower = Math.max(Math.min(Math.min(Math.min(getEnergy() * .2, (eEnergy = e.getEnergy()) * .25), FIRE_POWER_NUMERATOR/eDistance), MAX_SHOT_POWER), .1);

		//Set the indicies for shooting
		int latVelIndex = (int) (Math.abs(latVel) / 3);
		int accellIndex = (accelDiff == 0 ? 0 : (accelDiff > 0 ? 2 : 1));
		int distanceIndex = (int)(Math.min(11, (eDistance / 100D)));

		//Movement 
		double turnTo = getTurnAngle();
		setTurnRightRadians(Utils.normalRelativeAngle(turnTo));	

		//Reverse chance
		if ((eDelta = eLastEnergy - eEnergy) > 0 && eDelta <= 3) {
			eLastShot = eDelta;
		}
		//This is an adaptation from Raiko. If you can't beat 'em, join 'em
		double theta = .64 * bulletV(eLastShot)/eDistance;
		if ((Math.random() > Math.pow(theta, theta) && flatten)) {
			reverse();
		} else if (out(MOVEMENT_DISTANCE * direction, turnTo)) {
			reverse();
		}

		//Finally move
		setAhead(Math.cos(turnTo)*100*direction);
		//setAhead(MOVEMENT_DISTANCE * direction);

		//Radar
		setTurnRadarRightRadians(Math.sin(eAbsBearing - getRadarHeadingRadians()));

		//Gun 
		//Initialize the statbuffer
		if (statBuffer == null) {
			statBuffer = (int[][][][]) readObject(targetName);
		}

		//Retrieve the current stat segment to use
		int stats[] = statBuffer[accellIndex][latVelIndex][distanceIndex];

		//Get the current best Guess Factor to shoot at
		int bestIndex = (int)((GUESS_FACTORS -1) / 2);
		for (int i = 0; i < stats.length ; i++) {
			if (stats[i] > stats[bestIndex])
				bestIndex = i;
		}

		//Aim the gun
		//Original
		setTurnGunRightRadians(robocode.util.Utils.normalRelativeAngle(eAbsBearing - getGunHeadingRadians() + (getGuessAngle(bestIndex, firePower))));

		//If the gun is cool, fire and shoot a Wave
		if (getGunHeat() == 0 && firePower > 0) {
			setFire(firePower);
			Wave w = new Wave();
			w.guessFactors = stats;
			w.wDirection = eDirection;
			w.eventTime = getTime();
			w.shotOrigin = new Point2D.Double(getX(), getY());
			w.shotPower = firePower;
			w.startingAbsTargetBearing = eAbsBearing;
			w.maxAnglePossible = Math.asin(8D / bulletV(firePower));
			addCustomEvent(w);
		}

		//Housekeeping
		eLastVelocity = eVelocity;
		eLastEnergy = eEnergy;
	}
	
//	public void onHitByBullet(HitByBulletEvent e) {
//		if (!flatten)
//			hits++;
//		flatten = true;
//	}

	// Check if we are hit with full lead aim
	// Adapted from Axe's Musashi: http://robowiki.net/?Musashi
	public void onHitByBullet(HitByBulletEvent e) {
		if (lastReverseTime > (eDistance + 50) / e.getVelocity() && !flatten) {
			flatten = (++hits > 1);
		}
	}

	//Method to determine if I should wall smooth or not.
	private double getTurnAngle() {
		double turnTo = eBearing + (Math.PI / 2) + (.2618 * direction * (eDistance > MAX_STAND_OFF_DISTANCE ? CLOSE_IN : (eDistance < MIN_STAND_OFF_DISTANCE ? BACK_OFF : 0)));
		if (out(WALL_CURVE_DISTANCE * direction, turnTo) && eDistance > 200) {  //Start smoothing
			//int cDirection = ((getVelocity() * Math.sin(getHeadingRadians() - Math.atan2(getX() - getBattleFieldWidth() / 2, getY() - getBattleFieldHeight() / 2))) > 0 ? 1 : -1);
			double test;
			//No "diving" in
			if (Math.abs(Math.sin(eBearing)) < .500 && flatten) {		//.500 == sin 30 degrees
				reverse();
			//Hug the wall.
			} else if (!out(WALL_CURVE_DISTANCE * direction, getHeadingRadians())) {
				return 0;	//Moving along the wall
			//Smooth into it
			} else {
				return (BEND_ANGLE * -direction);
			}
		} //End smoothing
		return turnTo;
	}

	//Reverse my direction
	public void reverse() {
		lastReverseTime = getTime();
		direction = -direction;
	}

	// Save the data if I win the round
	public void onWin(WinEvent e) {
		try {
			ObjectOutputStream  oos = new ObjectOutputStream(new GZIPOutputStream(new RobocodeFileOutputStream(getDataFile(targetName))));
			oos.writeObject(statBuffer);
			oos.close();
		} catch (IOException ex) {
			//Should do something here but alas it is too big
			//e.printStackTrace();
		}
	}

	// Figures the Guess Angle Radians to offset the gun by
	private double getGuessAngle(int guessIndex, double firePower) {
		if (eEnergy == 0.0) //If the enemy bot is disabled, fire directly at it.
			return 0.0;
		return (guessIndex - ((GUESS_FACTORS - 1) / 2)) / ((GUESS_FACTORS - 1) / 2) * Math.asin(8.0 / bulletV(firePower)) * eDirection;
	}

	// Method to read data from disc. Taken from SandboxMini
	Object readObject(String fileName) {
		try {
			ObjectInputStream ois = new ObjectInputStream(new GZIPInputStream(new FileInputStream(getDataFile(fileName))));
			Object o = ois.readObject();
			ois.close();
			return o;
		} catch (Exception e) {
			return new int[3][3][11][(int)GUESS_FACTORS];
			//Should do something here but alas it is too big
			//e.printStackTrace();
		}
	}
	
	//Retrieve the bullets velocity
	private double bulletV (double shotPower) {
		return (20 - (3 * shotPower));
	}
	
	//Stolen from kawigi and modified to make it smaller
	public boolean out(double c, double angle) {
		//double angle;
		return !(field.contains(Math.sin(angle = getHeadingRadians())*c+getX(), Math.cos(angle)*c+getY()));
	}
	
	//Stolen from kawigi
	//private double distanceFromCorner() {
	//private boolean cornered() {
	//	double x, y;
	//	return Point2D.distance(0, 0, Math.min((x = getX()), getBattleFieldWidth()-x), Math.min((y = getY()), getBattleFieldHeight()-y)) < 180;
	//}

	//Test for closeness to wall
	//public double wallDist(double x, double y) {
	//	return Math.min(Math.min(x - 18D, getBattleFieldWidth() - 18D - x), Math.min(y - 18D, getBattleFieldHeight() -18D - y));
	//}
	
	// Jekyl's implementation of a Wave (mostly)
	public class Wave extends Condition {
		public Point2D.Double shotOrigin;
		public double shotPower;
		public double startingAbsTargetBearing;
		public double maxAnglePossible;
		public long eventTime;
		public int[] guessFactors;
		public int wDirection;

		public boolean test() {
			if (((getTime() - eventTime) * bulletV(shotPower)) >= shotOrigin.distance(eX,eY)) {
				double currentAbsBearingFromShotOrigin = Math.atan2(eX - shotOrigin.getX(), eY - shotOrigin.getY());
				guessFactors[(int)Math.max(0, Math.min((GUESS_FACTORS - 1),(int) Math.round(((((Utils.normalRelativeAngle(currentAbsBearingFromShotOrigin - startingAbsTargetBearing) * wDirection) / maxAnglePossible) * ((GUESS_FACTORS - 1) / 2)) + ((GUESS_FACTORS - 1) / 2)))))]++;
				removeCustomEvent(this);
			}
			return false;
		}
	}  //End Wave
}

As you can see there is quite a bit of code commented out and you can see some of the variables I am monkeying around with. As always, questions, comments, and feedback are always appreciated.


Robo Home | BlackPearl | Changes | Preferences | AllPages
Edit revision 23 of this page | View other revisions | View current revision
Edited February 17, 2004 3:36 EST by Sparafucil3 (diff)
Search: