Difference between revisions of "User:Voidious/Optimal Velocity"

From Robowiki
Jump to navigation Jump to search
(somewhere to put the full version of this)
 
(attempt at getMaxVelocity version)
Line 119: Line 119:
 
* Version 1 posted in-line at [[Talk:Robocode/Game Physics]].
 
* Version 1 posted in-line at [[Talk:Robocode/Game Physics]].
 
* Version 2 bounds newVelocity by speed + Rules.ACCELERATION and sets a min of 2 for the number of ticks to split the extra distance over.
 
* Version 2 bounds newVelocity by speed + Rules.ACCELERATION and sets a min of 2 for the number of ticks to split the extra distance over.
 +
 +
----
 +
 +
Phrasing this a different way, we might try to figure out the max velocity we can go now and still decelerate to a given distance. I think this works, and it does seem like a much more elegant solution.
 +
<pre>
 +
double getMaxVelocity(double distance)
 +
{
 +
    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;
 +
}
 +
</pre>
 +
 +
I derived this by first mapping some things out for different distances, then dissecting it:
 +
* [0, 2] - distance
 +
* (2, 6] - 2 + ((distance-2)/2)
 +
* (6, 12] - 4 + ((distance-6)/3)
 +
* (12, 20] - 6 + ((distance-12)/4)
  
 
[[Category:Source Code]]
 
[[Category:Source Code]]

Revision as of 19:09, 15 July 2009

I don't want to clutter the Talk:Robocode/Game Physics page with full versions of this, but I want the updated full version somewhere... The goal is to go distance in the fewest number of ticks, limited by Rules.ACCELERATION, Rules.DECELERATION, and currentCommands.getMaxVelocity().

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

    /**
     * Returns the new velocity based on the current velocity and distance to move.
     *
     * @param velocity the current velocity
     * @param distance the distance to move
     * @return the new velocity based on the current velocity and distance to move
     */
    private double 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 -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 = 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 decelDistance(double speed){
        double distance = 0;
        while(speed > 0){
            speed = Math.max(0,speed - Rules.DECELERATION);
            distance += speed;
        }
        return distance;
    }
  • Version 1 posted in-line at Talk:Robocode/Game Physics.
  • Version 2 bounds newVelocity by speed + Rules.ACCELERATION and sets a min of 2 for the number of ticks to split the extra distance over.

Phrasing this a different way, we might try to figure out the max velocity we can go now and still decelerate to a given distance. I think this works, and it does seem like a much more elegant solution.

double getMaxVelocity(double distance)
{
    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;
}

I derived this by first mapping some things out for different distances, then dissecting it:

  • [0, 2] - distance
  • (2, 6] - 2 + ((distance-2)/2)
  • (6, 12] - 4 + ((distance-6)/3)
  • (12, 20] - 6 + ((distance-12)/4)