Difference between revisions of "Circular Targeting/Walkthrough"

From Robowiki
Jump to navigation Jump to search
(Adding categories)
m (update TeX, remove first person reference)
 
(10 intermediate revisions by 6 users not shown)
Line 1: Line 1:
Circular [[Targeting]] is used to hit bots that often move in circles or large arcs. The first step is to measure the turnrate of your target bot by subtracting it's current heading with it's previous one. If you do not get a fresh scan of your target, you need to divide this by the time between current scan and your last scan to obtain an average turnrate:
+
[[Circular targeting]] is used to hit bots that often move in circles or large arcs. The first step is to measure the turn rate of your target bot by subtracting its current heading with its previous one. If you do not get a fresh scan of your target, you need to divide this value by the time between current scan and your last scan to obtain an average turn rate:
<pre>
 
public void onScannedRobot(ScannedRobotEvent e)
 
{
 
double turnrate=(e.getHeadingRadians()-lastEnemyHeading)/(getTime()-scantime);
 
scantime=getTime();
 
lastEnemyHeading=e.getHeadingRadians();
 
...
 
...
 
...
 
}</pre>
 
  
Now we need to know the current position of the target bot. To keep things easy, we only calculate the position of your target relative to your position. Keep in mind that in Robocode,  "north" is zero degrees(radians) and clockwise is a positive angle. This is exactly the inverse of unit-circle math, where the x-axis is zero degrees and anti-clockwise is a positive angle.
+
:<math>
<pre>
+
turnRate = {{enemyHeading - lastEnemyHeading} \over {\Delta t}}
absoluteBearing = e.getBearingRadians() + getHeadingRadians();
+
</math>
double relativeX = e.getDistance()*Math.sin(absoluteBearing);
 
double relativeY = e.getDistance()*Math.cos(absoluteBearing);
 
</pre>
 
  
Since we <u>assume</u> the target will keep turning at the same turnrate every turn, we can predict it's position at any time by calculating a new velocity-vector, and add that to it's position. If we call the time that you scanned the target t=0, then it's position at t=1 will be:
+
Now we need to know the current position of the target bot. To keep things easy, we only calculate the position of your target relative to your position. Keep in mind that in Robocode,  "north" is zero degrees (radians) and clockwise is a positive angle. This is exactly the inverse of unit-circle math, where the x-axis is zero degrees and counter-clockwise is a positive angle.
 +
:<math>
 +
bearing = enemyBearing + headingBearing
 +
</math>
 +
:<math>
 +
x_{relative} = distance \times sin(bearing)
 +
</math>
 +
:<math>
 +
y_{relative} = distance \times cos(bearing)
 +
</math>
  
(this is not a java code snippet)
+
Since we <u>assume</u> the target will keep turning at the same turn rate every turn, we can predict its position at any time by calculating a new velocity vector and add that to its position. If we call the time that you scanned the target ''t=0'', then its position at ''t=1'' will be:
( 1 ) newX(1) = relativeX + enemyVelocity*sin(initialHeading + turnrate)
 
( 2 ) newY(1) = relativeX + enemyVelocity*cos(initialHeading + turnrate)
 
  
at time t=2, it will be
+
:<math>
 +
x_1 = x_{relative} + enemyVelocity \times sin(initialHeading + turnRate)
 +
</math>
 +
:<math>
 +
y_1 = y_{relative} + enemyVelocity \times cos(initialHeading + turnRate)
 +
</math>
  
(this is not a java code snippet)
+
at time ''t=2'', it will be:
( 3 ) newX(2) = relativeX + enemyVelocity*sin(initialHeading + turnrate) + enemyVelocity*sin(initialHeading + turnrate + turnrate)
 
( 4 ) newY(2) = relativeX + enemyVelocity*cos(initialHeading + turnrate) + enemyVelocity*cos(initialHeading + turnrate + turnrate)
 
  
You can see that you can calculate the new (relative) X/Y position at t=T by adding a newly calculated velocity-vector to its position on every t.
+
:<math>
 +
x_2 = x_{relative} + enemyVelocity \times sin(initialHeading + turnRate)
 +
      + enemyVelocity \times sin(initialHeading + turnRate + turnRate)
 +
</math>
 +
:<math>
 +
y_2 = y_{relative} + enemyVelocity \times cos(initialHeading + turnRate)
 +
      + enemyVelocity \times cos(initialHeading + turnRate + turnRate)
 +
</math>
  
(this is not a java code snippet)
+
You can see that you can calculate the new (relative) X/Y position at t=T by adding a newly calculated velocity vector to its position on every ''t''.
( 5 ) newX(T) = relativeX + http://kcl.kwokkie.net/klli87/images/sum.gif enemyVelocity*sin(initialHeading + t*turnrate)
 
( 6 ) newY(T) = relativeY + http://kcl.kwokkie.net/klli87/images/sum.gif enemyVelocity*cos(initialHeading + t*turnrate)
 
  
 +
:<math>
 +
x_t = x_{relative} + \sum_{n=0}^{t} {enemyVelocity \times sin(initialHeading + turnRate \times n)}
 +
</math>
 +
:<math>
 +
y_t = y_{relative} + \sum_{n=0}^{t} {enemyVelocity \times cos(initialHeading + turnRate \times n)}
 +
</math>
  
Space and time is not continuous in Robocode, but the discreet steps are small enough, that we can approximate the above summation with an integral:
+
Space and time is not continuous in Robocode, but the discrete steps are small enough that we can approximate the above summation with an integral:
  
(this is not a java code snippet)
+
:<math>
( 7 ) newX(T) = relativeX + enemyVelocity * http://kcl.kwokkie.net/klli87/images/integral.gif sin(initialHeading + t*turnrate) dt
+
x_t = x_{relative} + \int_{n=0}^{t} {enemyVelocity \times sin(initialHeading + turnRate \times n)} dn
( 8 ) newY(T) = relativeY + enemyVelocity * http://kcl.kwokkie.net/klli87/images/integral.gif cos(initialHeading + t*turnrate) dt
+
</math>
 +
:<math>
 +
y_t = y_{relative} + \int_{n=0}^{t} {enemyVelocity \times cos(initialHeading + turnRate \times n)} dn
 +
</math>
  
 +
Integrating from t=0 to t=T which results in: (<u>watch the +/- signs!! and where you need cosine and sine</u>)
  
integrating from t=0 to t=T wich results in: (<u>watch the +/- signs!! and where you need cosine and sine</u>)
+
:<math>
(this is not a java code snippet)
+
x_t = x_{relative} - {enemyVelocity \over turnRate} \times (cos(initialHeading - turnRate \times t) - cos(initialHeading))
( 9 ) newX(T) = relativeX - (enemyVelocity/turnrate) * (cos(initialHeading + T*turnrate) - cos(initialHeading))
+
</math>
( 10 ) newY(T) = relativeY + (enemyVelocity/turnrate) * (sin(initialHeading + T*turnrate) - sin(initialHeading))
+
:<math>
 +
y_t = y_{relative} - {enemyVelocity \over turnRate} \times (sin(initialHeading - turnRate \times t) - sin(initialHeading))
 +
</math>
  
 +
Just fill in ''t'' in the above and you can predict your circular-moving target's position at time ''t''!
  
Just fill in T in the above and you can predict your circular-moving target's position at time T!
+
Now to predict at which time your bullet is going to hit your target. The time it would take for your bullet to travel to the target's ''current'' position is a good guess to start with. So your first guess would be ''t = enemyDistance/bulletSpeed''. If we plug this T in last two formulas, we have a prediction of the target's new (relative) position at time ''t''. But since the target will be moving around, rather than playing [[Sitting Duck]], this is a rather poor approximation of the time ''t'' we are after. We can improve our approximation by calculating the time it would take for your bullet to reach the <u>previously predicted</u> position. This new ''t'' can then in turn be used to calculate a better predicted position, which, in turn, can be used to calculate an even better approximation of the actual time ''t'', etc. I myself have never used this iteration technique, but I remember reading somewhere (RoboCodeRepository forums?) that 4 or 5 of these iterations should be good enough. Alisdair Owens, the author of [[Nicator]] and the [OtherPeoplesRobots SnippetBot tutorial], wrote an article for "Secrets of the Robocode Masters" about circular targeting with iteration. You can find it [http://www-106.ibm.com/developerworks/library/j-circular/ here].
Now to predict at wich time your bullet is going to hit your target. The time it would take for your bullet to travel to the target's <i>current</i> position is a good guess to start with. So your first guess would be T = enemyDistance/bulletSpeed. If we plug this T in formulae (9) and (10), we have a prediction of the target's new (relative) position at time T. But since the target will be moving around, rather than playing sittingDuck, this is a rather poor approximation of the time T we are after. We can improve our approximation by calculating the time it would take for your bullet to reach the <u>previously predicted</u> position. This new T can then in turn be used to calculate a better predicted position, wich, in turn, can be used to calculate an even better approximation of the actual time T, etc...etc...  I myself have never used this iteration technique, but I remember reading somewhere(RoboCodeRepository forums?) that 4 or 5 of these iterations should be good enough. Alisdair Owens, the author of Nicator and the [OtherPeoplesRobots SnippetBot tutorial], wrote an article for "Secrets of the Robocode master" about circular targeting with iteration. You can find it here: http://www-106.ibm.com/developerworks/library/j-circular/
 
  
Another method is to start at T=0, and keep increasing with T with 1. Every time you increase your T, compare the distance your bullet would travel in T timeframes, with the distance between you and the predicted position of your target at time T. Stop when your bullet reaches your target, and you'll have a predicted position! Because the position of the target is calculated step-by-step anyway, we don't need formulae (9) and (10) and will be performing the summations (5) and (6) step by step as well.  
+
Another method is to start at ''t=0'' and keep increasing with ''t'' with 1. Every time you increase your ''t'', compare the distance your bullet would travel in ''t'' ticks with the distance between you and the predicted position of your target at time ''t''. Stop when your bullet reaches your target, and you'll have a predicted position! Because the position of the target is calculated step-by-step anyway, we can use the formula in step ''t=2'' as well.
You can find an example from this in  [[CodeSnippets]] section. It's the source-code from my bot [[CodeSnippets/Nano Circular Linear Predictor | Nano Circular Linear Predictor]], a bot I built just to prove that a circular predictor fits into a NanoBot. :-)
 
Note that the step-by-step method can be used for both CircularTargeting and LinearTargeting, whereas the integrated formulae (9) and (10) are not suited for linear targeting. (Linear targeting means turnrate = 0)
 
  
 +
You can find an example from this in [[:Category:Code Snippets|Code Snippets]] section. It's the source code from [[User:Dummy|Dummy]]'s bot [[Code Snippets/Nano Circular Linear Predictor|Nano Circular Linear Predictor]], a bot he built just to prove that a circular predictor fits into a NanoBot.
 +
 +
Note that the step-by-step method can be used for both [[circular targeting]] and [[linear targeting]], whereas the integrated formulas are not suited for linear targeting (linear targeting means ''turnRate = 0'').
 +
 +
[[Category:Tutorials]]
 
[[Category:Simple Targeting Strategies]]
 
[[Category:Simple Targeting Strategies]]
[[Category:Tutorials]]
 
[[Category:Code Snippets]]
 

Latest revision as of 04:22, 3 August 2009

Circular targeting is used to hit bots that often move in circles or large arcs. The first step is to measure the turn rate of your target bot by subtracting its current heading with its previous one. If you do not get a fresh scan of your target, you need to divide this value by the time between current scan and your last scan to obtain an average turn rate:

<math>

turnRate = {{enemyHeading - lastEnemyHeading} \over {\Delta t}} </math>

Now we need to know the current position of the target bot. To keep things easy, we only calculate the position of your target relative to your position. Keep in mind that in Robocode, "north" is zero degrees (radians) and clockwise is a positive angle. This is exactly the inverse of unit-circle math, where the x-axis is zero degrees and counter-clockwise is a positive angle.

<math>

bearing = enemyBearing + headingBearing </math>

<math>

x_{relative} = distance \times sin(bearing) </math>

<math>

y_{relative} = distance \times cos(bearing) </math>

Since we assume the target will keep turning at the same turn rate every turn, we can predict its position at any time by calculating a new velocity vector and add that to its position. If we call the time that you scanned the target t=0, then its position at t=1 will be:

<math>

x_1 = x_{relative} + enemyVelocity \times sin(initialHeading + turnRate) </math>

<math>

y_1 = y_{relative} + enemyVelocity \times cos(initialHeading + turnRate) </math>

at time t=2, it will be:

<math>

x_2 = x_{relative} + enemyVelocity \times sin(initialHeading + turnRate)

     + enemyVelocity \times sin(initialHeading + turnRate + turnRate)

</math>

<math>

y_2 = y_{relative} + enemyVelocity \times cos(initialHeading + turnRate)

     + enemyVelocity \times cos(initialHeading + turnRate + turnRate)

</math>

You can see that you can calculate the new (relative) X/Y position at t=T by adding a newly calculated velocity vector to its position on every t.

<math>

x_t = x_{relative} + \sum_{n=0}^{t} {enemyVelocity \times sin(initialHeading + turnRate \times n)} </math>

<math>

y_t = y_{relative} + \sum_{n=0}^{t} {enemyVelocity \times cos(initialHeading + turnRate \times n)} </math>

Space and time is not continuous in Robocode, but the discrete steps are small enough that we can approximate the above summation with an integral:

<math>

x_t = x_{relative} + \int_{n=0}^{t} {enemyVelocity \times sin(initialHeading + turnRate \times n)} dn </math>

<math>

y_t = y_{relative} + \int_{n=0}^{t} {enemyVelocity \times cos(initialHeading + turnRate \times n)} dn </math>

Integrating from t=0 to t=T which results in: (watch the +/- signs!! and where you need cosine and sine)

<math>

x_t = x_{relative} - {enemyVelocity \over turnRate} \times (cos(initialHeading - turnRate \times t) - cos(initialHeading)) </math>

<math>

y_t = y_{relative} - {enemyVelocity \over turnRate} \times (sin(initialHeading - turnRate \times t) - sin(initialHeading)) </math>

Just fill in t in the above and you can predict your circular-moving target's position at time t!

Now to predict at which time your bullet is going to hit your target. The time it would take for your bullet to travel to the target's current position is a good guess to start with. So your first guess would be t = enemyDistance/bulletSpeed. If we plug this T in last two formulas, we have a prediction of the target's new (relative) position at time t. But since the target will be moving around, rather than playing Sitting Duck, this is a rather poor approximation of the time t we are after. We can improve our approximation by calculating the time it would take for your bullet to reach the previously predicted position. This new t can then in turn be used to calculate a better predicted position, which, in turn, can be used to calculate an even better approximation of the actual time t, etc. I myself have never used this iteration technique, but I remember reading somewhere (RoboCodeRepository forums?) that 4 or 5 of these iterations should be good enough. Alisdair Owens, the author of Nicator and the [OtherPeoplesRobots SnippetBot tutorial], wrote an article for "Secrets of the Robocode Masters" about circular targeting with iteration. You can find it here.

Another method is to start at t=0 and keep increasing with t with 1. Every time you increase your t, compare the distance your bullet would travel in t ticks with the distance between you and the predicted position of your target at time t. Stop when your bullet reaches your target, and you'll have a predicted position! Because the position of the target is calculated step-by-step anyway, we can use the formula in step t=2 as well.

You can find an example from this in Code Snippets section. It's the source code from Dummy's bot Nano Circular Linear Predictor, a bot he built just to prove that a circular predictor fits into a NanoBot.

Note that the step-by-step method can be used for both circular targeting and linear targeting, whereas the integrated formulas are not suited for linear targeting (linear targeting means turnRate = 0).