[Home]BasicGTSurfer/Code

Robo Home | BasicGTSurfer | Changes | Preferences | AllPages

Showing revision 1
Difference (from revision 1 to revision 1) (minor diff)
(The revisions are identical or unavailable.)
<pre>
    package wiki.tutorial;

   import robocode.*;
   import robocode.util.Utils;
   import java.awt.geom.*;     // for Point2D's
   import java.lang.*;         // for Double and Integer objects
   import java.util.ArrayList; // for collection of waves
   import java.awt.*;

    public class BasicGTSurfer extends AdvancedRobot {
      public static int BINS = 47;
      public static double _surfStats[] = new double[BINS]; // we'll use 47 bins
      public Point2D.Double _myLocation;     // our bot's location
      public Point2D.Double _enemyLocation;  // enemy bot's location

      public Point2D.Double _lastGoToPoint?;
      public double direction = 1;

      public ArrayList _enemyWaves;
      public ArrayList _surfDirections;
      public ArrayList _surfAbsBearings?;

    // We must keep track of the enemy's energy level to detect EnergyDrop,
    // indicating a bullet is fired
      public static double _oppEnergy = 100.0;

    // This is a rectangle that represents an 800x600 battle field,
    // used for a simple, iterative WallSmoothing method (by Kawigi).
    // If you're not familiar with WallSmoothing, the wall stick indicates
    // the amount of space we try to always have on either end of the tank
    // (extending straight out the front or back) before touching a wall.
      public static Rectangle2D.Double _fieldRect
        = new java.awt.geom.Rectangle2D.Double(18, 18, 764, 564);
      public static double WALL_STICK = 160;

       public void run() {
         _enemyWaves = new ArrayList();
         _surfDirections = new ArrayList();
         _surfAbsBearings? = new ArrayList();

         setAdjustGunForRobotTurn(true);
         setAdjustRadarForGunTurn(true);

         do {
            // basic mini-radar code
            turnRadarRightRadians?(Double.POSITIVE_INFINITY);
         } while (true);
      }

       public void onScannedRobot(ScannedRobotEvent e) {
         _myLocation = new Point2D.Double(getX(), getY());

         double lateralVelocity = getVelocity()*Math.sin(e.getBearingRadians?());
         double absBearing = e.getBearingRadians?() + getHeadingRadians();

         setTurnRadarRightRadians(Utils.normalRelativeAngle(absBearing - getRadarHeadingRadians?()) * 2);

         _surfDirections.add(0,
            new Integer((lateralVelocity >= 0) ? 1 : -1));
         _surfAbsBearings?.add(0, new Double(absBearing + Math.PI));

         double bulletPower = _oppEnergy - e.getEnergy();
         if (bulletPower < 3.01 && bulletPower > 0.09
            && _surfDirections.size() > 2) {
            EnemyWave ew = new EnemyWave();
            ew.fireTime = getTime() - 1;
            ew.bulletVelocity = bulletVelocity(bulletPower);
            ew.distanceTraveled = bulletVelocity(bulletPower);
            ew.direction = ((Integer)_surfDirections.get(2)).intValue();
            ew.directAngle = ((Double)_surfAbsBearings?.get(2)).doubleValue();
            ew.fireLocation = (Point2D.Double)_enemyLocation.clone(); // last tick

            _enemyWaves.add(ew);
         }

         _oppEnergy = e.getEnergy();

        // update after EnemyWave detection, because that needs the previous
        // enemy location as the source of the wave
         _enemyLocation = project(_myLocation, absBearing, e.getDistance());

         updateWaves();
         doSurfing();

        // gun code would go here...
      }

       public void updateWaves() {
         for (int x = 0; x < _enemyWaves.size(); x++) {
            EnemyWave ew = (EnemyWave)_enemyWaves.get(x);

            ew.distanceTraveled = (getTime() - ew.fireTime) * ew.bulletVelocity;
            if (ew.distanceTraveled >
                _myLocation.distance(ew.fireLocation) + 50) {
               _enemyWaves.remove(x);
               x--;
            }
         }
      }

       public EnemyWave getClosestSurfableWave?() {
         double closestDistance = 50000; // I juse use some very big number here
         EnemyWave surfWave = null;

         for (int x = 0; x < _enemyWaves.size(); x++) {
            EnemyWave ew = (EnemyWave)_enemyWaves.get(x);
            double distance = _myLocation.distance(ew.fireLocation)
                - ew.distanceTraveled;

            if (distance > ew.bulletVelocity && distance < closestDistance) {
               surfWave = ew;
               closestDistance = distance;
            }
         }

         return surfWave;
      }

    // Given the EnemyWave that the bullet was on, and the point where we
    // were hit, calculate the index into our stat array for that factor.
       public static int getFactorIndex?(EnemyWave ew, Point2D.Double targetLocation) {
         double offsetAngle = (absoluteBearing(ew.fireLocation, targetLocation)
            - ew.directAngle);
         double factor = Utils.normalRelativeAngle(offsetAngle)
            / maxEscapeAngle?(ew.bulletVelocity) * ew.direction;

         return (int)limit(0,
            (factor * ((BINS - 1) / 2)) + ((BINS - 1) / 2),
            BINS - 1);
      }

    // Given the EnemyWave that the bullet was on, and the point where we
    // were hit, update our stat array to reflect the danger in that area.
       public void logHit(EnemyWave ew, Point2D.Double targetLocation) {
         int index = getFactorIndex?(ew, targetLocation);

         for (int x = 0; x < BINS; x++) {
            // for the spot bin that we were hit on, add 1;
            // for the bins next to it, add 1 / 2;
            // the next one, add 1 / 5; and so on...
            _surfStats[x] += 1.0 / (Math.pow(index - x, 2) + 1);
         }
      }

       public void onHitByBullet?(HitByBulletEvent? e) {
        // If the _enemyWaves collection is empty, we must have missed the
        // detection of this wave somehow.
         if (!_enemyWaves.isEmpty()) {
            Point2D.Double hitBulletLocation? = new Point2D.Double(
                e.getBullet().getX(), e.getBullet().getY());
            EnemyWave hitWave = null;

            // look through the EnemyWaves, and find one that could've hit us.
            for (int x = 0; x < _enemyWaves.size(); x++) {
               EnemyWave ew = (EnemyWave)_enemyWaves.get(x);

               if (Math.abs(ew.distanceTraveled -
                    _myLocation.distance(ew.fireLocation)) < 50
                    && Math.round(bulletVelocity(e.getBullet().getPower()) * 10)
                       == Math.round(ew.bulletVelocity * 10)) {
                  hitWave = ew;
                  break;
               }
            }

            if (hitWave != null) {
               logHit(hitWave, hitBulletLocation?);

                // We can remove this wave now, of course.
               _enemyWaves.remove(_enemyWaves.lastIndexOf?(hitWave));
            }
         }
      }

    // CREDIT: mini sized predictor from Apollon, by rozu
    // http://robowiki.net?Apollon
       public ArrayList predictPositions(EnemyWave surfWave, int direction) {
         Point2D.Double predictedPosition = (Point2D.Double)_myLocation.clone();
         double predictedVelocity = getVelocity();
         double predictedHeading = getHeadingRadians();
         double maxTurning, moveAngle, moveDir;
         ArrayList traveledPoints = new ArrayList();

         int counter = 0; // number of ticks in the future
         boolean intercepted = false;

         do {
            double distance = predictedPosition.distance(surfWave.fireLocation);
            double offset = Math.PI/2 - 1 + distance/400;

            moveAngle =
                wallSmoothing(predictedPosition, absoluteBearing(surfWave.fireLocation,
                predictedPosition) + (direction * (offset)), direction)
                - predictedHeading;
            moveDir = 1;

            if(Math.cos(moveAngle) < 0) {
               moveAngle += Math.PI;
               moveDir = -1;
            }

            moveAngle = Utils.normalRelativeAngle(moveAngle);

         // maxTurning is built in like this, you can't turn more then this in one tick
            maxTurning = Math.PI/720d*(40d - 3d*Math.abs(predictedVelocity));
            predictedHeading = Utils.normalRelativeAngle(predictedHeading
                + limit(-maxTurning, moveAngle, maxTurning));

         // this one is nice ;). if predictedVelocity and moveDir have
            // different signs you want to breack down
         // otherwise you want to accelerate (look at the factor "2")
            predictedVelocity += (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir);
            predictedVelocity = limit(-8, predictedVelocity, 8);

         // calculate the new predicted position
            predictedPosition = project(predictedPosition, predictedHeading, predictedVelocity);

         //add this point the our prediction
            traveledPoints.add(predictedPosition);

            counter++;

            if (predictedPosition.distance(surfWave.fireLocation) - 20 <
                surfWave.distanceTraveled + (counter * surfWave.bulletVelocity)
              //   + surfWave.bulletVelocity
            	 ) {
               intercepted = true;
            }
         } while(!intercepted && counter < 500);

      //we can't get the the last point, because we need to slow down
         if(traveledPoints.size() > 1)
            traveledPoints.remove(traveledPoints.size() - 1);

         return traveledPoints;
      }

       public double checkDanger(EnemyWave surfWave, Point2D.Double position) {
         int index = getFactorIndex?(surfWave, position);
         double distance = position.distance(surfWave.fireLocation);
         return _surfStats[index]/distance;
      }

       public Point2D.Double getBestPoint?(EnemyWave surfWave){
         if(surfWave.safePoints == null){
            ArrayList forwardPoints = predictPositions(surfWave, 1);
            ArrayList reversePoints = predictPositions(surfWave, -1);
            int FminDangerIndex? = 0;
            int RminDangerIndex? = 0;
            double FminDanger? = Double.POSITIVE_INFINITY;
            double RminDanger? = Double.POSITIVE_INFINITY;
            for(int i = 0, k = forwardPoints.size(); i < k; i++){
               double thisDanger = checkDanger(surfWave, (Point2D.Double)(forwardPoints.get(i)));
               if(thisDanger <= FminDanger?){
                  FminDangerIndex? = i;
                  FminDanger? = thisDanger;
               }
            }
            for(int i = 0, k = reversePoints.size(); i < k; i++){
               double thisDanger = checkDanger(surfWave, (Point2D.Double)(reversePoints.get(i)));
               if(thisDanger <= RminDanger?){
                  RminDangerIndex? = i;
                  RminDanger? = thisDanger;
               }
            }
            ArrayList bestPoints;
            int minDangerIndex?;

            if(FminDanger? < RminDanger? ){
               bestPoints = forwardPoints;
               minDangerIndex? = FminDangerIndex?;
            }
            else {
               bestPoints = reversePoints;
               minDangerIndex? = RminDangerIndex?;
            }

            Point2D.Double bestPoint = (Point2D.Double)bestPoints.get(minDangerIndex?);

            while(bestPoints.indexOf(bestPoint) != -1)
               bestPoints.remove(bestPoints.size() - 1);
            bestPoints.add(bestPoint);

            surfWave.safePoints = bestPoints;

         	//debugging - so that we should always be on top of the last point
            bestPoints.add(0,new Point2D.Double(getX(), getY()));

         }
         else
            if(surfWave.safePoints.size() > 1)
               surfWave.safePoints.remove(0);

         if(surfWave.safePoints.size() >= 1){
            for(int i = 0,k=surfWave.safePoints.size(); i < k; i++){
               Point2D.Double goToPoint? = (Point2D.Double)surfWave.safePoints.get(i);
               if(goToPoint?.distanceSq(_myLocation) > 20*20*1.1)
               //if it's not 20 units away we won't reach max velocity
                  return goToPoint?;
            }
         //if we don't find a point 20 units away, return the end point
            return (Point2D.Double)surfWave.safePoints.get(surfWave.safePoints.size() - 1);

         }

         return null;
      }

       public void doSurfing() {
         EnemyWave surfWave = getClosestSurfableWave?();
         double distance = _enemyLocation.distance(_myLocation);
         if (surfWave == null || distance < 50) { 
            //do 'away' movement  best distance of 400 - modified from RaikoNano
            double absBearing = absoluteBearing(_myLocation, _enemyLocation);
            double headingRadians = getHeadingRadians();
            double stick = 160;//Math?.min(160,distance);
            double  v2, offset = Math.PI/2 + 1 - distance/400;

            while(!_fieldRect.
            contains(project(_myLocation,v2 = absBearing + direction*(offset -= 0.02), stick)

            // 	getX() + stick * Math.sin(v2 = absBearing + direction * (offset -= .02)), getY() + stick * Math.cos(v2)
            ));

            if( offset < Math.PI/3 ) 
               direction = -direction;
            setAhead(50*Math.cos(v2 - headingRadians));
            setTurnRightRadians?(Math.tan(v2 - headingRadians));

         }
         else
            goTo(getBestPoint?(surfWave));
      }
       private void goTo(Point2D.Double destination) {
         if(destination == null){
            if(_lastGoToPoint? != null)
               destination = _lastGoToPoint?;
            else
               return;
         }

         _lastGoToPoint? = destination;
         Point2D.Double location = new Point2D.Double(getX(), getY());
         double distance = location.distance(destination);
         double angle = Utils.normalRelativeAngle(absoluteBearing(location, destination) - getHeadingRadians());
         if (Math.abs(angle) > Math.PI/2) {
            distance = -distance;
            if (angle > 0) {
               angle -= Math.PI;
            }
            else {
               angle += Math.PI;
            }
         }

      	//this is hacked so that the bot doesn't turn once we get to our destination
         setTurnRightRadians?(angle*Math.signum(Math.abs((int)distance)));

         setAhead(distance);
      }

    // This can be defined as an inner class if you want.
       class EnemyWave {
         Point2D.Double fireLocation;
         long fireTime;
         double bulletVelocity, directAngle, distanceTraveled;
         int direction;
         ArrayList safePoints;

          public EnemyWave() { }
      }

    // CREDIT: Iterative WallSmoothing by Kawigi
    //   - return absolute angle to move at after account for WallSmoothing
    // robowiki.net?WallSmoothing
       public double wallSmoothing(Point2D.Double botLocation, double angle, int orientation) {
         while (!_fieldRect.contains(project(botLocation, angle, 160))) {
            angle += orientation*0.05;
         }
         return angle;
      }

    // CREDIT: from CassiusClay, by PEZ
    //   - returns point length away from sourceLocation, at angle
    // robowiki.net?CassiusClay
       public static Point2D.Double project(Point2D.Double sourceLocation, double angle, double length) {
         return new Point2D.Double(sourceLocation.x + Math.sin(angle) * length,
            sourceLocation.y + Math.cos(angle) * length);
      }

    // got this from RaikoMicro, by Jamougha, but I think it's used by many authors
    //  - returns the absolute angle (in radians) from source to target points
       public static double absoluteBearing(Point2D.Double source, Point2D.Double target) {
         return Math.atan2(target.x - source.x, target.y - source.y);
      }

       public static double limit(double min, double value, double max) {
         return Math.max(min, Math.min(value, max));
      }

       public static double bulletVelocity(double power) {
         return (20D - (3D*power));
      }

       public static double maxEscapeAngle?(double velocity) {
         return Math.asin(8.0/velocity);
      }

       public static void setBackAsFront(AdvancedRobot robot, double goAngle) {
         double angle =
            Utils.normalRelativeAngle(goAngle - robot.getHeadingRadians());
         if (Math.abs(angle) > (Math.PI/2)) {
            if (angle < 0) {
               robot.setTurnRightRadians?(Math.PI + angle);
            } 
            else {
               robot.setTurnLeftRadians?(Math.PI - angle);
            }
            robot.setBack(100);
         } 
         else {
            if (angle < 0) {
               robot.setTurnLeftRadians?(-1*angle);
            } 
            else {
               robot.setTurnRightRadians?(angle);
            }
            robot.setAhead(100);
         }
      }

       public void onPaint(java.awt.Graphics2D g) {
         g.setColor(Color.red);
         for(int i = 0; i < _enemyWaves.size(); i++){
            EnemyWave w = (EnemyWave)(_enemyWaves.get(i));
            Point2D.Double center = w.fireLocation;

            //int radius = (int)(w.distanceTraveled + w.bulletVelocity);
            //hack to make waves line up visually, due to execution sequence in robocode engine
            //use this only if you advance waves in the event handlers (eg. in onScannedRobot())
            //NB! above hack is now only necessary for robocode versions before 1.4.2
            //otherwise use: 
            int radius = (int)w.distanceTraveled;

            if(radius - 40 < center.distance(_myLocation))
               g.drawOval((int)(center.x - radius ), (int)(center.y - radius), radius*2, radius*2);
         }
      }

   }

Robo Home | BasicGTSurfer | Changes | Preferences | AllPages
Edit revision 1 of this page | View other revisions | View current revision
Edited October 3, 2007 22:34 EST by Skilgannon (diff)
Search: