Difference between revisions of "Darkcanuck/VelocityTest"
Jump to navigation
Jump to search
Darkcanuck (talk | contribs) (uses right version now...) |
RednaxelaBot (talk | contribs) m (Using <syntaxhighlight>.) |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
− | Some | + | Some code to test 1.7.1.3's updated getNewVelocity() method vs a few wiki-contributed versions. |
The main class: | The main class: | ||
− | < | + | <syntaxhighlight> |
import robocode.Rules; | import robocode.Rules; | ||
Line 9: | Line 9: | ||
private static Commands currentCommands = new Commands(); | private static Commands currentCommands = new Commands(); | ||
+ | private static boolean doPrint = false; | ||
public static void main(String[] args) { | public static void main(String[] args) { | ||
Line 15: | Line 16: | ||
System.out.println("\nDarkcanuck's VelocityTest\n"); | System.out.println("\nDarkcanuck's VelocityTest\n"); | ||
− | if (!args[0].equals("all")) { | + | if (!args[0].equals("all") && !args[0].equals("print")) { |
− | + | doPrint = true; | |
+ | double velocity = (args.length>0) ? Double.valueOf(args[0]) : 0.0; | ||
+ | double distance = (args.length>1) ? Double.valueOf(args[1]) : 0.0; | ||
+ | double maxvel = (args.length>2) ? Double.valueOf(args[2]) : -1.0; | ||
+ | int exptime = (args.length>3) ? Integer.valueOf(args[3]) : 0; | ||
+ | testCase(velocity, distance, maxvel, exptime); | ||
} else { | } else { | ||
− | testCase(0, 6); | + | if (args[0].equals("print")) |
+ | doPrint = true; | ||
+ | |||
+ | testCase( 8.0, 22.8, -1, 5); // Simonton's caveat #1 | ||
+ | testCase( 8.0, 22.5, -1, 5); // Simonton's caveat #2 | ||
+ | testCase( 8.0, -200.0, 7.0, 39); // Simonton's caveat #3 | ||
+ | |||
+ | testCase( 0.0, 6.0, -1, 4); // overshoot | ||
+ | testCase( 0.0, 10.0, -1, 6); // overshoots twice | ||
+ | testCase( 4.0, 0.0, -1, 4); // second, unnecessary overshoot | ||
+ | testCase(-1.9, 10.0, -1, 6); // impossible decel/accel! | ||
} | } | ||
} | } | ||
− | public static void testCase(double velocity, double distance) { | + | public static void testCase(double velocity, double distance, double maxvel, int exptick) { |
+ | |||
+ | StringBuffer b = new StringBuffer(); | ||
+ | boolean passed = true; | ||
+ | |||
double newvel = velocity; | double newvel = velocity; | ||
double newdist = distance; | double newdist = distance; | ||
+ | double maximum = (maxvel>=0) ? maxvel : Rules.MAX_VELOCITY; | ||
+ | |||
int tick = 0; | int tick = 0; | ||
+ | int maxtick = (exptick>0) ? exptick : (int) Math.max(Math.abs(distance), 100); | ||
+ | |||
+ | b.append("Starting velocity=" + velocity + " distance=" + distance + | ||
+ | ((maxvel>=0)? " max=" + maxvel : "") + "\n"); | ||
− | + | currentCommands.setMaxVelocity(maximum); | |
while ((Math.abs(newvel)>0.00001) || (Math.abs(newdist)>0.00001)) { | while ((Math.abs(newvel)>0.00001) || (Math.abs(newdist)>0.00001)) { | ||
− | newvel = | + | // test next velocity change |
− | newdist -= | + | //double nextvel = orig_getNewVelocity(newvel, newdist); |
+ | //double nextvel = void_getNewVelocity(newvel, newdist); | ||
+ | //double nextvel = skil_getNewVelocity(newvel, newdist); | ||
+ | //double nextvel = posi_getNewVelocity(newvel, newdist); | ||
+ | double nextvel = vopo_getNewVelocity(newvel, newdist); | ||
+ | passed &= checkAccel(nextvel, newvel) && checkMax(nextvel, maximum); | ||
+ | |||
+ | // apply changes | ||
+ | newvel = nextvel; | ||
+ | newdist -= nextvel; | ||
tick++; | tick++; | ||
− | + | b.append(" " + tick + " velocity=" + newvel + " remain=" + newdist + "\n"); | |
+ | |||
+ | // check for infinite loops | ||
+ | if (tick > 2*maxtick) | ||
+ | break; | ||
} | } | ||
− | System.out.println("\n"); | + | |
+ | // check time taken | ||
+ | passed &= (tick-1 <= maxtick); | ||
+ | |||
+ | if (!passed) | ||
+ | b.append(" FAIL\n"); | ||
+ | |||
+ | if (!passed || doPrint) | ||
+ | System.out.println(b.toString() + "\n"); | ||
+ | } | ||
+ | |||
+ | private static boolean checkAccel(double newvel, double oldvel) { | ||
+ | double reverse = (oldvel<0) ? -1.0 : 1.0; | ||
+ | double min = oldvel - reverse*Rules.DECELERATION; | ||
+ | double max = oldvel + reverse*Rules.ACCELERATION; | ||
+ | if (oldvel==0) | ||
+ | min = oldvel - reverse*Rules.ACCELERATION; | ||
+ | |||
+ | if (oldvel<0) | ||
+ | return (newvel>=max) && (newvel<=min); | ||
+ | else | ||
+ | return (newvel<=max) && (newvel>=min); | ||
+ | } | ||
+ | private static boolean checkMax(double newvel, double maxvel) { | ||
+ | return (Math.abs(newvel) <= Math.abs(maxvel)); | ||
} | } | ||
− | private static double | + | /****************** ORIGINAL FROM ROBOCODE 1.7.1.3 *********************/ |
+ | private static double orig_getNewVelocity(double velocity, double distance) { | ||
// If the distance is negative, then change it to be positive and change the sign of the input velocity and the result | // If the distance is negative, then change it to be positive and change the sign of the input velocity and the result | ||
if (distance < 0) { | if (distance < 0) { | ||
− | return - | + | return -orig_getNewVelocity(-velocity, -distance); |
} | } | ||
Line 113: | Line 177: | ||
// Return the new velocity with the correct sign. We have been working with the speed, which is always positive | // Return the new velocity with the correct sign. We have been working with the speed, which is always positive | ||
return (velocity < 0) ? -newVelocity : newVelocity; | return (velocity < 0) ? -newVelocity : newVelocity; | ||
− | } | + | } |
− | } | + | |
− | </ | + | |
+ | /************************* FROM Voidious *******************************/ | ||
+ | private static double void_getNewVelocity(double velocity, double distance) { | ||
+ | // If the distance is negative, then change it to be positive and change the sign of the input velocity and the result | ||
+ | if (distance < 0) { | ||
+ | return -void_getNewVelocity(-velocity, -distance); | ||
+ | } | ||
+ | |||
+ | double newVelocity; | ||
+ | |||
+ | // Get the speed, which is always positive (because it is a scalar) | ||
+ | final double speed = Math.abs(velocity); | ||
+ | |||
+ | if (velocity < 0) { | ||
+ | // Check if we are decelerating, i.e. if the velocity is negative. | ||
+ | newVelocity = speed - 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 = speed / 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(Rules.ACCELERATION * accelTime, distance); | ||
+ | |||
+ | // Note: We change the sign here due to the sign check later when returning the result | ||
+ | velocity *= -1; | ||
+ | } | ||
+ | } else { | ||
+ | // Deceleration distance (d) is calculated iteratively due to Robocode's | ||
+ | // discrete time system. | ||
+ | final double decelDist = void_decelDistance(speed); | ||
+ | |||
+ | // Deceleration ticks is the number of ticks it will take to get to | ||
+ | // zero velocity. | ||
+ | final long decelTime = Math.round( // VOIDIOUS: for rounding errors? maybe unnecessary | ||
+ | Math.ceil((speed - Rules.DECELERATION) / Rules.DECELERATION)); | ||
+ | |||
+ | // The maximum distance coverable with an equivalent decelTime | ||
+ | final double decelTimeMaxDist = ((decelTime + 1) / 2.0) * decelTime // sum of 1..decelTime | ||
+ | * Rules.DECELERATION; | ||
+ | |||
+ | if (distance <= Rules.DECELERATION) { | ||
+ | // If we can cover remaining distance and then completely stop, | ||
+ | // set speed = distance | ||
+ | newVelocity = Math.max(speed - Rules.DECELERATION, distance); | ||
+ | } else if (distance <= decelTimeMaxDist) { | ||
+ | // If we can cover distance in decelTime, split any extra | ||
+ | // distance (between decelDist and distance) over decelTime | ||
+ | // ticks | ||
+ | newVelocity = speed - Rules.DECELERATION + | ||
+ | ((distance - decelDist) / decelTime); | ||
+ | } else { | ||
+ | // If we need more than decelTime ticks, try to spread the | ||
+ | // extra distance over (decelTime + 1) ticks. This will just max | ||
+ | // the acceleration if it needs to (ie, if we need more ticks). | ||
+ | // VOIDIOUS: I think this part would break if Rules.ACCELERATION | ||
+ | // were set above Rules.DECELERATION; we might need an | ||
+ | // extra case or something. Doh. =( | ||
+ | newVelocity = Math.min(speed + Rules.ACCELERATION, | ||
+ | (decelTime * Rules.DECELERATION) + | ||
+ | ((distance - decelTimeMaxDist) / Math.max(decelTime + 1, 2))); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // VOIDIOUS: I think it makes more sense to do this here; no need to decelerate maximally | ||
+ | // if you don't need to to accomodate a new setMaxVelocity. | ||
+ | newVelocity = Math.max(speed - Rules.DECELERATION, | ||
+ | Math.min(speed + Rules.ACCELERATION, | ||
+ | Math.min(newVelocity, currentCommands.getMaxVelocity()))); | ||
+ | |||
+ | // Return the new velocity with the correct sign. We have been working with the speed, which is always positive | ||
+ | return (velocity < 0) ? -newVelocity : newVelocity; | ||
+ | } | ||
+ | |||
+ | private static final double void_decelDistance(double speed){ | ||
+ | double distance = 0; | ||
+ | while(speed > 0){ | ||
+ | speed = Math.max(0,speed - Rules.DECELERATION); | ||
+ | distance += speed; | ||
+ | } | ||
+ | return distance; | ||
+ | } | ||
+ | |||
+ | /**************************** FROM Skilgannon *********************************/ | ||
+ | private static double skil_getNewVelocity(double velocity, double distance) { | ||
+ | |||
+ | if (distance < 0) | ||
+ | return -skil_getNewVelocity(-velocity, -distance); | ||
+ | // If the distance is negative, then change it to be positive | ||
+ | // and change the sign of the input velocity and the result | ||
+ | |||
+ | double maxVel = currentCommands.getMaxVelocity(); | ||
+ | if (velocity < 0) | ||
+ | return Math.min(velocity + Rules.DECELERATION, maxVel); | ||
+ | //we want to go in the opposite direction, so decelerate | ||
+ | |||
+ | else{ | ||
+ | //we are going in the direction we want, test what happens if we accelerate | ||
+ | |||
+ | double accel = Math.min(Rules.ACCELERATION, maxVel - velocity); | ||
+ | //speed up, but not more than max velocity. decel if maxVel is less than velocity. | ||
+ | |||
+ | accel = Math.max(accel, -Rules.DECELERATION); | ||
+ | //prevent excess deceleration due to bot lowering the maxVel while velocity is high | ||
+ | |||
+ | if (distance > skil_decelDistance(velocity + accel)) | ||
+ | return velocity + accel; | ||
+ | else if(accel != 0 && distance > skil_decelDistance(velocity)) | ||
+ | return velocity; | ||
+ | else{ | ||
+ | if(distance < Rules.DECELERATION | ||
+ | //we'll be able to cover remaining distance in 1 tick and then decel to stop | ||
+ | |||
+ | && velocity - distance <= Rules.DECELERATION | ||
+ | //and our velocity is low enough for us to get to that required velocity | ||
+ | ) | ||
+ | return Math.min(distance, maxVel); | ||
+ | //choose the velocity to cover all remaining distance | ||
+ | |||
+ | return Math.max(-maxVel, velocity - Rules.DECELERATION); | ||
+ | //velocity > 0 and we are close enough, so decelerate. | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | /** | ||
+ | * Returns the linear distance it would take to decelerate from a given positive velocity | ||
+ | * | ||
+ | * @param velocity the positive velocity from which to test | ||
+ | * @return the linear distance required to decelerate to a standstill | ||
+ | */ | ||
+ | private static final double skil_decelDistance(double velocity){ | ||
+ | double distance = 0; | ||
+ | while(velocity > 0){ | ||
+ | distance += velocity; | ||
+ | velocity = Math.max(0,velocity - Rules.DECELERATION); | ||
+ | } | ||
+ | return distance; | ||
+ | } | ||
+ | |||
+ | /******************** FROM Positive ********************/ | ||
+ | static double posi_getNewVelocity(double velocityArg, double distanceArg) | ||
+ | { | ||
+ | double velocity; | ||
+ | double distance; | ||
+ | |||
+ | // Make sure the remaining distance is always positive or zero | ||
+ | // (we switch this back later) | ||
+ | if(distanceArg<0) | ||
+ | { | ||
+ | velocity=-velocityArg; | ||
+ | distance=-distanceArg; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | velocity=velocityArg; | ||
+ | distance=distanceArg; | ||
+ | } | ||
+ | |||
+ | // Get the next most positive velocity the robot can reach for next turn | ||
+ | double mostPositiveReachableVelocity = posi_VelocityToMostPositiveVelocity(velocity); | ||
+ | // Get the next most negative velocity the robot can reach for next turn | ||
+ | double mostNegativeReachableVelocity = posi_VelocityToMostNegativeVelocity(velocity); | ||
+ | |||
+ | |||
+ | double highestWantedVelocity = Math.min(currentCommands.getMaxVelocity(), posi_maxSpeedToStopInDisp(distance)); | ||
+ | |||
+ | // The real next velocity is limited by what is actually reachable | ||
+ | double nextVelocity = Math.min(mostPositiveReachableVelocity,Math.max(mostNegativeReachableVelocity,highestWantedVelocity )); | ||
+ | |||
+ | // Switch return value back if needed | ||
+ | if(distanceArg<0) | ||
+ | nextVelocity = -nextVelocity; | ||
+ | |||
+ | return nextVelocity; | ||
+ | } | ||
+ | |||
+ | |||
+ | static public double posi_VelocityToMostPositiveVelocity(double velocity) | ||
+ | { | ||
+ | // Returns the most positive reachable velocity from the | ||
+ | // specified velocity in one turn | ||
+ | if(velocity>0) | ||
+ | { | ||
+ | double returnVelocity = velocity+Rules.ACCELERATION; | ||
+ | if(returnVelocity>Rules.MAX_VELOCITY) | ||
+ | return Rules.MAX_VELOCITY; | ||
+ | else | ||
+ | return returnVelocity; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | double returnVelocity = velocity+Rules.DECELERATION; | ||
+ | if(returnVelocity>Rules.ACCELERATION) | ||
+ | return Rules.ACCELERATION; | ||
+ | else | ||
+ | return returnVelocity; | ||
+ | } | ||
+ | } | ||
+ | static public double posi_VelocityToMostNegativeVelocity(double velocity) | ||
+ | { | ||
+ | // Returns the most negative reachable velocity from the | ||
+ | // specified velocity in one turn | ||
+ | if(velocity<0) | ||
+ | { | ||
+ | double returnVelocity = velocity-Rules.ACCELERATION; | ||
+ | if(returnVelocity<-Rules.MAX_VELOCITY) | ||
+ | return -Rules.MAX_VELOCITY; | ||
+ | else | ||
+ | return returnVelocity; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | double returnVelocity = velocity-Rules.DECELERATION; | ||
+ | if(returnVelocity<-Rules.ACCELERATION) | ||
+ | return -Rules.ACCELERATION; | ||
+ | else | ||
+ | return returnVelocity; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | static private final double[] dispStopArray = | ||
+ | {0.0000,1.0000,2.0000,2.5000,3.0000, | ||
+ | 3.5000,4.0000,4.3333,4.6666,5.0000, | ||
+ | 5.3333,5.6666,6.0000,6.2500,6.5000, | ||
+ | 6.7500,7.0000,7.2500,7.5000,7.7500}; | ||
+ | static public double posi_maxSpeedToStopInDisp(double displacement) | ||
+ | { | ||
+ | // Returns the biggest velocity the robot could go to this turn, | ||
+ | // still being able to stop without overshooting. (Or if | ||
+ | // remaining displacement is less than 2, returns that) | ||
+ | // This routine could be improved to match up with robocode's | ||
+ | // older velocity selecting rules. | ||
+ | if(displacement>=0) | ||
+ | { | ||
+ | if(displacement>=20) | ||
+ | return 8.0; | ||
+ | else if(displacement<=2) | ||
+ | return displacement; | ||
+ | else | ||
+ | return dispStopArray[(int)Math.floor(displacement)]; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return 0; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | /****************** FROM Voidious + Positive ***********************/ | ||
+ | public static double vopo_getNewVelocity(double velocity, double distance) { | ||
+ | if(distance<0) | ||
+ | return -vopo_getNewVelocity(-velocity,-distance); | ||
+ | double highestVelocity = vopo_getMaxVelocity(distance); // highest velocity without overshooting | ||
+ | double wantedVelocity = Math.min(highestVelocity,8.0); | ||
+ | // the actually wanted velocity by the robot is the highest possible, | ||
+ | // limited by what the robot set by the setMaxVelocity command | ||
+ | return vopo_getClosestReachableVelocityToVelocity(velocity, wantedVelocity); | ||
+ | // return whatever is closest to that velocity | ||
+ | } | ||
+ | |||
+ | public static double vopo_getClosestReachableVelocityToVelocity(double currentVelocity,double wantedVelocity) | ||
+ | { | ||
+ | // this function assumes wantedVelocity<=Rules.MAXVELOCITY | ||
+ | // with this function you can basically assume setAhead(Infinity) or setAhead(-Infinity) | ||
+ | // was called, and you determine the next velocity based on the max velocity | ||
+ | // set by the robot. For example, if the current velocity is 0 and the max velocity | ||
+ | // set was 4.0, it would return 1.0. If the current velocity was 8.0, it would return 6.0. | ||
+ | if(wantedVelocity<0) | ||
+ | return -vopo_getClosestReachableVelocityToVelocity(-currentVelocity,-wantedVelocity); | ||
+ | if(currentVelocity<0) | ||
+ | { | ||
+ | double nextVelocity; | ||
+ | // we are travelling the wrong way, decelerate | ||
+ | nextVelocity = currentVelocity + Rules.DECELERATION; | ||
+ | if(nextVelocity>Rules.ACCELERATION) | ||
+ | // make sure we can't jump from -0.1 to 1.9 or something | ||
+ | nextVelocity = Rules.ACCELERATION; | ||
+ | if(nextVelocity>wantedVelocity) | ||
+ | // if the wanted velocity is for example 0.5, limit the velocity to that. | ||
+ | return wantedVelocity; | ||
+ | else | ||
+ | // else return the highest possible | ||
+ | return nextVelocity; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(currentVelocity>wantedVelocity) | ||
+ | { | ||
+ | // both velocities are positive, but we need to decelerate | ||
+ | double nextVelocity = currentVelocity - Rules.DECELERATION; | ||
+ | if(nextVelocity<wantedVelocity) | ||
+ | // if we can decelerate more than what's wanted, return what's wanted | ||
+ | return wantedVelocity; | ||
+ | else | ||
+ | // else return the closest to it | ||
+ | return nextVelocity; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // the wantedVelocity is higher than current | ||
+ | double nextVelocity = currentVelocity + Rules.ACCELERATION; | ||
+ | if(nextVelocity>wantedVelocity) | ||
+ | // if we can accelerate more than what's wanted, return what's wanted | ||
+ | return wantedVelocity; | ||
+ | else | ||
+ | // else return the closest to it | ||
+ | return nextVelocity; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public static double vopo_getMaxVelocity(double distance) | ||
+ | { | ||
+ | long decelTime = vopo_decelTime(distance); | ||
+ | double decelDist = (decelTime / 2.0) * (decelTime-1) // sum of 0..(decelTime-1) | ||
+ | * Rules.DECELERATION; | ||
+ | |||
+ | return ((decelTime - 1) * Rules.DECELERATION) + | ||
+ | ((distance - decelDist) / decelTime); | ||
+ | } | ||
+ | |||
+ | public static long vopo_decelTime(double distance) { | ||
+ | long x = 1; | ||
+ | do { | ||
+ | // (square(x) + x) / 2) = 1, 3, 6, 10, 15... | ||
+ | if (distance <= ((square(x) + x) / 2) * Rules.DECELERATION) { | ||
+ | return x; | ||
+ | } | ||
+ | x++; | ||
+ | } while (true); | ||
+ | } | ||
+ | |||
+ | public static long square(long i) { | ||
+ | return i * i; | ||
+ | } | ||
+ | |||
+ | }</syntaxhighlight> | ||
And a support class for setting the max velocity: | And a support class for setting the max velocity: | ||
− | < | + | <syntaxhighlight> |
import robocode.Rules; | import robocode.Rules; | ||
Line 127: | Line 532: | ||
public static void setMaxVelocity(double maxVel) { maxVelocity = maxVel; } | public static void setMaxVelocity(double maxVel) { maxVelocity = maxVel; } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Latest revision as of 09:27, 1 July 2010
Some code to test 1.7.1.3's updated getNewVelocity() method vs a few wiki-contributed versions.
The main class:
import robocode.Rules;
public class VelocityTest {
private static Commands currentCommands = new Commands();
private static boolean doPrint = false;
public static void main(String[] args) {
// init
System.out.println("\nDarkcanuck's VelocityTest\n");
if (!args[0].equals("all") && !args[0].equals("print")) {
doPrint = true;
double velocity = (args.length>0) ? Double.valueOf(args[0]) : 0.0;
double distance = (args.length>1) ? Double.valueOf(args[1]) : 0.0;
double maxvel = (args.length>2) ? Double.valueOf(args[2]) : -1.0;
int exptime = (args.length>3) ? Integer.valueOf(args[3]) : 0;
testCase(velocity, distance, maxvel, exptime);
} else {
if (args[0].equals("print"))
doPrint = true;
testCase( 8.0, 22.8, -1, 5); // Simonton's caveat #1
testCase( 8.0, 22.5, -1, 5); // Simonton's caveat #2
testCase( 8.0, -200.0, 7.0, 39); // Simonton's caveat #3
testCase( 0.0, 6.0, -1, 4); // overshoot
testCase( 0.0, 10.0, -1, 6); // overshoots twice
testCase( 4.0, 0.0, -1, 4); // second, unnecessary overshoot
testCase(-1.9, 10.0, -1, 6); // impossible decel/accel!
}
}
public static void testCase(double velocity, double distance, double maxvel, int exptick) {
StringBuffer b = new StringBuffer();
boolean passed = true;
double newvel = velocity;
double newdist = distance;
double maximum = (maxvel>=0) ? maxvel : Rules.MAX_VELOCITY;
int tick = 0;
int maxtick = (exptick>0) ? exptick : (int) Math.max(Math.abs(distance), 100);
b.append("Starting velocity=" + velocity + " distance=" + distance +
((maxvel>=0)? " max=" + maxvel : "") + "\n");
currentCommands.setMaxVelocity(maximum);
while ((Math.abs(newvel)>0.00001) || (Math.abs(newdist)>0.00001)) {
// test next velocity change
//double nextvel = orig_getNewVelocity(newvel, newdist);
//double nextvel = void_getNewVelocity(newvel, newdist);
//double nextvel = skil_getNewVelocity(newvel, newdist);
//double nextvel = posi_getNewVelocity(newvel, newdist);
double nextvel = vopo_getNewVelocity(newvel, newdist);
passed &= checkAccel(nextvel, newvel) && checkMax(nextvel, maximum);
// apply changes
newvel = nextvel;
newdist -= nextvel;
tick++;
b.append(" " + tick + " velocity=" + newvel + " remain=" + newdist + "\n");
// check for infinite loops
if (tick > 2*maxtick)
break;
}
// check time taken
passed &= (tick-1 <= maxtick);
if (!passed)
b.append(" FAIL\n");
if (!passed || doPrint)
System.out.println(b.toString() + "\n");
}
private static boolean checkAccel(double newvel, double oldvel) {
double reverse = (oldvel<0) ? -1.0 : 1.0;
double min = oldvel - reverse*Rules.DECELERATION;
double max = oldvel + reverse*Rules.ACCELERATION;
if (oldvel==0)
min = oldvel - reverse*Rules.ACCELERATION;
if (oldvel<0)
return (newvel>=max) && (newvel<=min);
else
return (newvel<=max) && (newvel>=min);
}
private static boolean checkMax(double newvel, double maxvel) {
return (Math.abs(newvel) <= Math.abs(maxvel));
}
/****************** ORIGINAL FROM ROBOCODE 1.7.1.3 *********************/
private static double orig_getNewVelocity(double velocity, double distance) {
// If the distance is negative, then change it to be positive and change the sign of the input velocity and the result
if (distance < 0) {
return -orig_getNewVelocity(-velocity, -distance);
}
double newVelocity;
// Get the speed, which is always positive (because it is a scalar)
final double speed = Math.abs(velocity);
// 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 (velocity < 0 || speed > currentCommands.getMaxVelocity()) {
// If the velocity is negative, we are decelerating
newVelocity = speed - 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 = speed / 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(currentCommands.getMaxVelocity(),
Math.min(Rules.DECELERATION * decelTime * decelTime + Rules.ACCELERATION * accelTime * accelTime,
distance));
// Note: We change the sign here due to the sign check later when returning the result
velocity *= -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 = speed / 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 (distance <= 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 = distance / (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(speed - Rules.DECELERATION, distance);
} else {
// New velocity (v) = a * t, i.e. deceleration * time
newVelocity = time * Rules.DECELERATION;
if (speed < newVelocity) {
// If the speed is less that the new velocity we just calculated, then use the old speed instead
newVelocity = speed;
} else if (speed - newVelocity > Rules.DECELERATION) {
// The deceleration must not exceed the max. deceleration.
// Hence, we limit the velocity to the speed minus the max. deceleration.
newVelocity = speed - Rules.DECELERATION;
}
}
} else {
// Else, we need to accelerate, but only to max. velocity
newVelocity = Math.min(speed + Rules.ACCELERATION, currentCommands.getMaxVelocity());
}
}
// Return the new velocity with the correct sign. We have been working with the speed, which is always positive
return (velocity < 0) ? -newVelocity : newVelocity;
}
/************************* FROM Voidious *******************************/
private static double void_getNewVelocity(double velocity, double distance) {
// If the distance is negative, then change it to be positive and change the sign of the input velocity and the result
if (distance < 0) {
return -void_getNewVelocity(-velocity, -distance);
}
double newVelocity;
// Get the speed, which is always positive (because it is a scalar)
final double speed = Math.abs(velocity);
if (velocity < 0) {
// Check if we are decelerating, i.e. if the velocity is negative.
newVelocity = speed - 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 = speed / 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(Rules.ACCELERATION * accelTime, distance);
// Note: We change the sign here due to the sign check later when returning the result
velocity *= -1;
}
} else {
// Deceleration distance (d) is calculated iteratively due to Robocode's
// discrete time system.
final double decelDist = void_decelDistance(speed);
// Deceleration ticks is the number of ticks it will take to get to
// zero velocity.
final long decelTime = Math.round( // VOIDIOUS: for rounding errors? maybe unnecessary
Math.ceil((speed - Rules.DECELERATION) / Rules.DECELERATION));
// The maximum distance coverable with an equivalent decelTime
final double decelTimeMaxDist = ((decelTime + 1) / 2.0) * decelTime // sum of 1..decelTime
* Rules.DECELERATION;
if (distance <= Rules.DECELERATION) {
// If we can cover remaining distance and then completely stop,
// set speed = distance
newVelocity = Math.max(speed - Rules.DECELERATION, distance);
} else if (distance <= decelTimeMaxDist) {
// If we can cover distance in decelTime, split any extra
// distance (between decelDist and distance) over decelTime
// ticks
newVelocity = speed - Rules.DECELERATION +
((distance - decelDist) / decelTime);
} else {
// If we need more than decelTime ticks, try to spread the
// extra distance over (decelTime + 1) ticks. This will just max
// the acceleration if it needs to (ie, if we need more ticks).
// VOIDIOUS: I think this part would break if Rules.ACCELERATION
// were set above Rules.DECELERATION; we might need an
// extra case or something. Doh. =(
newVelocity = Math.min(speed + Rules.ACCELERATION,
(decelTime * Rules.DECELERATION) +
((distance - decelTimeMaxDist) / Math.max(decelTime + 1, 2)));
}
}
// VOIDIOUS: I think it makes more sense to do this here; no need to decelerate maximally
// if you don't need to to accomodate a new setMaxVelocity.
newVelocity = Math.max(speed - Rules.DECELERATION,
Math.min(speed + Rules.ACCELERATION,
Math.min(newVelocity, currentCommands.getMaxVelocity())));
// Return the new velocity with the correct sign. We have been working with the speed, which is always positive
return (velocity < 0) ? -newVelocity : newVelocity;
}
private static final double void_decelDistance(double speed){
double distance = 0;
while(speed > 0){
speed = Math.max(0,speed - Rules.DECELERATION);
distance += speed;
}
return distance;
}
/**************************** FROM Skilgannon *********************************/
private static double skil_getNewVelocity(double velocity, double distance) {
if (distance < 0)
return -skil_getNewVelocity(-velocity, -distance);
// If the distance is negative, then change it to be positive
// and change the sign of the input velocity and the result
double maxVel = currentCommands.getMaxVelocity();
if (velocity < 0)
return Math.min(velocity + Rules.DECELERATION, maxVel);
//we want to go in the opposite direction, so decelerate
else{
//we are going in the direction we want, test what happens if we accelerate
double accel = Math.min(Rules.ACCELERATION, maxVel - velocity);
//speed up, but not more than max velocity. decel if maxVel is less than velocity.
accel = Math.max(accel, -Rules.DECELERATION);
//prevent excess deceleration due to bot lowering the maxVel while velocity is high
if (distance > skil_decelDistance(velocity + accel))
return velocity + accel;
else if(accel != 0 && distance > skil_decelDistance(velocity))
return velocity;
else{
if(distance < Rules.DECELERATION
//we'll be able to cover remaining distance in 1 tick and then decel to stop
&& velocity - distance <= Rules.DECELERATION
//and our velocity is low enough for us to get to that required velocity
)
return Math.min(distance, maxVel);
//choose the velocity to cover all remaining distance
return Math.max(-maxVel, velocity - Rules.DECELERATION);
//velocity > 0 and we are close enough, so decelerate.
}
}
}
/**
* Returns the linear distance it would take to decelerate from a given positive velocity
*
* @param velocity the positive velocity from which to test
* @return the linear distance required to decelerate to a standstill
*/
private static final double skil_decelDistance(double velocity){
double distance = 0;
while(velocity > 0){
distance += velocity;
velocity = Math.max(0,velocity - Rules.DECELERATION);
}
return distance;
}
/******************** FROM Positive ********************/
static double posi_getNewVelocity(double velocityArg, double distanceArg)
{
double velocity;
double distance;
// Make sure the remaining distance is always positive or zero
// (we switch this back later)
if(distanceArg<0)
{
velocity=-velocityArg;
distance=-distanceArg;
}
else
{
velocity=velocityArg;
distance=distanceArg;
}
// Get the next most positive velocity the robot can reach for next turn
double mostPositiveReachableVelocity = posi_VelocityToMostPositiveVelocity(velocity);
// Get the next most negative velocity the robot can reach for next turn
double mostNegativeReachableVelocity = posi_VelocityToMostNegativeVelocity(velocity);
double highestWantedVelocity = Math.min(currentCommands.getMaxVelocity(), posi_maxSpeedToStopInDisp(distance));
// The real next velocity is limited by what is actually reachable
double nextVelocity = Math.min(mostPositiveReachableVelocity,Math.max(mostNegativeReachableVelocity,highestWantedVelocity ));
// Switch return value back if needed
if(distanceArg<0)
nextVelocity = -nextVelocity;
return nextVelocity;
}
static public double posi_VelocityToMostPositiveVelocity(double velocity)
{
// Returns the most positive reachable velocity from the
// specified velocity in one turn
if(velocity>0)
{
double returnVelocity = velocity+Rules.ACCELERATION;
if(returnVelocity>Rules.MAX_VELOCITY)
return Rules.MAX_VELOCITY;
else
return returnVelocity;
}
else
{
double returnVelocity = velocity+Rules.DECELERATION;
if(returnVelocity>Rules.ACCELERATION)
return Rules.ACCELERATION;
else
return returnVelocity;
}
}
static public double posi_VelocityToMostNegativeVelocity(double velocity)
{
// Returns the most negative reachable velocity from the
// specified velocity in one turn
if(velocity<0)
{
double returnVelocity = velocity-Rules.ACCELERATION;
if(returnVelocity<-Rules.MAX_VELOCITY)
return -Rules.MAX_VELOCITY;
else
return returnVelocity;
}
else
{
double returnVelocity = velocity-Rules.DECELERATION;
if(returnVelocity<-Rules.ACCELERATION)
return -Rules.ACCELERATION;
else
return returnVelocity;
}
}
static private final double[] dispStopArray =
{0.0000,1.0000,2.0000,2.5000,3.0000,
3.5000,4.0000,4.3333,4.6666,5.0000,
5.3333,5.6666,6.0000,6.2500,6.5000,
6.7500,7.0000,7.2500,7.5000,7.7500};
static public double posi_maxSpeedToStopInDisp(double displacement)
{
// Returns the biggest velocity the robot could go to this turn,
// still being able to stop without overshooting. (Or if
// remaining displacement is less than 2, returns that)
// This routine could be improved to match up with robocode's
// older velocity selecting rules.
if(displacement>=0)
{
if(displacement>=20)
return 8.0;
else if(displacement<=2)
return displacement;
else
return dispStopArray[(int)Math.floor(displacement)];
}
else
{
return 0;
}
}
/****************** FROM Voidious + Positive ***********************/
public static double vopo_getNewVelocity(double velocity, double distance) {
if(distance<0)
return -vopo_getNewVelocity(-velocity,-distance);
double highestVelocity = vopo_getMaxVelocity(distance); // highest velocity without overshooting
double wantedVelocity = Math.min(highestVelocity,8.0);
// the actually wanted velocity by the robot is the highest possible,
// limited by what the robot set by the setMaxVelocity command
return vopo_getClosestReachableVelocityToVelocity(velocity, wantedVelocity);
// return whatever is closest to that velocity
}
public static double vopo_getClosestReachableVelocityToVelocity(double currentVelocity,double wantedVelocity)
{
// this function assumes wantedVelocity<=Rules.MAXVELOCITY
// with this function you can basically assume setAhead(Infinity) or setAhead(-Infinity)
// was called, and you determine the next velocity based on the max velocity
// set by the robot. For example, if the current velocity is 0 and the max velocity
// set was 4.0, it would return 1.0. If the current velocity was 8.0, it would return 6.0.
if(wantedVelocity<0)
return -vopo_getClosestReachableVelocityToVelocity(-currentVelocity,-wantedVelocity);
if(currentVelocity<0)
{
double nextVelocity;
// we are travelling the wrong way, decelerate
nextVelocity = currentVelocity + Rules.DECELERATION;
if(nextVelocity>Rules.ACCELERATION)
// make sure we can't jump from -0.1 to 1.9 or something
nextVelocity = Rules.ACCELERATION;
if(nextVelocity>wantedVelocity)
// if the wanted velocity is for example 0.5, limit the velocity to that.
return wantedVelocity;
else
// else return the highest possible
return nextVelocity;
}
else
{
if(currentVelocity>wantedVelocity)
{
// both velocities are positive, but we need to decelerate
double nextVelocity = currentVelocity - Rules.DECELERATION;
if(nextVelocity<wantedVelocity)
// if we can decelerate more than what's wanted, return what's wanted
return wantedVelocity;
else
// else return the closest to it
return nextVelocity;
}
else
{
// the wantedVelocity is higher than current
double nextVelocity = currentVelocity + Rules.ACCELERATION;
if(nextVelocity>wantedVelocity)
// if we can accelerate more than what's wanted, return what's wanted
return wantedVelocity;
else
// else return the closest to it
return nextVelocity;
}
}
}
public static double vopo_getMaxVelocity(double distance)
{
long decelTime = vopo_decelTime(distance);
double decelDist = (decelTime / 2.0) * (decelTime-1) // sum of 0..(decelTime-1)
* Rules.DECELERATION;
return ((decelTime - 1) * Rules.DECELERATION) +
((distance - decelDist) / decelTime);
}
public static long vopo_decelTime(double distance) {
long x = 1;
do {
// (square(x) + x) / 2) = 1, 3, 6, 10, 15...
if (distance <= ((square(x) + x) / 2) * Rules.DECELERATION) {
return x;
}
x++;
} while (true);
}
public static long square(long i) {
return i * i;
}
}
And a support class for setting the max velocity:
import robocode.Rules;
public class Commands {
private static double maxVelocity = Rules.MAX_VELOCITY;
public static double getMaxVelocity() { return maxVelocity; }
public static void setMaxVelocity(double maxVel) { maxVelocity = maxVel; }
}