/** * Util.java */ package epgy.util; import java.awt.Graphics2D; import robocode.AdvancedRobot; import robocode.util.Utils; /** * This class contains utility methods * @author Matthew Chun-Lum * */ public abstract class EPGYUtil { public static final double WALL_STICK = 150; /** * Will return a random number greater than or equal to 0 and less than 1.0 * @return a random number greater than or equal to 0 and less than 1.0 */ public static double random() { return Math.random(); } /** * Will return an integer greater than or equal to 0 and less than {@code max} * @param max * @return a integer greater than or equal to 0 and less than {@code max} */ public static int random(int max) { return (int) Math.round(Math.random() * max); } /** * Computes the absolute bearing from the source to the target * @param source * @param target * @return the absolute bearing in radians */ public static double computeAbsoluteBearing(EPGYPoint source, EPGYPoint target) { return Utils.normalAbsoluteAngle(Math.atan2(target.x - source.x, target.y - source.y)); } /** * Determines the power of a bullet given its velocity * @param velocity * @return the power of the bullet */ public static double computeBulletPower(double velocity) { return (20.0 - velocity) / 3.0; } /** * Determines the velocity of a bullet given its power * @param bulletPower * @return the velocity of the bullet */ public static double computeBulletVelocity(double bulletPower) { return (20.0 - (3.0 * bulletPower)); } /** * Determines the max escape angle given velocity * @param velocity * @return the max escape angle */ public static double computeMaxEscapeAngle(double velocity) { return Math.asin(8.0 / velocity); } /** * Draws an oval using the passed point and radius * @param point * @param radius * @param g the graphics object reference */ public static void drawOval(EPGYPoint point, int radius, Graphics2D g) { g.drawOval((int) point.x - radius, (int) point.y - radius, radius * 2, radius * 2); } /** * Fills an oval using the passed point and radius * @param point * @param radius * @param g the graphics object reference */ public static void fillOval(EPGYPoint point, int radius, Graphics2D g) { g.fillOval((int) point.x - radius, (int) point.y - radius, radius * 2, radius * 2); } /** * Returns the index of the largest element in {@code arr} * @param arr * @return the index of the largest element in {@code arr} */ public static int indexOfLargest(double[] arr) { double largest = 0; int largestIndex = (arr.length - 1) /2; for(int i = 0; i < arr.length; i++) { if(arr[i] > largest) { largest = arr[i]; largestIndex = i; } } return largestIndex; } /** * Returns the index of the smallest element in {@code arr} * @param arr * @return the index of the smallest element in {@code arr} */ public static int indexOfSmallest(double[] arr) { double lowest = 50000; int lowestIndex = -1; for(int i = 0; i < arr.length; i++) { if(arr[i] < lowest) { lowest = arr[i]; lowestIndex = i; } } return lowestIndex; } /** * Determines if the passed value is within the range * @param min * @param max * @param value * @return {@code true} if {@code value} is between {@code min} and {@code max} */ public static boolean inRange(double min, double max, double value) { return (value >= min && value <= max); } /** * Limits the passed value to the range between the passed max * and min values * @param min * @param value * @param max * @return the limited value */ public static double limit(double min, double value, double max) { return Math.max(min, Math.min(value, max)); } /** * Returns the lowest value from a list of values * @param values * @return the lowest value from the list of values */ public static double lowest(double[] values) { double lowest = values[0]; for(int i = 1; i < values.length; i++) if(lowest > values[i]) lowest = values[i]; return lowest; } /** * Returns {@code 1} if {@code d} is positive, {@code 0} if * {@code d} is zero, and {@code -1} if {@code d} is negative * @param d * @return {@code 1} if {@code d} is positive, {@code 0} if * {@code d} is zero, and {@code -1} if {@code d} is negative */ public static int sign(double d) { return (d < 0 ? -1 : d > 0 ? 1 : 0); } /** * Returns {@code 1} if {@code d} is positive or zero and {@code -1} if {@code d} is negative * @param d * @return {@code 1} if {@code d} is positive or zero and {@code -1} if {@code d} is negative */ public static int nonZeroSign(double d) { if (d < 0) { return -1; } return 1; } /** * Computes the point produced from the projection of {@code source} * using the specified {@code angle} and {@code distance} * @param source the source point * @param angle the angle, in radians, to project by (referenced where 0 is north) * @param distance the distance of the projection * @return the projected point */ public static EPGYPoint project(EPGYPoint source, double angle, double distance) { double x = source.x + Math.sin(angle) * distance; double y = source.y + Math.cos(angle) * distance; return new EPGYPoint(x, y); } /** * Computes a rolling rolling average * @param value * @param newEntry * @param n * @param weighting * @return the averaged value */ public static double rollingAvg(double value, double newEntry, double n, double weighting ) { return (value * n + newEntry * weighting)/(n + weighting); } /** * Rounds the specified value to the passed float precision * @param value * @param precision * @return the rounded value */ public static double roundToPrecision(double value, int precision) { int temp = (int) Math.round((value * Math.max(1, Math.pow(10, precision)))); return ((double) temp) / Math.pow(10, precision); } /** * Scales a given value to fall within the given min and max values. * @param min * @param max * @param minExpected * @param maxExpected * @param value * @return the scaled value */ public static double scaleToRange(double min, double max, double minExpected, double maxExpected, double value) { return scaleToRange(min, max, minExpected, maxExpected, value, 0); } /** * Scales a given value to fall within the given min and max values * @param min * @param max * @param minExpected * @param maxExpected * @param value * @param offset * @return the scaled value */ public static double scaleToRange(double min, double max, double minExpected, double maxExpected, double value, double offset) { double scaleRange = max - min; double valueRange = maxExpected - minExpected; double percentDiff = scaleRange / valueRange; return EPGYUtil.limit(min, (value + offset) * percentDiff, max); } /** * Given an AdvancedRobot (EPGYBot is an extension of AdvancedRobot), * a desired angle to turn to, and a distance to move, this method * determines the quickest way to achieve the desired movement. This * ensures that the turn to the desired angle will be the shortest * (clockwise or counter-clockwise), and the move over the desired * distance will be either forwards or backwards. * Credit: Voidious * @param robot * @param goAngle * @param dist */ public static void setBackAsFront(AdvancedRobot robot, double goAngle, double dist) { double angle = Utils.normalRelativeAngle(goAngle - robot.getHeadingRadians()); if (Math.abs(angle) > (Math.PI/2)) { if (angle < 0) { robot.setTurnRightRadians(Math.PI + angle); } else { robot.setTurnLeftRadians(Math.PI - angle); } robot.setBack(dist); } else { if (angle < 0) { robot.setTurnLeftRadians(-1*angle); } else { robot.setTurnRightRadians(angle); } robot.setAhead(dist); } } /** * Simple wrapper for returning the square of a passed value * @param value * @return the square of the passed value */ public static double square(double value) { return Math.pow(value, 2); } /** * Computes the sum of two vectors * @param arr1 * @param arr2 * @return the sum of the two vectors */ public static double[] sum(double[] arr1, double[] arr2) { double[] sum = new double[arr1.length]; if(arr1.length != arr2.length) { System.err.print("Sum error: arrays not the same length!"); return null; } for(int i = 0; i < arr1.length; i++) sum[i] = arr1[i] + arr2[1]; return sum; } /** * Computes the percent difference between {@code v1} and {@code v2} * @param v1 * @param v2 * @return the percent difference between {@code v1} and {@code v2} */ public static double percentDifference(double v1, double v2) { double sum = (v1 + v2) / 2; if(sum == 0) return 1; return Math.abs((v1 - v2) / sum); } /** * Determines if {@code point} lies on the enemy robot * @param point * @param enemy * @return {@code true} if {@code point} is on the enemy */ public static boolean pointOnRobot(EPGYPoint point, EPGYEnemyRobot enemy) { EPGYPoint enemyLocation = enemy.getCurrentState().location; int radius = EPGYEnemyRobot.BOT_RADIUS; return (point.x >= enemyLocation.x - radius && point.x <= enemyLocation.x + radius) && (point.y >= enemyLocation.y - radius && point.y <= enemyLocation.y + radius); } }