User:Nat/Free code
From RoboWiki
Here I'll put all my code snippets. No license apply.
Contents |
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)
