User:Positive/Optimal Velocity

From Robowiki
< User:Positive
Revision as of 23:46, 15 July 2009 by Positive (talk | contribs) (Temporary fix)
Jump to navigation Jump to search

To not clutter up the Talk:Robocode/Game Physics page:

I believe this is the correct getClosestReachableVelocityToVelocity function (feel free to comment):

double 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 -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;
  }
 }
} 

getMaxVelocity function by Voidious:

    double getMaxVelocity(double distance)
    {
        if(distance>=20)  // temporary fix, works for maxVelocity==8.0 && maxDecel==2.0
          return Rules.MAX_VELOCITY;
        long decelTime = decelTime(distance);
        double decelDist = (decelTime / 2.0) * (decelTime-1) // sum of 0..(decelTime-1)
            * Rules.DECELERATION;
            
        return ((decelTime - 1) * Rules.DECELERATION) +
            ((distance - decelDist) / decelTime);
    }

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

    long square(long i) {
        return i * i;
    }

The getNewVelocity function:

    double getNewVelocity(double velocity, double distance) {
    	 if(distance<0)
    	  return -getNewVelocity(-velocity,-distance);
    	  double highestVelocity = 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 getClosestReachableVelocityToVelocity(velocity, wantedVelocity);
    	      // return whatever is closest to that velocity
    }

Simulator:


    public void simulate()
    {
    	double currentVelocity = 8.0;
    	double distanceRemain = -2.0;
    	while(distanceRemain!=0.0 || currentVelocity!=0.0)
    	{
    		out.println("velocity = "+currentVelocity+"; distance="+distanceRemain);
    		currentVelocity = getNewVelocity(currentVelocity,distanceRemain);
    		distanceRemain -=currentVelocity;
    	}
    }

Results

StartVelocity = 0.0; StartDistance = 6.0;

velocity = 0.0; distance=6.0
velocity = 1.0; distance=5.0
velocity = 2.0; distance=3.0
velocity = 2.5; distance=0.5
velocity = 0.5; distance=0.0

StartVelocity = 0.0; StartDistance = 10;

velocity = 0.0; distance=10.0
velocity = 1.0; distance=9.0
velocity = 2.0; distance=7.0
velocity = 3.0; distance=4.0
velocity = 3.0; distance=1.0
velocity = 1.0; distance=0.0

StartVelocity = -1.9; StartDistance = 10;

velocity = -1.9; distance=10.0
velocity = 0.10000000000000009; distance=9.9
velocity = 1.1; distance=8.8
velocity = 2.1; distance=6.700000000000001
velocity = 3.1; distance=3.600000000000001
velocity = 2.8000000000000007; distance=0.8000000000000003
velocity = 0.8000000000000007; distance=-4.440892098500626E-16;
velocity = -4.440892098500626E-16; distance=0.0

StartVelocity = 8.0; StartDistance = -2.0;

velocity = 8.0; distance=-2.0
velocity = 6.0; distance=-8.0
velocity = 4.0; distance=-12.0
velocity = 2.0; distance=-14.0
velocity = -0.0; distance=-14.0
velocity = -1.0; distance=-13.0
velocity = -2.0; distance=-11.0
velocity = -3.0; distance=-8.0
velocity = -4.0; distance=-4.0
velocity = -3.0; distance=-1.0
velocity = -1.0; distance=0.0

StartVelocity = 5.0; StartDistance = 40.0;

velocity = 5.0; distance=40.0
velocity = 6.0; distance=34.0
velocity = 7.0; distance=27.0
velocity = 8.0; distance=19.0
velocity = 7.75; distance=11.25
velocity = 5.75; distance=5.5
velocity = 3.75; distance=1.75
velocity = 1.75; distance=0.0

Suggested updateMovement code

Original updateMovement code:

    private void updateMovement() {
        double distance = currentCommands.getDistanceRemaining();

        if (Double.isNaN(distance)) {
            distance = 0;
        }

        velocity = getNewVelocity(velocity, distance);

        double dx = velocity * sin(bodyHeading);
        double dy = velocity * cos(bodyHeading);

        x += dx;
        y += dy;

        if (dx != 0 || dy != 0) {
            updateBoundingBox();
        }

        if (distance != 0) {
            currentCommands.setDistanceRemaining(distance - velocity);
        }
    }

Now I'm thinking, if the distance remaining == 0.0, and the velocity is set from 6.0 to 4.0, shouldnt the distance remaining become -4.0? Also, the updateBoundingBox is called whenever velocity!=0, because the condition dx==0 && dy==0 is only when the velocity==0, so that can be easier to check. I suggest:

    private void updateMovement() {
        double distance = currentCommands.getDistanceRemaining();

        if (Double.isNaN(distance)) {
            distance = 0;
        }

        velocity = getNewVelocity(velocity, distance);

	if(velocity!=0)
	{
		x += velocity * sin(bodyHeading);
		y += velocity * cos(bodyHeading);
		updateBoundingBox();
	}

	currentCommands.setDistanceRemaining(distance - velocity);
    }