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
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
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
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(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
setMaxVelocity(0)
. Is that what you meant? -- Simonton
getTurnRemainingRadians()
return the turn remaining for this tick, or next tick? The order-of-processing stuff always confuses me. -- AaronR
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