User:Nat/Free code
Here I'll put all my code snippets. No license apply.
Extended Point2D
Here my version of extended Point2D. It can convert vector into location within its constructor and provide angleTo function, which apply to robocode only. I found it useful to keep enemy data in melee with Kawigi style. (class EnemyInfo extends Point2D.Double
)
public class ExtendedPoint2D extends Point2D.Double {
private static final long serialVersionUID = 74324L;
public ExtendedPoint2D(double x, double y) {
super(x, y);
}
public ExtendedPoint2D(Point2D location, double angle, double dist) {
double enemyX = location.getX() + Math.sin(angle) * dist;
double enemyY = location.getY() + Math.cos(angle) * dist;
x = enemyX;
y = enemyY;
}
public void setVectorLocation(double angle, double dist) {
double enemyX = x + Math.sin(angle) * dist;
double enemyY = y + Math.cos(angle) * dist;
x = enemyX;
y = enemyY;
}
public double angleTo(Point2D location) {
double ex = location.getX();
double ey = location.getY();
return Math.atan2(ex - x, ey - y);
}
public double angleTo(double x, double y) {
return angleTo(new Point2D.Double(x, y));
}
}
The variable name is a bit confuse because I apply term 'enemy' where it is only another location, but I can't think of any better name.
XXX State
An incomplete state classes set. This is to be part of Asteroid framework, but the framework isn't ready yet so I released it here for now.
class State {
public static class PlaceState extends Point2D.Double {
private static final long serialVersionUID = -6610510098428272571L;
public int round;
public int time;
public PlaceState(double x, double y, int round, int time) {
super(x, y);
this.round = round;
this.time = time;
}
public PlaceState(double x, double y, int round, long time) {
this(x, y, round, (int) time);
}
public double getBearingTo(PlaceState ps) {
return Math.atan2(ps.x - x, ps.y - y);
}
}
public static class MovingPlaceState extends PlaceState {
private static final long serialVersionUID = 1L;
public double velocity, heading;
public MovingPlaceState(double x, double y, int round, int time,
double heading, double velocity) {
super(x, y, round, time);
this.velocity = velocity;
this.heading = heading;
}
public MovingPlaceState(double x, double y, int round, long time,
double heading, double velocity) {
this(x, y, round, (int) time, heading, velocity);
}
}
public static class RobotState extends MovingPlaceState {
private static final long serialVersionUID = -6759124718664347016L;
public RobotState(double x, double y, int round, int time,
double heading, double velocity) {
super(x, y, round, time, heading, velocity);
}
public RobotState(double x, double y, int round, long time,
double heading, double velocity) {
this(x, y, round, (int) time, heading, velocity);
}
public double getRelativeBearingTo(PlaceState ps) {
return Utils.normalRelativeAngle(getBearingTo(ps) - heading);
}
public double getLateralVelocityOf(RobotState rs) {
return rs.velocity * Math.sin(rs.heading - getBearingTo(rs));
}
public double getAdvancingVelocityOf(RobotState rs) {
return rs.velocity * -Math.cos(rs.heading - getBearingTo(rs));
}
public double getLateralVelocityFrom(RobotState rs) {
return velocity * Math.sin(getBearingTo(rs));
}
public double getAdvancingVelocityFrom(RobotState rs) {
return velocity * Math.cos(getBearingTo(rs));
}
}
}
Description
All method name is self-explaining except the get.......Velocity[From|Of](.....)
. Let me explain here. get.......VelocityOf(.....)
returns a ....... velocity of the state you passed as parameter from this state while get.......VelocityFrom(.....)
method returns a ....... velocity of this state from the state you passed as parameter. (It look silly since you can just switch 2 RobotState references when you call it to obtain other behaviour)
Movement Predictor
- a.k.a. Precise Predictor
My own movement predictor, written up for my new bot. Base loosely off Apollon. Some parts borrowed from fixed Robocode engine.
package nat.util;
import robocode.Rules;
import robocode.util.Utils;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import static path.to.your.util.class.which.has.method.limit;
/**
* Movement Predictor, also known as Precise Predictor
*/
public class MovementPredictor {
public static class PredictionStatus extends Point2D.Double {
private static final long serialVersionUID = 4116202515905711057L;
public final double heading, velocity;
public final long time;
public PredictionStatus(double x, double y, double h, double v, long t) {
super(x, y);
heading = h;
velocity = v;
time = t;
}
}
/**
* Calculate next tick prediction status. This always simulate accelerate to
* max velocity.
*
* @param status
* beginning status
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @return predicted state next tick
*/
public static PredictionStatus predict(PredictionStatus status,
double goAngle) {
return predict(status, goAngle, 8d);
}
/**
* Calculate next tick prediction status. This always simulate accelerate to
* max velocity.
*
* @param status
* beginning status
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @return predicted state next tick
*/
public static PredictionStatus predict(PredictionStatus status,
double goAngle, double maxVelocity) {
int moveDir = 1;
if (Math.cos(goAngle - status.heading) < 0) {
moveDir = -1;
}
return _predict(status, goAngle, maxVelocity, Double.POSITIVE_INFINITY
* moveDir);
}
/**
* Calculate predicted status for every ticks before it reach its
* destination.
*
* @param status
* beginning status
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @param distanceRemaining
* remain distance before stop
* @return list of predicted status
*/
public static List<PredictionStatus> predict(PredictionStatus status,
double goAngle, double maxVelocity, double distanceRemaining) {
List<PredictionStatus> predicted = new ArrayList<PredictionStatus>(20);
predicted.add(status);
while (distanceRemaining > 0) {
status = _predict(status, goAngle, maxVelocity, distanceRemaining);
predicted.add(status);
// Deduct the distance remaining by the velocity
distanceRemaining -= status.velocity;
}
return predicted;
}
/**
* Calculate predicted status for every ticks until timer run out.
*
* @param status
* beginning status
* @param tick
* time available to move
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @return list of predicted status
*/
public static List<PredictionStatus> predict(PredictionStatus status,
int tick, double goAngle, double maxVelocity) {
List<PredictionStatus> predicted = new ArrayList<PredictionStatus>(
tick + 2);
predicted.add(status);
while (tick-- > 0) {
status = predict(status, goAngle, maxVelocity);
predicted.add(status);
}
return predicted;
}
/**
* Calculate predicted status for every ticks before it reach its
* destination, or until timer run out.
*
* @param status
* beginning status
* @param tick
* time available to move
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @param distanceRemaining
* remain distance before stop
* @return list of predicted status
*/
public static List<PredictionStatus> predict(PredictionStatus status,
int tick, double goAngle, double maxVelocity,
double distanceRemaining) {
List<PredictionStatus> predicted = new ArrayList<PredictionStatus>(
tick + 2);
predicted.add(status);
while (distanceRemaining > 0 && tick-- > 0) {
status = _predict(status, goAngle, maxVelocity, distanceRemaining);
predicted.add(status);
// Deduct the distance remaining by the velocity
distanceRemaining -= status.velocity;
}
return predicted;
}
/**
* Calculate next tick prediction status. This always simulate accelerate to
* max velocity.
*
* @param status
* beginning status
* @param goAngle
* angle to move, in radians, absolute
* @param maxVelocity
* max allowed velocity of robot
* @param distanceRemaining
* the remaining distance
* @return predicted state next tick
*/
private static PredictionStatus _predict(PredictionStatus status,
double goAngle, double maxVelocity, double distanceRemaining) {
double x = status.x;
double y = status.y;
double heading = status.heading;
double velocity = status.velocity;
// goAngle here is absolute, change to relative bearing
goAngle -= heading;
// If angle is at back, consider change direction
if (Math.cos(goAngle) < 0) {
goAngle += Math.PI;
}
// Normalize angle
goAngle = Utils.normalRelativeAngle(goAngle);
// Max turning rate, taken from Rules class
double maxTurning = Math.toRadians(10d - 0.75 * velocity);
heading += limit(-maxTurning, goAngle, maxTurning);
// Get next velocity
velocity = getVelocity(velocity, maxVelocity, distanceRemaining);
// Calculate new location
x += Math.sin(heading) * velocity;
y += Math.cos(heading) * velocity;
// return the prediction status
return new PredictionStatus(x, y, heading, velocity, status.time + 1l);
}
/**
* This function return the new velocity base on the maximum velocity and
* distance remaining. This is copied from internal bug-fixed Robocode
* engine.
*
* @param currentVelocity
* current velocity of the robot
* @param maxVelocity
* maximum allowed velocity of the robot
* @param distanceRemaining
* the remaining distance to move
* @return velocity for current tick
*/
private static double getVelocity(double currentVelocity,
double maxVelocity, double distanceRemaining) {
if (distanceRemaining < 0) {
return -getVelocity(-currentVelocity, maxVelocity,
-distanceRemaining);
}
double newVelocity = currentVelocity;
final double maxSpeed = Math.abs(maxVelocity);
final double currentSpeed = Math.abs(currentVelocity);
// Check if we are decelerating, i.e. if the velocity is negative.
// Note that if the speed is too high due to a new max. velocity, we
// must also decelerate.
if (currentVelocity < 0 || currentSpeed > maxSpeed) {
// If the velocity is negative, we are decelerating
newVelocity = currentSpeed - Rules.DECELERATION;
// Check if we are going from deceleration into acceleration
if (newVelocity < 0) {
// If we have decelerated to velocity = 0, then the remaining
// time must be used for acceleration
double decelTime = currentSpeed / Rules.DECELERATION;
double accelTime = (1 - decelTime);
// New velocity (v) = d / t, where time = 1 (i.e. 1 turn).
// Hence, v = d / 1 => v = d
// However, the new velocity must be limited by the max.
// velocity
newVelocity = Math.min(maxSpeed, Math.min(Rules.DECELERATION
* decelTime * decelTime + Rules.ACCELERATION
* accelTime * accelTime, distanceRemaining));
// Note: We change the sign here due to the sign check later
// when returning the result
currentVelocity *= -1;
}
} else {
// Else, we are not decelerating, but might need to start doing so
// due to the remaining distance
// Deceleration time (t) is calculated by: v = a * t => t = v / a
final double decelTime = currentSpeed / Rules.DECELERATION;
// Deceleration time (d) is calculated by: d = 1/2 a * t^2 + v0 * t
// + t
// Adding the extra 't' (in the end) is special for Robocode, and v0
// is the starting velocity = 0
final double decelDist = 0.5 * Rules.DECELERATION * decelTime
* decelTime + decelTime;
// Check if we should start decelerating
if (distanceRemaining <= decelDist) {
// If the distance < max. deceleration distance, we must
// decelerate so we hit a distance = 0
// Calculate time left for deceleration to distance = 0
double time = distanceRemaining / (decelTime + 1); // 1 is added
// here due
// to the extra 't'
// for Robocode
// New velocity (v) = a * t, i.e. deceleration * time, but not
// greater than the current speed
if (time <= 1) {
// When there is only one turn left (t <= 1), we set the
// speed to match the remaining distance
newVelocity = M.max(currentSpeed - Rules.DECELERATION,
distanceRemaining);
} else {
// New velocity (v) = a * t, i.e. deceleration * time
newVelocity = time * Rules.DECELERATION;
if (currentSpeed < newVelocity) {
// If the speed is less that the new velocity we just
// calculated, then use the old speed instead
newVelocity = currentSpeed;
} else if (currentSpeed - newVelocity > Rules.DECELERATION) {
// The deceleration must not exceed the max.
// deceleration.
// Hence, we limit the velocity to the speed minus the
// max. deceleration.
newVelocity = currentSpeed - Rules.DECELERATION;
}
}
} else {
// Else, we need to accelerate, but only to max. velocity
newVelocity = M
.min(currentSpeed + Rules.ACCELERATION, maxSpeed);
}
}
// Return the new velocity with the correct sign. We have been working
// with the speed, which is always positive
return (currentVelocity < 0) ? -newVelocity : newVelocity;
}
}
Utility
This is my utility class I use for a while now, basically it is FastTrig with other implementations as all-in-one. Enjoy!
package nat.util;
import java.awt.geom.Point2D;
/**
* M - combination of FastTrig, java.lang.Math and robocode.util.Utils
*
* The trig section was created by Alexander Schultz, and improved by Julian Kent.
*
* The inverse trig section was first created by Julian Kent,
* and later improved by Nat Pavasant and Starrynte.
*
* The angle normalization is originally robocode.util.Utils'
* so it is Matthew's and Flemming's.
*
* Other parts was created by Nat Pavasant to use in his robot.
*
* @author Alexander Schultz (a.k.a. Rednaxela)
* @author Julian Kent (a.k.a. Skilgannon)
* @author Flemming N. Larsen
* @author Mathew A. Nelson
* @author Nat Pavasant
*/
public final class M {
public static final double PI = 3.1415926535897932384626433832795D;
public static final double TWO_PI = 6.2831853071795864769252867665590D;
public static final double HALF_PI = 1.5707963267948966192313216916398D;
public static final double QUARTER_PI = 0.7853981633974483096156608458199D;
public static final double THREE_OVER_TWO_PI = 4.7123889803846898576939650749193D;
/* Setting for trig */
private static final int TRIG_DIVISIONS = 8192; /* Must be power of 2 */
private static final int TRIG_HIGH_DIVISIONS = 131072; /* Must be power of 2 */
private static final double K = TRIG_DIVISIONS / TWO_PI;
private static final double ACOS_K = (TRIG_HIGH_DIVISIONS - 1) / 2;
private static final double TAN_K = TRIG_HIGH_DIVISIONS / PI;
/* Lookup tables */
private static final double[] sineTable = new double[TRIG_DIVISIONS];
private static final double[] tanTable = new double[TRIG_HIGH_DIVISIONS];
private static final double[] acosTable = new double[TRIG_HIGH_DIVISIONS];
/* Hide the constructor */
private M() {}
/**
* Initializing the lookup table
*/
public static final void init() {
for (int i = 0; i < TRIG_DIVISIONS; i++) {
sineTable[i] = Math.sin(i / K);
}
for (int i = 0; i < TRIG_HIGH_DIVISIONS; i++) {
tanTable[i] = Math.tan(i / TAN_K);
acosTable[i] = Math.acos(i / ACOS_K - 1);
}
}
/* Fast and reasonable accurate trig functions */
public static final double sin(double value) { return sineTable[(int) (((value * K + 0.5) % TRIG_DIVISIONS + TRIG_DIVISIONS)) & (TRIG_DIVISIONS - 1)]; }
public static final double cos(double value) { return sineTable[(int) (((value * K + 0.5) % TRIG_DIVISIONS + 1.25 * TRIG_DIVISIONS)) & (TRIG_DIVISIONS - 1)]; }
public static final double tan(double value) { return tanTable[(int) (((value * TAN_K + 0.5) % TRIG_HIGH_DIVISIONS + TRIG_HIGH_DIVISIONS)) & (TRIG_HIGH_DIVISIONS - 1)]; }
public static final double asin(double value) { return HALF_PI - acos(value); }
public static final double acos(double value) { return acosTable[(int) (value * ACOS_K + (ACOS_K + 0.5))]; }
public static final double atan(double value) { return (value >= 0 ? acos(1 / sqrt(value * value + 1)) : -acos(1 / sqrt(value * value + 1))); }
public static final double atan2(double x, double y) { return (x >= 0 ? acos(y / sqrt(x * x + y * y)) : -acos(y / sqrt(x * x + y * y))); }
/* Redirect to Math class (faster version not available) */
public static final double sqrt(double x) { return Math.sqrt(x); }
public static final double cbrt(double x) { return Math.cbrt(x); }
public static final double pow(double x, double a) { return Math.pow(x, a); }
/* Other implementation */
public static final double round(double x) { return (double) ( (int) (x + 0.5) ); }
public static final double floor(double x) { return (double) ( (int) x); }
public static final double ceil(double x) { return (double) ( (int) (x + 1d) ); }
public static final double sign(double x) { return x > 0 ? 1 : -1; }
public static final double signum(double x) { return x == 0 ? 0 : x > 0 ? 1 : -1; }
public static final double abs(double x) { return x < 0 ? -x : x; }
public static final double sqr(double x) { return x * x; }
public static final double cbr(double x) { return x * x * x; }
public static final double qur(double x) { return x * x * x * x; }
public static final double max(double a, double b) { return a > b ? a : b; }
public static final double min(double a, double b) { return a < b ? a : b; }
public static final double max(double a, double b, double c) { return max(c, max(a, b)); }
public static final double min(double a, double b, double c) { return min(c, min(a, b)); }
public static final double max(double a, double b, double c, double d) { return max(max(c, d), max(a, b)); }
public static final double min(double a, double b, double c, double d) { return min(min(c, d), min(a, b)); }
public static final double limit(double a, double b, double c) { return max(a, min(b, c)); }
public static final double round(double value, int nearest) { return Math.round(value / (double) nearest) * (double) nearest; }
public static final double getAngle(Point2D.Double source, Point2D.Double target) { return atan2(target.getY() - source.getX(), target.getY() - source.getY()); }
public static final boolean inRenge(double a, double b, double c) { return a <= b && b <= c; }
public static final Point2D project(Point2D point, double angle, double distance) { return new Point2D.Double(point.getX() + distance * sin(angle), point.getY() + distance * cos(angle)); }
/* from robocode.util.Utils */
public static final double NEAR_DELTA = .00001;
public static final double normalAbsoluteAngle(double angle) { return (angle %= TWO_PI) >= 0 ? angle : (angle + TWO_PI); }
public static final double normalRelativeAngle(double angle) { return (angle %= TWO_PI) >= 0 ? (angle < PI) ? angle : angle - TWO_PI : (angle >= -PI) ? angle : angle + TWO_PI; }
public static final boolean isNear(double value1, double value2) { return (abs(value1 - value2) < NEAR_DELTA); }
}
(more to be come)