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)