/**
* The superclass for all EPGY Robots
*/
package epgy.util;
import java.awt.Graphics2D;
import java.util.*;
import epgy.util.*;
import robocode.*;
import robocode.util.*;
/**
* EPGYBot is the superclass for all of your robots.
* @author Matthew Chun-Lum
*
*/
public abstract class EPGYBot extends AdvancedRobot {
public static final double MAX_RADAR_TRACKING_AMOUNT = Math.PI / 4.0;
public static final int MAX_HISTORY = 100;
protected static ArrayList<EPGYRobotState> states;
protected static HashMap<String, EPGYEnemyRobot> enemies;
protected String eLastScannedBot;
private boolean disableGunCorrection;
private boolean requestedShoot;
private double requestedPower;
private long fireTime;
// abstract methods, must be implemented in subclass
/**
* The setup method is invoked at the beginning of every round.
* This allows you to set certain variables at the beginning of
* every round.
*/
public abstract void setup();
/**
* This method is invoked every time an enemy robot is scanned. You probably
* will not be able to fire every time a robot is scanned due to gun
* cool-down, but you can still update the gun on every tick. It is important
* to realize that a shot is fired on the NEXT tick, so you may have to
* make special adjustments to compensate
* @param enemyName the name of the enemy robot from the current scan
*/
public abstract void doGun(String enemyName);
/**
* This method is invoked every time an enemy robot is scanned. I've taken care
* of spinning the radar for you, but if you want to use more complex
* behavior (like a focusing algorithm), you can put that here. It will
* override the default spinning behavior.
* The philosophy in Robocode is that we react to scanning the enemy robot,
* so the more frequently we can scan another robot, the better/more
* accurate our data will be.
* @param enemyName the name of the enemy robot from the current scan
*/
public abstract void doRadar(String enemyName);
/**
* This method is invoked every time an enemy robot is scanned. Here is
* where you should handle your robot's movement. Movement can be static
* or dynamic: you can have a fixed moving style, or react dynamically
* to another robot.
* @param enemyName the name of the enemy robot from the current scan
*/
public abstract void doMovement(String enemyName);
// public
public void run() {
initialSetup();
setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
setAdjustRadarForRobotTurn(true);
while(getRadarTurnRemainingRadians() == 0) {
turnRadarRightRadians(MAX_RADAR_TRACKING_AMOUNT * 5);
}
}
public void onScannedRobot(ScannedRobotEvent e) {
// update information about ourself
recordState();
// update information about the enemy robot
eLastScannedBot = e.getName();
if(!enemies.containsKey(e.getName()))
enemies.put(e.getName(), new EPGYEnemyRobot(e.getName(), this));
EPGYEnemyRobot enemy = enemies.get(e.getName());
enemy.update(e);
// check to fire guns
if(!disableGunCorrection && requestedShoot && getTime() >= fireTime && getGunTurnRemaining() == 0) {
setFire(requestedPower);
requestedShoot = false;
}
// do user-specified actions
doRadar(eLastScannedBot);
doMovement(eLastScannedBot);
doGun(eLastScannedBot);
}
/**
* Return the current state of your robot. The data structure
* returned is an {@link EPGYRobotState}. If there are no
* states, it returns {@code null}
* @return the current state of your EPGYBot
*/
public EPGYRobotState getCurrentState() {
if(states.isEmpty())
return null;
return states.get(0);
}
/**
* Returns the current location of your EPGYBot in the form
* of an {@link EPGYPoint}
* @return the current location of your EPGYBot
*/
public EPGYPoint getLocation() {
EPGYRobotState state = getCurrentState();
if(state == null)
state = recordState();
return state.location;
}
/**
* Return the width of the battlefield in pixels
* @return the width of the battlefield in pixels
*/
public double getWidth() {
return getBattleFieldWidth();
}
/**
* Return the height of the battlefield in pixels
* @return the height of the battlefield in pixels
*/
public double getHeight() {
return getBattleFieldHeight();
}
// bearing computation
/**
* Computes the bearing in degrees to the specified point
* @param point the desired destination point
* @return the bearing in degrees to the specified point
*/
public double computeBearingToPoint(EPGYPoint point) {
return Math.toDegrees(computeBearingToPointRadians(point));
}
/**
* Computes the bearing in radians to the specified point
* @param point the desired destination point
* @return the bearing in radians to the specified point
*/
public double computeBearingToPointRadians(EPGYPoint point) {
return Utils.normalRelativeAngle(EPGYUtil.computeAbsoluteBearing(getLocation(), point) - getHeadingRadians());
}
// movement control
/**
* Sets the robot to move the specified distance.
* A positive number will move the robot forward,
* and a negative number will move the robot backward.
* @param distance the distance you want to move.
* A negative distance moves the robot backwards.
*/
public void setMove(double distance) {
setAhead(distance);
}
/**
* Returns {@code true} if you are done with the current move
* @return {@code true} if you are done with the current move
*/
public boolean doneWithMove() {
return (getDistanceRemaining() == 0);
}
/**
* Sets the desired turn to the passed angle in degrees.
* A positive number will turn to the right, and a negative
* number will turn to the left.
* @param angle the angle to turn. A positive number
* will turn the robot to the right. A negative
* number will turn the robot to the left.
*/
public void setTurn(double angle) {
setTurnRight(angle);
}
/**
* Sets the desired turn to the passed angle in radians.
* A positive number will turn to the right, and a negative
* number will turn to the left.
* @param angle the angle to turn. A positive number
* will turn the robot to the right. A negative
* number will turn the robot to the left.
*/
public void setTurnRadians(double angle) {
setTurnRightRadians(angle);
}
/**
* Returns {@code true} if you are done with the current turn
* @return {@code true} if you are done with the current turn
*/
public boolean doneWithTurn() {
return (getTurnRemaining() == 0);
}
// gun control
/**
* Returns {@code true} if the gun is cool enough to shoot
* @return {@code true} if the gun is cool enough to shoot
*/
public boolean canShoot() {
return (getGunHeat() - getGunCoolingRate() <= 0);
}
/**
* Will turn the gun and shoot on the next tick
* The minimum power is 0.1 and the maximum is 3.0.
* The angle is the angle to turn the gun in degrees
* @param angle the angle to turn the gun in degrees
* @param power the power of the bullet
*/
public void shootWithAngleAndPower(double angle, double power) {
shootWithAngleAndPowerRadians(Math.toRadians(angle), power);
}
/**
* Will turn the gun and shoot on the next tick
* The minimum power is 0.1 and the maximum is 3.0.
* The angle is the angle to turn the gun in radians
* @param angle the angle to turn the gun in radians
* @param power the power of the bullet
*/
public void shootWithAngleAndPowerRadians(double angle, double power) {
angle = Utils.normalRelativeAngle(angle);
setTurnGunRightRadians(angle);
if(disableGunCorrection) {
setTurnGunRightRadians(angle);
setFire(power);
} else {
requestedShoot = true;
requestedPower = power;
fireTime = getTime() + 1;
}
}
/**
* Set whether the shootWithAngleAndPower methods will
* compensate for the fact that shots happen before the
* gun turns. {@code true} disables the gun correction,
* {@code false} enables the gun correction.
* @param disableGunCorrection
*/
public void setDisableGunCorrection(boolean disableGunCorrection) {
this.disableGunCorrection = disableGunCorrection;
}
// Enemy Information
/**
* Returns the x coordinate of the last scanned enemy
* @return the x coordinate of the last scanned enemy
*/
public double getScannedEnemyX() {
return getEnemyX(eLastScannedBot);
}
/**
* Returns the last known x coordinate of the specified enemy
* @param name the name of the enemy robot
* @return the last known x coordinate of the specified enemy
*/
public double getEnemyX(String name) {
return getEnemyState(name).location.x;
}
/**
* Returns the y coordinate of the last scanned enemy
* @return the y coordinate of the last scanned enemy
*/
public double getScannedEnemyY() {
return getEnemyY(eLastScannedBot);
}
/**
* Returns the last known y coordinate of the specified enemy
* @param name the name of the enemy robot
* @return the last known y coordinate of the specified enemy
*/
public double getEnemyY(String name) {
return getEnemyState(name).location.y;
}
/**
* Returns the location of the scanned enemy robot
* @return the location of the scanned enemy robot
*/
public EPGYPoint getScannedEnemyLocation() {
return getEnemyLocation(eLastScannedBot);
}
/**
* Returns the last known location of the specified enemy robot
* @param name the name of the enemy robot
* @return the last known location of the specified enemy robot
*/
public EPGYPoint getEnemyLocation(String name) {
return getEnemyState(name).location;
}
/**
* Returns the distance to the last scanned enemy
* @return the distance to the last scanned enemy
*/
public double getScannedEnemyDistance() {
return getEnemyDistance(eLastScannedBot);
}
/**
* Returns the last known distance to the specified enemy
* @param name the name of the enemy robot
* @return the last known distance to the specified enemy
*/
public double getEnemyDistance(String name) {
return getEnemyState(name).distance;
}
/**
* Returns the velocity of the scanned enemy robot
* @return the velocity of the last scanned enemy robot
*/
public double getScannedEnemyVelocity() {
return getEnemyVelocity(eLastScannedBot);
}
/**
* Returns the last known velocity of the specified enemy
* @param name the name of the enemy robot
* @return the last known velocity of the specified enemy
*/
public double getEnemyVelocity(String name) {
return getEnemyState(name).velocity;
}
/**
* Returns the heading of the last scanned enemy in degrees
* @return the heading of the last scanned enemy in degrees
*/
public double getScannedEnemyHeading() {
return Math.toDegrees(getScannedEnemyHeadingRadians());
}
/**
* Returns the heading in radians of the last scanned enemy
* @return the heading in radians of the last scanned enemy
*/
public double getScannedEnemyHeadingRadians() {
return getEnemyHeadingRadians(eLastScannedBot);
}
/**
* Returns the last known heading of the specified enemy in degrees
* @param name the name of the enemy robot
* @return the last known heading of the specified enemy in degrees
*/
public double getEnemyHeading(String name) {
return Math.toDegrees(getEnemyHeadingRadians(name));
}
/**
* Return the last known heading of the specified enemy in radians
* @param name the name of the enemy robot
* @return the last known heading of the specified enemy in radians
*/
public double getEnemyHeadingRadians(String name) {
return getEnemyState(name).heading;
}
/**
* Return the bearing to the enemy robot in degrees (from your robot's current heading)
* @return the bearing to the enemy robot in degrees (from your robot's current heading).
* You can think of the bearing as the amount you need to turn to be facing the enemy robot.
*/
public double getScannedEnemyBearing() {
return Math.toDegrees(getScannedEnemyBearingRadians());
}
/**
* Return the bearing to the enemy robot in radians (from your robot's current heading)
* @return the bearing to the enemy robot in radians (from your robot's current heading).
* You can think of the bearing as the amount you need to turn to be facing the enemy robot.
*/
public double getScannedEnemyBearingRadians() {
return getEnemyBearingRadians(eLastScannedBot);
}
/**
* Return the last known bearing to the specified enemy robot in degrees
* @param name the name of the enemy robot
* @return the last known bearing to the enemy robot in degrees (from your robot's current heading).
* You can think of the bearing as the amount you need to turn to be facing the enemy robot.
*/
public double getEnemyBearing(String name) {
return Math.toDegrees(getEnemyBearingRadians(name));
}
/**
* Return the last known bearing to the specified enemy robot in radians
* @param name the name of the enemy robot
* @return the last known bearing to the enemy robot in radians (from your robot's current heading)
* You can think of the bearing as the amount you need to turn to be facing the enemy robot.
*/
public double getEnemyBearingRadians(String name) {
return Utils.normalRelativeAngle(getEnemyState(name).absoluteBearing - getHeadingRadians());
}
/**
* Return the absolute bearing to the last scanned enemy robot in degrees
* @return the absolute bearing to the last scanned enemy robot in degrees.
* The absolute bearing is the amount your robot needs to rotate from 0 to
* face the enemy robot
*/
public double getScannedEnemyAbsoluteBearing() {
return Math.toDegrees(getScannedEnemyAbsoluteBearingRadians());
}
/**
* Return the absolute bearing to the last scanned enemy robot in radians
* @return the absolute bearing to the last scanned enemy robot in radians.
* The absolute bearing is the amount your robot needs to rotate from 0 to
* face the enemy robot
*/
public double getScannedEnemyAbsoluteBearingRadians() {
return getEnemyAbsoluteBearingRadians(eLastScannedBot);
}
/**
* Return the last known absolute bearing to the specified enemy robot in degrees
* @param name the name of the enemy robot
* @return the last known absolute bearing to the specified enemy robot in degrees.
* The absolute bearing is the amount your robot needs to rotate from 0 to
* face the enemy robot
*/
public double getEnemyAbsoluteBearing(String name) {
return Math.toDegrees(getEnemyAbsoluteBearingRadians(name));
}
/**
* Return the last known absolute bearing to the specified enmy robot in radians
* @param name the name of the enemy robot
* @return the last known absolute bearing to the last scanned enemy robot in radians.
* The absolute bearing is the amount your robot needs to rotate from 0 to
* face the enemy robot
*/
public double getEnemyAbsoluteBearingRadians(String name) {
return getEnemyState(name).absoluteBearing;
}
/**
* Return the gun bearing to the scanned enemy robot in degrees
* @return the gun bearing to the scanned enemy robot in degrees
* This is the amount you have to turn your gun to be facing directly at the
* enemy robot.
*/
public double getScannedGunBearingToEnemy() {
return Math.toDegrees(getScannedGunBearingToEnemyRadians());
}
/**
* Return the gun bearing to the scanned enemy robot in radians
* @return the gun bearing to the scanned enemy robot in radians
* This is the amount you have to turn your gun to be facing directly at the
* enemy robot.
*/
public double getScannedGunBearingToEnemyRadians() {
return getGunBearingToEnemyRadians(eLastScannedBot);
}
/**
* Return the last known gun bearing to the specified enemy robot in degrees
* @param name the name of the enemy robot
* @return the last known gun bearing to the specified enemy robot in degrees
* This is the amount you have to turn your gun to be facing directly at the
* enemy robot.
*/
public double getGunBearingToEnemy(String name) {
return Math.toDegrees(getGunBearingToEnemyRadians(name));
}
/**
* Return the last known gun bearing to the specified enemy robot in radians
* @param name the name of the enemy robot
* @return the last known gun bearing to the specified enemy robot in radians.
* This is the amount you have to turn your gun to be facing directly at the
* enemy robot.
*/
public double getGunBearingToEnemyRadians(String name) {
return Utils.normalRelativeAngle(getEnemyAbsoluteBearingRadians(name) - getGunHeadingRadians());
}
/**
* @return the energy of the last scanned enemy robot
*/
public double getScannedEnemyEnergy() {
return getEnemyEnergy(eLastScannedBot);
}
/**
* @param name the name of the enemy robot
* @return the last known energy of the specified enemy robot
*/
public double getEnemyEnergy(String name) {
return getEnemyState(name).energy;
}
// print wrappers
/**
* Prints the passed string in the console and starts a new line
* Wrapper for System.out.println();
* @param str
*/
public void println(String str) {
System.out.println(str);
}
/**
* Prints the passed string to the console
* Wrapper for System.out.print();
* @param str
*/
public void print(String str) {
System.out.print(str);
}
public void onPaint(Graphics2D g) {
// Do nothing, for now
}
// protected
protected EPGYRobotState recordState() {
states.add(0, new EPGYRobotState(this));
if(states.size() > MAX_HISTORY)
states.remove(states.size() - 1);
return states.get(0);
}
protected EPGYEnemyRobot getEnemy(String name) {
return enemies.get(name);
}
protected EPGYRobotState getEnemyState(String name) {
return getEnemy(name).getCurrentState();
}
// private
private void initialSetup() {
if(states == null)
states = new ArrayList<EPGYRobotState>();
if(enemies == null)
enemies = new HashMap<String, EPGYEnemyRobot>();
setup();
}
}