/**
* EnemyRobot.java
*/
package rampancy_old.util;
import robocode.*;
import robocode.util.*;
import java.util.*;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.*;
import rampancy_old.*;
import rampancy_old.management.EnemyListener;
import rampancy_old.statistics.PatternMatchingGunStatistic;
import rampancy_old.statistics.pattern.PMState;
import rampancy_old.util.tree.*;
/**
* This is the utility class that represents an enemy robot
* @author Matthew Chun-Lum
*
*/
public class EnemyRobot {
private String name;
private RampantRobot reference;
private Point2D.Double location;
private Point2D.Double lastLocation;
private double absoluteBearing;
private double lastAbsoluteBearing;
private double heading;
private double lastHeading;
private double energy;
private double lastEnergy;
private double bulletPower;
private double distance;
private double lastDistance;
private double velocity;
private double lastVelocity;
private int moveTimes;
private long timeSinceVelocityChange;
private int directionTraveling;
private int shotsFired;
private EnemyWave lastShot;
private boolean hasFired;
private boolean advancing;
private boolean recordOnFile;
private ArrayList<Double> trackedBearings;
private ArrayList<Integer> trackedDirections;
private ArrayList<EnemyListener> listeners;
private VariationProfile variationProfile;
private ArrayList<PMState> movementLog;
/**
* Just for testing
*/
public EnemyRobot() {}
/**
* Default constructor
* @param e
* @param reference
*/
public EnemyRobot(ScannedRobotEvent e, RampantRobot reference) {
setInitialState(e, reference);
listeners = new ArrayList<EnemyListener>();
recordOnFile = false;
variationProfile = new VariationProfile();
movementLog = new ArrayList<PMState>();
}
/**
* Sets the initial state of an EnemyRobot. May be called at the beginning of each round
* @param e
* @param reference
*/
public void setInitialState(ScannedRobotEvent e, RampantRobot reference) {
this.reference = reference;
name = e.getName();
hasFired = false;
advancing = false;
lastShot = null;
bulletPower = 0;
moveTimes = 0;
directionTraveling = 1;
setAbsoluteBearing(e.getBearingRadians() + reference.getHeadingRadians());
setHeading(e.getHeadingRadians());
setLocation(Util.project(reference.getLocation(), absoluteBearing, e.getDistance()));
setDistance(location.distance(reference.getLocation()));
setVelocity(e.getVelocity());
setEnergy(e.getEnergy());
resetTrackingLists();
}
/**
* Updates the robot
* @param e
*/
public void update(ScannedRobotEvent e) {
double lateralVelocity = reference.getVelocity() * Math.sin(e.getBearingRadians());
setAbsoluteBearing(e.getBearingRadians() + reference.getHeadingRadians());
setHeading(e.getHeadingRadians());
setLocation(Util.project(reference.getLocation(), absoluteBearing, e.getDistance()));
setDistance(getLocation().distance(reference.getLocation()));
setVelocity(e.getVelocity());
updateDirectionTraveling();
// Add the direction of Durandal CW or CCW relative to the robot
int direction = (lateralVelocity >= 0 ? 1 : -1);
trackedDirections.add(0, direction);
// Add the absolute bearing
trackedBearings.add(0, absoluteBearing + Math.PI);
setEnergy(e.getEnergy());
checkFire();
movementLog.add(new PMState(this));
if(movementLog.size() > PatternMatchingGunStatistic.LOG_DEPTH)
movementLog.remove(0);
notifyListeners();
}
public ArrayList<PMState> getMovementLog() {
return movementLog;
}
/**
* @return {@code true} if the robot has fired
*/
public boolean hasFired() {
return hasFired;
}
/**
* @return {@code true} if this robot's record was loaded from a file
*/
public boolean recordOnFile() {
return recordOnFile;
}
public void setRecordOnFile(boolean val) {
recordOnFile = val;
}
// ----------- Getters and Setters ------------- //
public int getShotsFired() {
return shotsFired;
}
public void updateVariationProfile() {
variationProfile.update(this);
}
public VariationProfile getVariationProfile() {
return variationProfile;
}
/**
* @return the last direction that Durandal was to the enemy when the enemy could have shot
*/
public int getLastUsableSurfDirection() {
if(trackedDirections.size() < 3)
return 0;
return trackedDirections.get(2);
}
/**
* @return the last bearing that the enemy could have used to shoot
*/
public double getLastUsableBearing() {
if(trackedBearings.size() < 3)
return 0;
return trackedBearings.get(2);
}
/**
* @return the direction traveling, which is either {@code 1} or {@code -1}
*/
public int getDirectionTraveling() {
return directionTraveling;
}
/**
* Updates the direction traveling based on velocity and bearing
*/
public void updateDirectionTraveling() {
if (getVelocity() != 0) {
if (Math.sin(getHeading()-getAbsoluteBearing()) * getVelocity() < 0)
directionTraveling = -1;
else
directionTraveling = 1;
}
}
/**
* @return the name of the EnemyRobot
*/
public String getName() {
return name;
}
/**
* Sets the name
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the reference to Durandal
* @param reference
*/
public void setReference(RampantRobot reference) {
this.reference = reference;
resetTrackingLists();
}
/**
* @return the current absolute bearing
*/
public double getAbsoluteBearing() {
return absoluteBearing;
}
/**
* Sets the current absolute bearing and updates the last absolute bearing
* @param absoluteBearing
*/
public void setAbsoluteBearing(double absoluteBearing) {
setLastAbsoluteBearing(this.absoluteBearing);
this.absoluteBearing = absoluteBearing;
}
/**
* @return the last absolute bearing
*/
public double getLastAbsoluteBearing() {
return lastAbsoluteBearing;
}
/**
* Sets the last absolute bearing
* @param lastAbsoluteBearing
*/
public void setLastAbsoluteBearing(double lastAbsoluteBearing) {
this.lastAbsoluteBearing = lastAbsoluteBearing;
}
/**
* @return the heading
*/
public double getHeading() {
return heading;
}
/**
* @param heading the heading to set
*/
public void setHeading(double heading) {
setLastHeading(this.heading);
this.heading = heading;
}
/**
* @return the lastHeading
*/
public double getLastHeading() {
return lastHeading;
}
/**
* @param lastHeading the lastHeading to set
*/
public void setLastHeading(double lastHeading) {
this.lastHeading = lastHeading;
}
/**
* @return the distance to this robot
*/
public double getDistance() {
return distance;
}
/**
* Sets the distance
* @param distance
*/
public void setDistance(double distance) {
setLastDistance(this.distance);
this.distance = distance;
}
/**
* @return the last recorded distance to the enemy
*/
public double getLastDistance() {
return lastDistance;
}
/**
* Sets the last distance
* @param lastDistance
*/
public void setLastDistance(double lastDistance) {
this.lastDistance = lastDistance;
}
/**
* @return the number of scans since the velocity was last 0
*/
public int getMoveTimes() {
return moveTimes;
}
/**
* @return the current velocity of the robot
*/
public double getVelocity() {
return velocity;
}
/**
* Sets the velocity of the robot
* @param velocity
*/
public void setVelocity(double velocity) {
setLastVelocity(this.velocity);
this.velocity = velocity;
if(this.velocity == 0)
moveTimes = 0;
else
moveTimes++;
if(this.velocity == lastVelocity)
timeSinceVelocityChange++;
else
timeSinceVelocityChange = 0;
checkForRam();
}
/**
* @return the last velocity of the robot
*/
public double getLastVelocity() {
return lastVelocity;
}
/**
* Sets the last velocity of the robot
* @param lastVelocity
*/
public void setLastVelocity(double lastVelocity) {
this.lastVelocity = lastVelocity;
}
public long getTimeSinceVelocityChange() {
return timeSinceVelocityChange;
}
/**
* @return the location of the EnemyRobot
*/
public Point2D.Double getLocation() {
return location;
}
/**
* Sets the location of the EnemyRobot and updates the lastLocation
* @param location
*/
public void setLocation(Point2D.Double location) {
setLastLocation(this.location);
this.location = location;
// this.location.x -= 2;
// this.location.y += 2;
}
/**
* @return the last location of the EnemyRobot
*/
public Point2D.Double getLastLocation() {
return lastLocation;
}
/**
* Sets the lastLocation of the EnemyRobot
* @param lastLocation
*/
public void setLastLocation(Point2D.Double lastLocation) {
this.lastLocation = lastLocation;
}
/**
* @return the current recorded energy of the EnemyRobot
*/
public double getEnergy() {
return energy;
}
/**
* Sets the current energy of the EnemyRobot and updates the lastEnergy
* @param energy
*/
public void setEnergy(double energy) {
setLastEnergy(this.energy);
this.energy = energy;
}
/**
* @return the lastEnergy of the EnemyRobot
*/
public double getLastEnergy() {
return lastEnergy;
}
/**
* Sets the lastEnergy of the EnemyRobot
* @param lastEnergy
*/
public void setLastEnergy(double lastEnergy) {
this.lastEnergy = lastEnergy;
}
/**
* @return the power the last shot was fired with
*/
public double getBulletPower() {
return bulletPower;
}
/**
* @return an EnemyWave of the last shot fired
*/
public EnemyWave getLastShot() {
EnemyWave temp = lastShot;
lastShot = null;
hasFired = false;
return temp;
}
/**
* Simulates a bullet shot
* @return an enemy wave representing a virtual bullet
*/
public EnemyWave getVirtualBullet() {
EnemyWave wave = new EnemyWave(this, (Point2D.Double) getLocation().clone(), reference, reference.getTime(), 2.5);
wave.setVirtual(true);
return wave;
}
public void draw(Graphics2D g) {
g.setColor(Color.red);
g.fillOval((int) location.x - 2, (int) location.y - 1, 4, 4);
}
// ----------- Listener Code ------------- //
/**
* Notifies the objects listening to the EnemyManager
*/
public void notifyListeners() {
for(EnemyListener listener : listeners)
listener.enemyUpdated(this);
}
/**
* Adds a listener to the list of listeners
* @param listener
*/
public void addListener(EnemyListener listener) {
if(!listeners.contains(listener))
listeners.add(listener);
}
/**
* Removes a listener from the list of listeners
* @param listener
*/
public void removeListener(EnemyListener listener) {
if(listeners.contains(listener))
listeners.remove(listener);
}
/**
* Removes all the listeners from the list of listeners
*/
public void removeAllListeners() {
listeners.clear();
}
// ------------ Private Helpers ------------ //
/**
* Clears the trackedBearings and trackedDirections arrays
*/
private void resetTrackingLists() {
trackedBearings = new ArrayList<Double>();
trackedDirections = new ArrayList<Integer>();
}
/**
* Determines if the robot has fired a bullet
*/
private void checkFire() {
bulletPower = getLastEnergy() - getEnergy();
if(bulletPower < 0 || bulletPower > 3.0 || bulletPower < 0.1) return;
if(trackedDirections.size() < 3) return;
lastShot = new EnemyWave(this, (Point2D.Double) getLocation().clone(), reference, reference.getTime(), bulletPower);
hasFired = true;
shotsFired++;
}
private void checkForRam() {
if(!advancing && Util.isAdvancing(getAbsoluteBearing(), getHeading(), getLastHeading() - getHeading(), getVelocity())) {
lastShot = new EnemyWave(this, (Point2D.Double) getLocation().clone(), reference, reference.getTime(), 0.5);
advancing = true;
hasFired = true;
} else {
advancing = false;
}
}
}