Skip to main content

Secrets from the Robocode masters: Factored wall avoidance

David McCoy, Writer, Independent
David McCoy was introduced to Robocode by a coworker about 8 months ago and was immediately fascinated. His competition bot, droid.PrairieWolf, has ranked as high as first place in the melee competition at the Gladiatorial League. Contact David at dsmccoy@velocitus.net.

Summary:  It's difficult to come up with an algorithm that keeps your bot out of the walls without getting it trapped in a corner or deviating too much from the desired movement direction. A simple solution is factored wall avoidance. In this tip, David McCoy shows you how to implement this handy technique.

Date:  01 May 2002
Level:  Introductory
Activity:  3289 views

With a few additions to the bot we built in "Tracking your opponents' movement", we can add factored wall avoidance to an existing or troublesome movement algorithm. Factored wall avoidance attempts to find the best possible heading by factoring together the desired heading with a safe heading based on how close our bot is to a wall.

Adding helper methods for common mathematical calculations

The first things we will add to our robot are a few helper methods for frequently used mathematical algorithms.

The calculateBearingToXYRadians() method uses the java.lang.Math method atan2() to calculate the absolute bearing from sourceX,sourceY to targetX,targetY, then converts this value to a bearing relative to sourceHeading.

We will also require a normalizeAbsoluteAngleRadians() method and a normalizeRelativeAngleRadians() method.


Listing 1. Math helper methods


private static final double DOUBLE_PI = (Math.PI * 2);
private static final double HALF_PI = (Math.PI / 2);

public double calculateBearingToXYRadians(double sourceX, double sourceY,
    double sourceHeading, double targetX, double targetY) {
        return normalizeRelativeAngleRadians(
           Math.atan2((targetX - sourceX), (targetY - sourceY)) -
               sourceHeading);
    }

public double normalizeAbsoluteAngleRadians(double angle) {
   if (angle < 0) {
        return (DOUBLE_PI + (angle % DOUBLE_PI));
    } else {
        return (angle % DOUBLE_PI);
    }
}

public static double normalizeRelativeAngleRadians(double angle) {
    double trimmedAngle = (angle % DOUBLE_PI);
    if (trimmedAngle > Math.PI) {
        return -(Math.PI - (trimmedAngle % Math.PI));
    } else if (trimmedAngle < -Math.PI) {
        return (Math.PI + (trimmedAngle % Math.PI));
    } else {
        return trimmedAngle;
    }
}


Extending AdvancedRobot with back-as-front functionality

Next, we need to extend the AdvancedRobot class functionality with some helper methods to allow back-as-front operations for navigating in reverse:

  • The getRelativeHeading() method handles the overhead of calculating the correct heading relative to the bot's current direction.

  • The reverseDirection() method is fairly straightforward. It toggles the direction instance variable and reverses the bot's direction. Note that because it takes time to decelerate, the bot may continue to move in the same direction for up to four frames before reversing, depending on its velocity.

  • The setAhead() and setBack() methods override the methods of the same name in the AdvancedRobot class. They set the speed of the bot relative to the current direction and adjust the direction instance variable as necessary. We do this to ensure that relative operations are always in relation to the direction the bot is currently moving.

  • The setTurnLeftRadiansOptimal() and setTurnRightRadiansOptimal() methods reverse the direction of the bot for turns greater than (Math.PI / 2). You will want to use these methods when using the adjustHeadingForWalls method, which we will discuss later.

Note: I access the direction instance variable directly instead of using getter and setter methods. This is not generally a good programming practice, but I often do it in my bot code to speed up data access.


Listing 2. Robot helper methods



public double getRelativeHeadingRadians() {
    double relativeHeading = getHeadingRadians();
    if (direction < 1) {
        relativeHeading =
                normalizeAbsoluteAngleRadians(relativeHeading + Math.PI);
    }
    return relativeHeading;
}

public void reverseDirection() {
    double distance = (getDistanceRemaining() * direction);
    direction *= -1;
    setAhead(distance);
}

public void setAhead(double distance) {
    double relativeDistance = (distance * direction);
    super.setAhead(relativeDistance);
    if (distance < 0) {
        direction *= -1;
    }
}

public void setBack(double distance) {
    double relativeDistance = (distance * direction);
    super.setBack(relativeDistance);
    if (distance > 0) {
        direction *= -1;
    }
}

public void setTurnLeftRadiansOptimal(double angle) {
    double turn = normalizeRelativeAngleRadians(angle);
    if (Math.abs(turn) > HALF_PI) {
        reverseDirection();
        if (turn < 0) {
            turn = (HALF_PI + (turn % HALF_PI));
        } else if (turn > 0) {
            turn = -(HALF_PI - (turn % HALF_PI));
        }
    }
    setTurnLeftRadians(turn);
}

public void setTurnRightRadiansOptimal(double angle) {
    double turn = normalizeRelativeAngleRadians(angle);
    if (Math.abs(turn) > HALF_PI) {
        reverseDirection();
        if (turn < 0) {
            turn = (HALF_PI + (turn % HALF_PI));
        } else if (turn > 0) {
            turn = -(HALF_PI - (turn % HALF_PI));
        }
    }
        setTurnRightRadians(turn);
}


Adding factored wall avoidance

The last method we need to add is adjustHeadingForWalls().

The first half of this method chooses a safe x,y position based on the bot's proximity to the walls (this will be the bot's current x or y location or a center point if the bot is near a wall). The second half of the method calculates the bearing to the "safe" point and factors this in with the desired heading relative to how close the bot is to a wall.

The degree to which the bot worries about the walls can be adjusted using the WALL_AVOID_INTERVAL and WALL_AVOID_FACTORS constants.


Listing 3. The wall avoidance method


private static final double WALL_AVOID_INTERVAL = 10;
private static final double WALL_AVOID_FACTORS = 20;
private static final double WALL_AVOID_DISTANCE =
        (WALL_AVOID_INTERVAL * WALL_AVOID_FACTORS);

private double adjustHeadingForWalls(double heading) {
    double fieldHeight = getBattleFieldHeight();
    double fieldWidth = getBattleFieldWidth();
    double centerX = (fieldWidth / 2);
    double centerY = (fieldHeight / 2);
    double currentHeading = getRelativeHeadingRadians();
    double x = getX();
    double y = getY();
    boolean nearWall = false;
    double desiredX;
    double desiredY;

    // If we are too close to a wall, calculate a course toward 
    // the center of the battlefield.
    if ((y < WALL_AVOID_DISTANCE) ||
            ((fieldHeight - y) < WALL_AVOID_DISTANCE)) {
        desiredY = centerY;
        nearWall = true;
    } else {
        desiredY = y;
    }
    if ((x < WALL_AVOID_DISTANCE) ||
            ((fieldWidth - x) < WALL_AVOID_DISTANCE)) {
        desiredX = centerX;
        nearWall = true;
    } else {
        desiredX = x;
    }

    // Determine the safe heading and factor it in with the desired 
    // heading if the bot is near a wall
    if (nearWall) {
        double desiredBearing = 
           calculateBearingToXYRadians(x, 
                                       y, 
                                       currentHeading, 
                                       desiredX, 
                                       desiredY);
        double distanceToWall = Math.min(
                Math.min(x, (fieldWidth - x)),
                Math.min(y, (fieldHeight - y)));
        int wallFactor =
                (int)Math.min((distanceToWall / WALL_AVOID_INTERVAL),
                              WALL_AVOID_FACTORS);
        return ((((WALL_AVOID_FACTORS - wallFactor) * desiredBearing) +
                 (wallFactor * heading)) / WALL_AVOID_FACTORS);
    } else {
        return heading;
    }
}


Putting it all together

The rest is easy. We can use our current navigation algorithm and feed the result through the adjustHeadingForWalls() method to avoid the walls.

To keep things simple, the example bot (see Download to download the source code needed to add this technique) will always attempt to move in a straight line by requesting a heading change of 0.


Listing 4. Wall avoidance method



public void run() {
    while(true) {
        setTurnRightRadiansOptimal(adjustHeadingForWalls(0));
        setAhead(100);
        execute();
    }
}

That's all there is to it. Simple, but effective.



Download

DescriptionNameSizeDownload method
Source codej-fwa.zip4KB HTTP

Information about download methods


Resources

  • Read all of the Secrets from the Robocode masters. This page will be updated as new tips become available.

  • Visit the official Robocode site for more information.

  • RoboLeague by Christian Schnell is a league and season manager for Robocode. It ensures that all possible groupings indeed play their matches, manages the results, and produces HTML status reports.

  • "Rock 'em, sock 'em Robocode" (developerWorks, January 2002) disarms Robocode and starts you on your way to building your own customized lean, mean, fighting machine.

  • In "Rock 'em, sock 'em Robocode: Round 2" (developerWorks, May 2002), Sing Li looks at advanced robot construction and team play.

  • New to Java? Check out "Introduction to Java programming" (developerWorks, November 2004), a tutorial that steps you through the fundamentals of Java language programming.

  • developerWorks: Hundreds of articles about every aspect of Java programming.

About the author

David McCoy was introduced to Robocode by a coworker about 8 months ago and was immediately fascinated. His competition bot, droid.PrairieWolf, has ranked as high as first place in the melee competition at the Gladiatorial League. Contact David at dsmccoy@velocitus.net.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=194672
ArticleTitle=Secrets from the Robocode masters: Factored wall avoidance
publish-date=05012002
author1-email=dsmccoy@velocitus.net
author1-email-cc=jaloi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers