[Home]Movement/CodeSnippetGoTo

Robo Home | Movement | Changes | Preferences | AllPages

If you ever find you need a function for moving to a specific point on the battle field, while not wasting too much time turning the robot around, use something like this (which is code from Mako):
    private void goTo(Point2D point) {
        double distance = location.distance(point);
        double angle = normalRelativeAngle(absoluteBearing(location, point) - getHeading());
        if (Math.abs(angle) > 90.0) {
            distance *= -1.0;
            if (angle > 0.0) {
                angle -= 180.0;
            }
            else {
                angle += 180.0;
            }
        }
        setTurnRight(angle);
        setAhead(distance);
    }

It is assumed you keep your robots' position as a Point2D and call it "location". But it is reasonably simple to modify this to just use coordinates instead.

If you use it you'll find you want WallAvoidance as well. I use this function for this:

    private void translateInsideField(Point2D point, double margin) {
        double X = Math.max(margin, Math.min(fieldRectangle.getWidth() - margin, point.getX()));
        double Y = Math.max(margin, Math.min(fieldRectangle.getHeight() - margin, point.getY()));
        point.setLocation(X, Y);
    }

This assumes you have a Rectangle2D named fieldRectangle. Use these functions like this:

Point2D destination ...
...
translateInsideField(destination, 35);
goTo(destination);

To be complete here's the absoluteBearing() and normalRelativeAngle() functions used by goTo():

    private double absoluteBearing(Point2D source, Point2D target) {
         return Math.toDegrees(Math.atan2(target.getX() - source.getX(), target.getY() - source.getY()));
    }

    private double normalRelativeAngle(double angle) {
        double relativeAngle = angle % 360;
        if (relativeAngle <= -180)
            return 180 + (relativeAngle % 180);
        else if (relativeAngle > 180)
            return -180 + (relativeAngle % 180);
        else
            return relativeAngle;
    }
-- PEZ


As this using setTurn and setAhead, doesn't that mean it will go round the corners at a curve, rather than stop and turn? If so, doesn't that make the distance slightly too short? -- Tango

Hang on, not just the distance, the angle too... -- Tango

Ok, i've just run a little test, and it seems to not be very acruate. I may have a bug in the code, but i don't think so. I'll put it here so you can check: /GoToTester. The results i got were:

Going to:617.4033989160434, 192.59685274255
Got to: 617.4033989160425, 192.59685274255025
Going to:638.979160094087, 549.0028883776123
Got to: 617.0726377457916, 203.44867163762578
Going to:152.60434264516562, 190.13134625044182
Got to: 157.36814759419306, 170.45493872721636
Going to:414.3233985177302, 242.61058570705111
Got to: 158.59470981023338, 170.6951302830833
Going to:93.00733040297432, 152.38992634689527
Got to: 110.43413876872322, 157.2536864784152
Going to:500.4762780630491, 365.88651690289515
Got to: 111.39732841890115, 157.52250927839708
Going to:340.0873017231276, 256.2230939786768
Got to: 113.23360477474912, 158.31503003203935

It seems to be very inconsistant. The 1st move is almost perfect, but some are nowhere near. This is part of the reason i think i may have a bug in the code. -- Tango

I too think there's a bug in the goto-code. But not that huge. I often see my bots turning around very much, something that should be prevented by this function... As for your results I think it's because you don't take into account that both getturnremaining() and getdistanceremaining() can return negative numbers. In fact, they often do with this goto-function giving the movement commands. Take a look at /GoToBot for a bot resembling your testingbot. -- PEZ

I modified GoToBot to get my testing bot, hence it having the same name. I'll change the >0 to !=0 and that should fix it, yes? -- Tango

With that change it is better, but still not perfect. I guess that is the curved corners fault.

Going to:386.4198389757968, 559.17943017897
Got to: 388.6551095552587, 557.3184742475037
Going to:471.917292555878, 83.55690393219658
Got to: 468.7459714353496, 83.53632307661653
Going to:539.2140819713197, 223.81174374017849
Got to: 536.9557327932158, 224.54620220117138
Going to:284.9742511979826, 480.5808464314793
Got to: 309.69711133217186, 493.93508970580365
Going to:456.0052566686705, 546.7729132542152
Got to: 457.82999596267786, 526.9811484233526
Going to:71.65945999038469, 174.7262887613292
Got to: 71.3780450466824, 175.09379467951442
Going to:517.1099351157167, 80.49648674073453
Got to: 517.0415579087721, 89.2510631638913
Going to:537.8964615827879, 506.84008453730445
Got to: 505.6559372599263, 498.35714514216545
Going to:321.2752173956358, 61.38191837847893
Got to: 321.60480206159724, 61.271263456182965

-- Tango

It's not supposed to be perfect. The shortcomings are discussed in a little more detail on the page /CodeSnippetBasicGoTo. But looking at your tests I think it looks close enough. What I do to make it more accurate is to keep the current destination as a member variable of the class and then keep calling goTo() (either in the run() method or when a robot is scanned) with that destination until Math.abs(getDistanceRemaining()) < ARRIVED_THRESHOLD. --PEZ

Alright, Im planning on implementing a GoTo? system for all of my movement (who knows if it will work). My thought was to use a custom event system to handle accuracy, so that when goTo was called it would remove the previous custom event for goto and add a new one, and this new one would, in turn, call go to every time it was not in position. But I am starting to confuse myself more when I try to implement it. So how would I make a method, that when called would remove all custom events of a certain type, and then add a custom event of that type (well actually it would create a new object of type condition that would add itself as a custom event)?? -- Jokester

Not that Jokester will see this answer maybe. But I would probably do something like this:

class GotoEvent extends Condition {
    private static GotoEvent current;
    private Point2D destination;
    private AdvancedRobot robot;

    public GotoEvent(AdvancedRobot robot, Point2D p) {
        this.destination = p;
        this.robot = robot;
        if (current != null) {
             robot.removeCustomEvent(current);
        }
        current = this;
        robot.addCustomEvent(current);
    }

    public boolean test() {
        robot.goTo(destination);
        return false;
    }
}
Completely untested, but it seems it should work? There's no test to see if we have reached the destination, but it's not strictly necessary anyway I think. Then again I don't see why you would need an event like this really. Why not just keep track of where you want to go and keep calling goTo() in the run() or scannedRobot() methods?

-- PEZ

I've experimented with the GoTo? by making my bot going to the center of the battlefield, the faults of the above code is that it makes the robot go to the target way too fast and thus making turns very soft, I solved that by using setMaxVelocity?() My current forumla (in radians): setMaxVelocity?(8-(angle/(Math.PI/4))*8) If you are interested in the full code, give me a call. -- Avihoo Ilan (this is my first time in the robowiki)

I don't see the accuraccy of this function to much of a problem as any decent bot using it will constantly picking a new point to goto, thus it will all blend togeather. -- Chase-san

Smallest CodeSize

	private void goTo(double destinationX, double destinationY) {
		destinationX -= getX();
		destinationY -= getY();
                double angle = Utils.normalRelativeAngle(Math.atan2(destinationX, destinationY) - getHeadingRadians() );
		double turnAngle = Math.atan(Math.tan(angle));
                setTurnRightRadians(turnAngle);
                setAhead(Math.hypot(destinationX, destinationY) * (angle == turnAngle ? 1 : -1));
	}

-- Stelokim

Someone can test this if they're interested, but assuming you call this every tick, it should safely shrink to:

private void goTo(int x, int y) {
    double a;
    setTurnRightRadians(Math.tan(
        a = Math.atan2(x -= (int) getX(), y -= (int) getY()) 
              - getHeadingRadians()));
    setAhead(Math.hypot(x, y) * Math.cos(a));
}
It will not start moving full speed until generally pointing in the correct direction (also depending on how far away the go-to point is), but that may be considered worse or a feature depending! -- Simonton

I just edited the snippet above to make it smaller, and it has been tested now (it needed one change)! Using ints instead of doubles makes for smaller codesize arithmetic. -- Simonton

Smallest CodeSize for ExtendsRobot

	private void goTo(double destinationX, double destinationY) {
		destinationX -= getX();
		destinationY -= getY();
                double angle = robocode.util.Utils.normalRelativeAngle(Math.atan2(destinationX, destinationY) - Math.toRadians(getHeading()) );
		double turnAngle = Math.atan(Math.tan(angle));

                turnRight(Math.toDegrees(turnAngle));
		ahead(Math.hypot(destinationX, destinationY) * (angle == turnAngle ? 1 : -1));
	}

-- Stelokim

What about the driveTo() code from the BeginnersFAQ page? Im too lazy to check if it turns 90 degrees at most, and i'm not a nano/micro code expert, but it should...

driveTo() - from DuelistMicro as stated on the BeginnersFAQ page

driveTo(double nextX, double nextY){
  double myX = getX();double myY = getY();
  setTurnRightRadians?(normalizedTurnAngle? =  Math.atan(Math.tan(turnAngle = ((Math.atan2(nextX - myX, nextY - myY) - setHeadingRadians()) + (7.0 *  Math.PI)) % (2.0 * Math.PI) -  Math.PI)));
  setAhead((normalizedTurnAngle? == turnAngle ? 1.0 : -1.0) * (java.awt.geom.Point2D.Double.distance(myX, myY, nextX, nextY)));}
--Starrynte


Question: what is the best system/function/algorithm for knowing how much to slow for a turn, such that it optimizes the time it takes to reach a given destination? -- Simonton

Maybe solve iteratively? But usually it doesn't much matter that you land at a precise location, does it matter in this case? --David Alves

I want to break it into a function if I can. I'll be using it a lot of times per turn, for my surfing. So yes, from what I hear, absolute precision is imperative in surfing. Well, precise predictions, that is. -- Simonton

Precisely predicting where you will drive to is important. Precisely driving to a given point is not important. At least I haven't had a reason to precisely drive to a certain point as quickly as possible yet. It's more like, "If I choose to go left for the next 12 turns in row, exactly where will I end up?" rather than "How can I get to exactly there as quickly as possible?". --David Alves

With my style WaveSurfing that becomes more important, though I doubt it's worth a whole lot. I plot my course considering whether I can reach certain points on incoming waves. If I have an optimal driving technique, I will be able to reach more of those points, creating more possible paths to choose from. And maybe one of those will be the best choice! There are a lot of other things I need to do that are more important in my surfing, but I would like to implement this at some point. -- Simonton

I doubt that this will help, but ... looks up the GamePhysics page and does algebra ... setMaxVelocity((Math.PI / 18 - Math.min(Math.abs(getTurnRemainingRadians()), Math.PI / 18)) / (Math.PI / 240)) should find the fastest way to turn to face a given direction. -- AaronR

Wow Aaron ... can you explain that? -- Simonton

Great! AaronR! Now I feel like using that:) No more braking hard!! e.g. setMaxVelocity(Math.abs(getTurnRemaining()) > 30 ? 0 : 8); // Hit the brake pedal hard if we need to turn sharply

For WaveSurfing you are staying pretty much perpendicular, aren't you? Since this doesn't require much turning, I'm guessing that you would only reduce your max escape angle by slowing down. I fooled around with this in the early days of DrussGT, and it only added another place for bugs to creep in. You have to remember that you may get to a better escape angle by not slowing down, and not turning as much either. The only place I can see that slowing down would help would be for avoiding wall hits, although those are easily avoided without this hack. -- Skilgannon

The other thing is, at least in a normal surfer, the only place this code would kick in is near a corner (because of the sharp wallsmoothing turns). Guess what kind of segmentation your gun needs to hit a bot that always slows down in the corner? =) -- AaronR


Robo Home | Movement | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited October 21, 2007 22:08 EST by Simonton (diff)
Search: