User:Nat/Free code

From Robowiki
< User:Nat
Revision as of 14:46, 6 June 2009 by Nat (talk | contribs) (add movement predictor)
Jump to navigation Jump to search

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.that.contains.function.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;
		}
	}

	public interface AnglerModifier {
		public double getModifiedAngle(double goAngle,
				PredictionStatus 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
	 * @return predicted state next tick
	 */
	public static PredictionStatus predict(PredictionStatus status,
			double goAngle, double maxVelocity) {
		double x = status.x;
		double y = status.y;
		double heading = status.heading;
		double velocity = status.velocity;

		int moveDir = 1;

		// 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;
			moveDir = -1;
		}

		// 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, Double.POSITIVE_INFINITY
				* moveDir);

		// 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);
	}

	/**
	 * 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);
			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);
			predicted.add(status);

			// Deduct the distance remaining by the velocity
			distanceRemaining -= status.velocity;
		}

		return predicted;
	}

	/**
	 * 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 = Math.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 = Math.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;
	}
}

(more to be come)