Wall Smoothing/Implementations

From Robowiki
< Wall Smoothing
Revision as of 21:11, 21 August 2009 by Voidious (talk | contribs) (migrating from old wiki)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
Wall Smoothing Sub-pages:
Wall SmoothingImplementations

Simple Iterative Wall Smoothing (by PEZ)

This algorithm is simple to code and easy to understand, but it is relatively slow to execute. This is a good one to use for Random Movement or Code Size-restricted bots. If you're Wave Surfing with Precise Prediction, your Wall Smoothing is called many times per tick, so you'll want something faster.

This is used in many bots, such as Tityus and Raiko.

static double direction;	//1 for clockwise or -1 for counterclockwise
...
// this is the absolute heading I want to move in to go clockwise or
// counterclockwise around my enemy if I want to move closer to them,
// I would use less of an offset from absBearing (I'll go right toward
// them if I move at absBearing)
double goalDirection = absBearing-Math.PI/2*direction;
Rectangle2D fieldRect = new Rectangle2D.Double(18, 18, getBattleFieldWidth()-36,
    getBattleFieldHeight()-36);
while (!fieldRect.contains(getX()+Math.sin(goalDirection)*120, getY()+
        Math.cos(goalDirection)*120))
{
	goalDirection += direction*.1;	//turn a little toward enemy and try again
}
double turn =
    robocode.util.Utils.normalRelativeAngle(goalDirection-getHeadingRadians());
if (Math.abs(turn) > Math.PI/2)
{
	turn = robocode.util.Utils.normalRelativeAngle(turn + Math.PI);
	setBack(100);
}
else
	setAhead(100);
setTurnRightRadians(turn);

Fast Wall Smoothing (by Voidious)

This algorithm is much faster than the simple one (above), and the code is nice and compact, but it can be difficult to understand. It's designed to mimic the functionality of the simple one by PEZ, but it is much faster to execute and allows you to get very close to the walls.

Note that while this is iterative and contains a "sanity check" to avoid an infinite loop, it should never loop more than twice if implemented correctly. It is used in Dookious and Diamond, so take a look at their source code if you're still confused on how to use it.

// _bfWidth and _bfHeight set to battle field width and height
private static double WALL_STICK = 140;
private java.awt.geom.Rectangle2D.Double _fieldRect =
    new java.awt.geom.Rectangle2D.Double(18, 18,
    _bfWidth-36, _bfHeight-36);

// ...
/**
 * x/y = current coordinates
 * startAngle = absolute angle that tank starts off moving - this is the angle
 *   they will be moving at if there is no wall smoothing taking place.
 * orientation = 1 if orbiting enemy clockwise, -1 if orbiting counter-clockwise
 * smoothTowardEnemy = 1 if smooth towards enemy, -1 if smooth away
 * NOTE: this method is designed based on an orbital movement system; these
 *   last 2 arguments could be simplified in any other movement system.
 */
public double wallSmoothing(double x, double y, double startAngle,
    int orientation, int smoothTowardEnemy) {

    angle = startAngle;

    // in Java, (-3 MOD 4) is not 1, so make sure we have some excess
    // positivity here
    angle += (4*Math.PI);

    double testX = x + (Math.sin(angle)*WALL_STICK);
    double testY = y + (Math.cos(angle)*WALL_STICK);
    double wallDistanceX = Math.min(x - 18, _bfWidth - x - 18);
    double wallDistanceY = Math.min(y - 18, _bfHeight - y - 18);
    double testDistanceX = Math.min(testX - 18, _bfWidth - testX - 18);
    double testDistanceY = Math.min(testY - 18, _bfHeight - testY - 18);

    double adjacent = 0;
    int g = 0; // because I'm paranoid about potential infinite loops

    while (!_fieldRect.contains(testX, testY) && g++ < 25) {
        if (testDistanceY < 0 && testDistanceY < testDistanceX) {
            // wall smooth North or South wall
            angle = ((int)((angle + (Math.PI/2)) / Math.PI)) * Math.PI;
            adjacent = Math.abs(wallDistanceY);
        } else if (testDistanceX < 0 && testDistanceX <= testDistanceY) {
            // wall smooth East or West wall
            angle = (((int)(angle / Math.PI)) * Math.PI) + (Math.PI/2);
            adjacent = Math.abs(wallDistanceX);
        }

        // use your own equivalent of (1 / POSITIVE_INFINITY) instead of 0.005
        // if you want to stay closer to the wall ;)
        angle += smoothTowardEnemy*orientation*
            (Math.abs(Math.acos(adjacent/WALL_STICK)) + 0.005);

        testX = x + (Math.sin(angle)*WALL_STICK);
        testY = y + (Math.cos(angle)*WALL_STICK);
        testDistanceX = Math.min(testX - 18, _bfWidth - testX - 18);
        testDistanceY = Math.min(testY - 18, _bfHeight - testY - 18);

        if (smoothTowardEnemy == -1) {
            // this method ended with tank smoothing away from enemy... you may
            // need to note that globally, or maybe you don't care.
        }
    }

    return angle; // you may want to normalize this
}

Non-Iterative Wall Smoothing (by David Alves)

This algorithm is very slightly faster than Voidious' and is simple to understand, but the code is huge and ugly. You have been warned! See /Non-Iterative.

Non-Iterative Wall Hugging (by Simonton)

This algorithm allows you to get closer to the walls by adjusting the length of the "blind man's stick" based on your bot's current position relative to the wall. See /Fancy Stick.