Difference between revisions of "Komarious/Code"

From Robowiki
Jump to navigation Jump to search
(migrating from old wiki)
 
m (Using <syntaxhighlight>.)
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
This is the code to Komarious 1.712. Code size is 1492 when compiled with Jikes (3 colors = 13 bytes).
+
{{Navbox small
 +
| title        = Sub-pages
 +
| parent      = Komarious
 +
| page1        = Version History
 +
| page2        = Code
 +
| page3        = Archived Talk 20090424
 +
}}
 +
----
 +
This is the code to Komarious 1.842. Code size is 1492 when compiled with Jikes (and 3 colors = 13 bytes).
  
 
----
 
----
  
<pre>
+
<syntaxhighlight>
 
package voidious.mini;
 
package voidious.mini;
  
Line 11: Line 19:
 
import java.util.LinkedList;
 
import java.util.LinkedList;
 
import java.awt.Color;
 
import java.awt.Color;
 +
import robocode.Rules;
  
 
/**
 
/**
Line 29: Line 38:
  
 
public class Komarious extends AdvancedRobot {
 
public class Komarious extends AdvancedRobot {
     private static double _surfStats[][][] = new double[4][5][47];
+
     ////////////////////////////////////////////////////////
     private static double _nextSurfSegment[];
+
    // Shared between movement and gun
 +
    static final int GF_ZERO = 23;
 +
     static final int GF_ONE = 46;
 
     public static Point2D.Double _myLocation;
 
     public static Point2D.Double _myLocation;
 
     public static Point2D.Double _enemyLocation;
 
     public static Point2D.Double _enemyLocation;
 +
    private static double _lastDistance;
 +
    private static double _enemyAbsoluteBearing;
 +
    ////////////////////////////////////////////////////////
 +
 +
    ////////////////////////////////////////////////////////
 +
    // Movement
 +
    static final double WALL_STICK = 140;
 +
    static final double A_LITTLE_LESS_THAN_HALF_PI = 1.25; // Math.PI/2 would be
 +
        // perpendicular movement, less will keep us moving away slightly
 +
    private static double _surfStats[][][] = new double[4][5][GF_ONE+1];
 
     public static LinkedList _enemyWaves;
 
     public static LinkedList _enemyWaves;
 
     private static double _oppEnergy;
 
     private static double _oppEnergy;
 
 
     private static Wave _surfWave;
 
     private static Wave _surfWave;
     private static double _lastDistance;
+
     private static Wave _nextSurfWave;
 
 
    private static int _lastLastOrientation;
 
 
     private static double _lastLatVel;
 
     private static double _lastLatVel;
    private static double _lastLastAbsBearingRadians;
 
 
     private static double _lastAbsBearingRadians;
 
     private static double _lastAbsBearingRadians;
    private static double _lastPredictedDistance;
 
   
 
 
     private static double _goAngle;
 
     private static double _goAngle;
    private static int _ramCounter;
 
   
 
 
     private static java.awt.geom.Rectangle2D.Double _fieldRect
 
     private static java.awt.geom.Rectangle2D.Double _fieldRect
 
         = new java.awt.geom.Rectangle2D.Double(18, 18, 764, 564);
 
         = new java.awt.geom.Rectangle2D.Double(18, 18, 764, 564);
 +
    ////////////////////////////////////////////////////////
  
    // Math.PI/2 would be perpendicular movement, less will keep moving
 
    // further away
 
    static final double A_LITTLE_LESS_THAN_HALF_PI = 1.25;
 
    static final double WALL_STICK = 140;
 
 
    static final int GF_ZERO = 23;
 
    static final int GF_ONE = 46;
 
    private static double _enemyAbsoluteBearing;
 
    private static int _lastGunOrientation;
 
   
 
 
     ////////////////////////////////////////////////////////
 
     ////////////////////////////////////////////////////////
     // RaikoMicro's gun
+
     // Gun
 
+
    static final double LOG_BASE_E_TO_2_CONVERSION_CONSTANT = 1.4427;
 +
    static double[][][][][][] _gunStats = new double[6][4][4][2][3][GF_ONE+1];
 
     static double lastVChangeTime;
 
     static double lastVChangeTime;
 
     static int enemyVelocity;
 
     static int enemyVelocity;
     static double[][][][][][] _gunStats = new double[6][4][4][2][3][GF_ONE+1];
+
     private static int _ramCounter;
     static final double LOG_BASE_E_TO_2_CONVERSION_CONSTANT = 1.4427;
+
     private static int _lastGunOrientation;
 
 
 
     ////////////////////////////////////////////////////////
 
     ////////////////////////////////////////////////////////
  
Line 79: Line 83:
  
 
         do {
 
         do {
             turnRadarRightRadians(Double.POSITIVE_INFINITY);
+
             turnRadarRightRadians(1);
 
         } while (true);
 
         } while (true);
  
Line 86: Line 90:
 
     public void onScannedRobot(ScannedRobotEvent e) {
 
     public void onScannedRobot(ScannedRobotEvent e) {
 
         Wave w;
 
         Wave w;
 +
        int direction;
  
 
         double bulletPower;
 
         double bulletPower;
 
         if ((bulletPower = _oppEnergy - e.getEnergy()) <= 3
 
         if ((bulletPower = _oppEnergy - e.getEnergy()) <= 3
 
             && bulletPower > 0) {
 
             && bulletPower > 0) {
             addCustomEvent(w = new Wave());
+
             (w = _nextSurfWave).bulletSpeed = Rules.getBulletSpeed(bulletPower);
            w.bulletVelocity = (20D - (bulletPower*3));
+
             addCustomEvent(w);
             w.directAngle = _lastLastAbsBearingRadians;
 
            w.sourceLocation = _enemyLocation;
 
            w.orientation = _lastLastOrientation;
 
            w.waveGuessFactors = _nextSurfSegment;
 
 
             _enemyWaves.addLast(w);
 
             _enemyWaves.addLast(w);
 
         }
 
         }
         _nextSurfSegment = _surfStats[(int)(Math.min((_lastDistance+50)/200, 3))][(int)((Math.abs(_lastLatVel)+1)/2)];
+
         (_nextSurfWave = w = new Wave()).directAngle = _lastAbsBearingRadians;
 +
        w.waveGuessFactors = _surfStats[(int)(Math.min((_lastDistance+50)/200, 3))][(int)((Math.abs(_lastLatVel)+1)/2)];
 +
        w.orientation = direction = sign(_lastLatVel);
 +
        double enemyAbsoluteBearing;
 +
        w.sourceLocation = _enemyLocation =
 +
            project((_myLocation = new Point2D.Double(getX(), getY())),
 +
                    _enemyAbsoluteBearing = enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians(), _lastDistance = e.getDistance());
  
 
//        _oppEnergy = e.getEnergy(); // MC (done in gun normally)
 
//        _oppEnergy = e.getEnergy(); // MC (done in gun normally)
  
         addCustomEvent(w = new Wave());
+
         (w = new Wave()).sourceLocation = _myLocation;
         setTurnRadarRightRadians(Utils.normalRelativeAngle((w.directAngle = _enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians()) - getRadarHeadingRadians()) * 2);
+
        addCustomEvent(w);
 +
         setTurnRadarRightRadians(Utils.normalRelativeAngle((w.directAngle = enemyAbsoluteBearing) - getRadarHeadingRadians()) * 2);
  
        _enemyLocation =
 
            project((w.sourceLocation = _myLocation = new Point2D.Double(getX(), getY())),
 
            _enemyAbsoluteBearing, _lastDistance = e.getDistance());
 
  
 
         // WaveSurfing /////////////////////////////////////////////////////////
 
         // WaveSurfing /////////////////////////////////////////////////////////
        int direction = (_lastLastOrientation = sign(_lastLatVel));
 
 
         try {
 
         try {
 
             _goAngle = (_surfWave = (Wave)(_enemyWaves.getFirst()))
 
             _goAngle = (_surfWave = (Wave)(_enemyWaves.getFirst()))
            .absoluteBearing(_myLocation) +
+
                .absoluteBearing(_myLocation) +
            A_LITTLE_LESS_THAN_HALF_PI *  
+
                A_LITTLE_LESS_THAN_HALF_PI *
                (direction = (sign(checkDanger(-1) - checkDanger(1))));
+
                    (direction = (sign(checkDanger(-1) - checkDanger(1))));
 
         } catch (Exception ex) { }
 
         } catch (Exception ex) { }
  
         // CREDIT: code by Iiley,
+
         // CREDIT: code by Iiley, optimized with idea from ChaseSan
 
         // http://robowiki.net?BackAsFront
 
         // http://robowiki.net?BackAsFront
         double angle = Utils.normalRelativeAngle(
+
         double angle;
        wallSmoothing(_myLocation, _goAngle, direction) - getHeadingRadians());
+
        setTurnRightRadians(Math.tan(angle =
         double turnAngle;
+
            wallSmoothing(_myLocation, _goAngle, direction) - getHeadingRadians()));
        setTurnRightRadians(turnAngle = Math.atan(Math.tan(angle)));
+
         setAhead(Math.cos(angle) * Double.POSITIVE_INFINITY);
        setAhead((angle == turnAngle) ? 100 : -100);
 
  
 
         ////////////////////////////////////////////////////////
 
         ////////////////////////////////////////////////////////
         // CREDIT: RaikoMicro's gun, by Jamougha
+
         // CREDIT: Originally based on RaikoMicro's gun, by Jamougha
 
         // http://robowiki.net?RaikoMicro
 
         // http://robowiki.net?RaikoMicro
  
Line 143: Line 146:
 
         // ------------- Fire control -------
 
         // ------------- Fire control -------
 
         double enemyLatVel;
 
         double enemyLatVel;
         _lastGunOrientation = w.orientation = sign(enemyLatVel = (e.getVelocity())*Math.sin(e.getHeadingRadians() - _enemyAbsoluteBearing));
+
         _lastGunOrientation = w.orientation = sign(enemyLatVel = (e.getVelocity())*Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));
  
 
         int bestGF = Math.min(3, (int)(Math.pow(280*lastVChangeTime++/_lastDistance, .7)));
 
         int bestGF = Math.min(3, (int)(Math.pow(280*lastVChangeTime++/_lastDistance, .7)));
Line 152: Line 155:
 
             bestGF = 4;
 
             bestGF = 4;
 
             if (enemyVelocity > newVelocity) {
 
             if (enemyVelocity > newVelocity) {
            bestGF = 5;
+
                bestGF = 5;
 
             }             
 
             }             
 
         }
 
         }
Line 171: Line 174:
 
         double power = 2 - Math.max(0, (30 - getEnergy()) / 16);
 
         double power = 2 - Math.max(0, (30 - getEnergy()) / 16);
 
//      double power = 3; // TC
 
//      double power = 3; // TC
         w.distance = -1.5 * (w.bulletVelocity = (20 - 3*power));
+
         w.distance = -1.5 * (w.bulletSpeed = Rules.getBulletSpeed(power));
  
         setTurnGunRightRadians(Utils.normalRelativeAngle(_enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*(Math.asin(8/w.bulletVelocity)/GF_ZERO))*(bestGF-GF_ZERO)) ));
+
         setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*(Math.asin(8/w.bulletSpeed)/GF_ZERO))*(bestGF-GF_ZERO)) ));
 
//        setTurnGunRightRadians(Utils.normalRelativeAngle(_enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*.035406)*(bestGF-GF_ZERO)) )); // TC
 
//        setTurnGunRightRadians(Utils.normalRelativeAngle(_enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*.035406)*(bestGF-GF_ZERO)) )); // TC
  
Line 184: Line 187:
  
 
         _lastLatVel = getVelocity()*Math.sin(e.getBearingRadians());
 
         _lastLatVel = getVelocity()*Math.sin(e.getBearingRadians());
        _lastLastAbsBearingRadians = _lastAbsBearingRadians;
+
         _goAngle = _lastAbsBearingRadians = enemyAbsoluteBearing + Math.PI;
         _goAngle = _lastAbsBearingRadians = _enemyAbsoluteBearing + Math.PI;
 
 
     }
 
     }
  
 
     public void onHitByBullet(HitByBulletEvent e) {
 
     public void onHitByBullet(HitByBulletEvent e) {
    _oppEnergy += e.getBullet().getPower() * 3;
+
        _oppEnergy += e.getBullet().getPower() * 3;
    if (_surfWave.distanceToPoint(_myLocation) - _surfWave.distance < 100) {
+
        logAndRemoveWave(_myLocation);
 +
    }
  
            // The waves are removed quickly enough that it's possible
+
    public void onBulletHitBullet(BulletHitBulletEvent e) {
             // I could be hit by a bullet on a wave that's been removed...
+
        logAndRemoveWave(new Point2D.Double(e.getBullet().getX(),
             // logging that could really screw up the stats.
+
             e.getBullet().getY()));
            logHit(_surfWave, _myLocation, 0.7);
+
  }
         }
+
 
 +
    public void logAndRemoveWave(Point2D.Double hitLocation) {
 +
        Wave w = _surfWave;
 +
        int x = 0;
 +
        do {
 +
             try {
 +
                if (Math.abs(w.distanceToPoint(hitLocation) - w.distance) < 100) {
 +
                    logHit(w, hitLocation, 0.85);
 +
                    _enemyWaves.remove(w);
 +
                    removeCustomEvent(w);
 +
                    return;
 +
                }
 +
                w = (Wave)_enemyWaves.get(x++);
 +
            } catch (Exception ex) { }
 +
         } while (x <= _enemyWaves.size());
 
     }
 
     }
   
+
 
 
     public void onBulletHit(BulletHitEvent e) {
 
     public void onBulletHit(BulletHitEvent e) {
    double bulletPower;
+
        _oppEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
    _oppEnergy -= (bulletPower = e.getBullet().getPower()) * 4 +
 
    (Math.max(0, bulletPower - 1) * 2);
 
 
     }
 
     }
  
Line 210: Line 225:
  
 
     public void onCustomEvent(CustomEvent e) {
 
     public void onCustomEvent(CustomEvent e) {
    removeCustomEvent(e.getCondition());
+
        removeCustomEvent(e.getCondition());
 
     }
 
     }
 
      
 
      
 
     public static void logHit(Wave w, Point2D.Double targetLocation, double rollingDepth) {
 
     public static void logHit(Wave w, Point2D.Double targetLocation, double rollingDepth) {
         for (int x = 46; x >= 0; x--) {
+
         for (int x = GF_ONE; x >= 0; x--) {
 
             w.waveGuessFactors[x] = ((w.waveGuessFactors[x] * rollingDepth)
 
             w.waveGuessFactors[x] = ((w.waveGuessFactors[x] * rollingDepth)
 
                 + ((1 + w.weight) / (Math.pow(x - getFactorIndex(w, targetLocation), 2) + 1)))
 
                 + ((1 + w.weight) / (Math.pow(x - getFactorIndex(w, targetLocation), 2) + 1)))
Line 226: Line 241:
 
             w.absoluteBearing(botLocation)
 
             w.absoluteBearing(botLocation)
 
             - w.directAngle) * w.orientation)
 
             - w.directAngle) * w.orientation)
             / Math.asin(8.0/w.bulletVelocity))
+
             / Math.asin(8.0/w.bulletSpeed))
             * (GF_ZERO)) + (GF_ZERO)), 46);
+
             * (GF_ZERO)) + (GF_ZERO)), GF_ONE);
 
     }
 
     }
  
 
     private double checkDanger(int direction) {
 
     private double checkDanger(int direction) {
         int index = getFactorIndex(_surfWave, predictPosition(direction));
+
         Wave surfWave = Komarious._surfWave;
 
+
         Point2D.Double predictedPosition = _myLocation;
         return (_surfWave.waveGuessFactors[index]
+
         double predictedHeading = getHeadingRadians();
            + .01 / (Math.abs(index - GF_ZERO) + 1))
 
            / Math.pow(_lastPredictedDistance, 4);
 
    }
 
 
 
    // CREDIT: mini sized predictor from Apollon, by rozu
 
    // http://robowiki.net?Apollon
 
    private Point2D.Double predictPosition(int direction) {
 
         Point2D.Double predictedPosition = (Point2D.Double)_myLocation;
 
 
         double predictedVelocity = getVelocity();
 
         double predictedVelocity = getVelocity();
        double predictedHeading = getHeadingRadians();
+
         double maxTurning, moveAngle, moveDir, lastPredictedDistance;
         double maxTurning, moveAngle, moveDir;
 
  
 
         // - actual distance traveled so far needs +bullet_velocity,
 
         // - actual distance traveled so far needs +bullet_velocity,
Line 258: Line 264:
  
 
             if (Math.cos(moveAngle =
 
             if (Math.cos(moveAngle =
                 wallSmoothing(predictedPosition, _surfWave.absoluteBearing(
+
                 wallSmoothing(predictedPosition, surfWave.absoluteBearing(
 
                         predictedPosition) + (direction * A_LITTLE_LESS_THAN_HALF_PI), direction)
 
                         predictedPosition) + (direction * A_LITTLE_LESS_THAN_HALF_PI), direction)
 
                         - predictedHeading) < 0) {
 
                         - predictedHeading) < 0) {
Line 276: Line 282:
 
             // calculate the new predicted position
 
             // calculate the new predicted position
 
             predictedPosition = project(predictedPosition,  
 
             predictedPosition = project(predictedPosition,  
            predictedHeading = Utils.normalRelativeAngle(predictedHeading +  
+
                predictedHeading = Utils.normalRelativeAngle(predictedHeading +
            limit(-(maxTurning = Math.PI/720d*(40d -
+
                    limit(-(maxTurning = Rules.getTurnRateRadians(Math.abs(predictedVelocity))),
            3d*Math.abs(predictedVelocity))),  
+
                        Utils.normalRelativeAngle(moveAngle), maxTurning)),
            Utils.normalRelativeAngle(moveAngle), maxTurning)),
+
                    (predictedVelocity = limit(-8,
                (predictedVelocity = limit(-8,
+
                    predictedVelocity + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
                predictedVelocity + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
+
                    8)));
                 8)));
+
 
 +
 
 +
        } while (
 +
                 (lastPredictedDistance = surfWave.distanceToPoint(predictedPosition)) >=
 +
                surfWave.distance + ((++counter) * surfWave.bulletSpeed));
  
            if ((_lastPredictedDistance =
+
        int index;
            _surfWave.distanceToPoint(predictedPosition)) <
+
        return (surfWave.waveGuessFactors[index = getFactorIndex(surfWave, predictedPosition)]
                _surfWave.distance + ((++counter) * _surfWave.bulletVelocity)) {
+
            + .01 / (Math.abs(index - GF_ZERO) + 1))
                return predictedPosition;
+
             / Math.pow(lastPredictedDistance, 4);
             }
 
    } while (true);
 
 
     }
 
     }
  
Line 304: Line 312:
  
 
     private static int sign(double d) {
 
     private static int sign(double d) {
    if (d < 0) { return -1; }
+
        if (d < 0) { return -1; }
    return 1;
+
        return 1;
 
     }
 
     }
  
Line 318: Line 326:
 
      
 
      
 
     private static boolean gunWallDistance(double wallDistance) {
 
     private static boolean gunWallDistance(double wallDistance) {
    return _fieldRect.contains(project(_myLocation, _enemyAbsoluteBearing + (_lastGunOrientation*wallDistance), _lastDistance));
+
        return _fieldRect.contains(project(_myLocation, _enemyAbsoluteBearing + (_lastGunOrientation*wallDistance), _lastDistance));
 
     }
 
     }
}
+
   
 +
    static class Wave extends Condition {
 +
        Point2D.Double sourceLocation;
 +
        double[] waveGuessFactors;
 +
        double bulletSpeed, directAngle, distance;
 +
        int orientation, weight;
 +
 
 +
        public double distanceToPoint(Point2D.Double p) {
 +
            return sourceLocation.distance(p);
 +
        }
  
class Wave extends Condition {
+
        public double absoluteBearing(Point2D.Double target) {
    Point2D.Double sourceLocation;
+
            return Math.atan2(target.x - sourceLocation.x, target.y - sourceLocation.y);
    double[] waveGuessFactors;
+
        }
    double bulletVelocity, directAngle, distance;
 
    int orientation, weight;
 
   
 
    public double distanceToPoint(Point2D.Double p) {
 
    return sourceLocation.distance(p);
 
    }
 
   
 
    public double absoluteBearing(Point2D.Double target) {
 
        return Math.atan2(target.x - sourceLocation.x, target.y - sourceLocation.y);
 
    }
 
  
    public boolean test() {
+
        public boolean test() {
    boolean isSurfed;
+
            if (distanceToPoint(Komarious._enemyWaves.contains(this)?
        if (distanceToPoint((isSurfed=Komarious._enemyWaves.contains(this))?
+
                    Komarious._myLocation:Komarious._enemyLocation)
        Komarious._myLocation:Komarious._enemyLocation)
+
                    <= (distance+=bulletSpeed) + (2 * bulletSpeed)) {
            <= (distance+=bulletVelocity) + (2*bulletVelocity)) {
 
 
//            logHit((Wave)_enemyWaves.removeFirst(),
 
//            logHit((Wave)_enemyWaves.removeFirst(),
 
//                Komarious._myLocation, 500);
 
//                Komarious._myLocation, 500);
        if (!isSurfed) {
+
                if (!Komarious._enemyWaves.remove(this)) {
                Komarious.logHit(this, Komarious._enemyLocation, 600);
+
                    Komarious.logHit(this, Komarious._enemyLocation, 600);
        }
+
                }
        Komarious._enemyWaves.remove(this);
 
  
        return true;
+
                return true;
 +
            }
 +
            return false;
 
         }
 
         }
        return false;
 
 
     }
 
     }
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
[[Category:Source Code]]
 
[[Category:Source Code]]

Latest revision as of 10:28, 1 July 2010

Sub-pages:
KomariousVersion History - Code - Archived Talk 20090424

This is the code to Komarious 1.842. Code size is 1492 when compiled with Jikes (and 3 colors = 13 bytes).


package voidious.mini;

import robocode.*;
import robocode.util.Utils;
import java.awt.geom.*;
import java.util.LinkedList;
import java.awt.Color;
import robocode.Rules;

/**
 * Komarious - a MiniBot by Voidious
 *
 * She's a duelist, and assumes it's a 1 on 1. This is a first attempt by
 * Voidious at making a mini-WaveSurfer.
 *
 * CREDITS:
 *   Jamougha - gun based on RaikoMicro 1.44
 *   rozu - mini-sized PrecisePrediction from Apollon
 *   Iiley - small codesize BackAsFront method
 *   PEZ - iterative WallSmoothing algorithm
 *
 * Code is open source, released under the RoboWiki Public Code License:
 * http://robowiki.net/cgi-bin/robowiki?RWPCL
 */

public class Komarious extends AdvancedRobot {
    ////////////////////////////////////////////////////////
    // Shared between movement and gun
    static final int GF_ZERO = 23;
    static final int GF_ONE = 46;
    public static Point2D.Double _myLocation;
    public static Point2D.Double _enemyLocation;
    private static double _lastDistance;
    private static double _enemyAbsoluteBearing;
    ////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////
    // Movement
    static final double WALL_STICK = 140;
    static final double A_LITTLE_LESS_THAN_HALF_PI = 1.25; // Math.PI/2 would be
        // perpendicular movement, less will keep us moving away slightly
    private static double _surfStats[][][] = new double[4][5][GF_ONE+1];
    public static LinkedList _enemyWaves;
    private static double _oppEnergy;
    private static Wave _surfWave;
    private static Wave _nextSurfWave;
    private static double _lastLatVel;
    private static double _lastAbsBearingRadians;
    private static double _goAngle;
    private static java.awt.geom.Rectangle2D.Double _fieldRect
        = new java.awt.geom.Rectangle2D.Double(18, 18, 764, 564);
    ////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////
    // Gun
    static final double LOG_BASE_E_TO_2_CONVERSION_CONSTANT = 1.4427;
    static double[][][][][][] _gunStats = new double[6][4][4][2][3][GF_ONE+1];
    static double lastVChangeTime;
    static int enemyVelocity;
    private static int _ramCounter;
    private static int _lastGunOrientation;
    ////////////////////////////////////////////////////////

    public void run() {
        setColors(Color.black, Color.black, Color.white);

        _enemyWaves = new LinkedList();
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);

        do {
            turnRadarRightRadians(1);
        } while (true);

    }

    public void onScannedRobot(ScannedRobotEvent e) {
        Wave w;
        int direction;

        double bulletPower;
        if ((bulletPower = _oppEnergy - e.getEnergy()) <= 3
            && bulletPower > 0) {
            (w = _nextSurfWave).bulletSpeed = Rules.getBulletSpeed(bulletPower);
            addCustomEvent(w);
            _enemyWaves.addLast(w);
        }
        (_nextSurfWave = w = new Wave()).directAngle = _lastAbsBearingRadians;
        w.waveGuessFactors = _surfStats[(int)(Math.min((_lastDistance+50)/200, 3))][(int)((Math.abs(_lastLatVel)+1)/2)];
        w.orientation = direction = sign(_lastLatVel);
        double enemyAbsoluteBearing;
        w.sourceLocation = _enemyLocation = 
            project((_myLocation = new Point2D.Double(getX(), getY())),
                    _enemyAbsoluteBearing = enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians(), _lastDistance = e.getDistance());

//        _oppEnergy = e.getEnergy(); // MC (done in gun normally)

        (w = new Wave()).sourceLocation = _myLocation;
        addCustomEvent(w);
        setTurnRadarRightRadians(Utils.normalRelativeAngle((w.directAngle = enemyAbsoluteBearing) - getRadarHeadingRadians()) * 2);


        // WaveSurfing /////////////////////////////////////////////////////////
        try {
            _goAngle = (_surfWave = (Wave)(_enemyWaves.getFirst()))
                .absoluteBearing(_myLocation) +
                A_LITTLE_LESS_THAN_HALF_PI *
                    (direction = (sign(checkDanger(-1) - checkDanger(1))));
        } catch (Exception ex) { }

        // CREDIT: code by Iiley, optimized with idea from ChaseSan
        // http://robowiki.net?BackAsFront
        double angle;
        setTurnRightRadians(Math.tan(angle = 
            wallSmoothing(_myLocation, _goAngle, direction) - getHeadingRadians()));
        setAhead(Math.cos(angle) * Double.POSITIVE_INFINITY);

        ////////////////////////////////////////////////////////
        // CREDIT: Originally based on RaikoMicro's gun, by Jamougha
        // http://robowiki.net?RaikoMicro

        /////
        // TC
/*
        Wave w;
        addCustomEvent(w = new Wave());
        _enemyLocation =
            project((w.sourceLocation = _myLocation = new Point2D.Double(getX(), getY())),
            _enemyAbsoluteBearing, _lastDistance = e.getDistance());
*/
        /////

        // ------------- Fire control -------
        double enemyLatVel;
        _lastGunOrientation = w.orientation = sign(enemyLatVel = (e.getVelocity())*Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));

        int bestGF = Math.min(3, (int)(Math.pow(280*lastVChangeTime++/_lastDistance, .7)));
//        int bestGF = Math.min(3, (int)(Math.sqrt(220D*lastVChangeTime++/_lastDistance))); // TC
        int newVelocity;
        if (enemyVelocity != (newVelocity = (int)(enemyLatVel = Math.abs(enemyLatVel)))) {
            lastVChangeTime = 0;
            bestGF = 4;
            if (enemyVelocity > newVelocity) {
                bestGF = 5;
            }            
        }
        enemyVelocity = newVelocity;
        
        
//        w.sourceLocation = _myLocation;
//        w.directAngle = _enemyAbsoluteBearing;
        w.waveGuessFactors = _gunStats[bestGF][(int)(LOG_BASE_E_TO_2_CONVERSION_CONSTANT * Math.log(enemyLatVel + 1.5))][gunWallDistance(0.18247367367) ? (gunWallDistance(0.36494734735) ? (gunWallDistance(0.63865785787) ? 3 : 2) : 1) : 0][gunWallDistance(-0.36494734735) ? 0 : 1][(int)limit(0, (_lastDistance-75)/200, 2)];
//        w.waveGuessFactors = _gunStats[bestGF][(int)(LOG_BASE_E_TO_2_CONVERSION_CONSTANT * Math.log(enemyLatVel + 1.5))][gunWallDistance(0.24430198263) ? (gunWallDistance(0.48860396528) ? (gunWallDistance(0.85505693924) ? 3 : 2) : 1) : 0][gunWallDistance(-0.48860396528) ? 0 : 1][(int)limit(0, (_lastDistance-75)/200, 2)]; // TC

        bestGF = GF_ZERO;

        for (int gf = GF_ONE; gf >= 0 && (_oppEnergy = e.getEnergy()) > 0; gf--) // Jamougha: Saves one byte compared to going up, weird
            if (w.waveGuessFactors[gf] > w.waveGuessFactors[bestGF])
                bestGF = gf;

        double power = 2 - Math.max(0, (30 - getEnergy()) / 16);
//      double power = 3; // TC
        w.distance = -1.5 * (w.bulletSpeed = Rules.getBulletSpeed(power));

        setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*(Math.asin(8/w.bulletSpeed)/GF_ZERO))*(bestGF-GF_ZERO)) ));
//        setTurnGunRightRadians(Utils.normalRelativeAngle(_enemyAbsoluteBearing - getGunHeadingRadians() + ((_lastGunOrientation*.035406)*(bestGF-GF_ZERO)) )); // TC

        if (Math.abs(getGunTurnRemaining()) < 3 && setFireBullet(power + (_ramCounter / (3*getRoundNum()+1))) != null) {
//        if (Math.abs(getGunTurnRemaining()) < 3 && setFireBullet(3) != null) { // TC
            w.weight = 4;
        }

        ////////////////////////////////////////////////////////

        _lastLatVel = getVelocity()*Math.sin(e.getBearingRadians());
        _goAngle = _lastAbsBearingRadians = enemyAbsoluteBearing + Math.PI;
    }

    public void onHitByBullet(HitByBulletEvent e) {
        _oppEnergy += e.getBullet().getPower() * 3;
        logAndRemoveWave(_myLocation);
    }

    public void onBulletHitBullet(BulletHitBulletEvent e) {
        logAndRemoveWave(new Point2D.Double(e.getBullet().getX(),
            e.getBullet().getY()));
  }
  
    public void logAndRemoveWave(Point2D.Double hitLocation) {
        Wave w = _surfWave;
        int x = 0;
        do {
            try {
                if (Math.abs(w.distanceToPoint(hitLocation) - w.distance) < 100) {
                    logHit(w, hitLocation, 0.85);
                    _enemyWaves.remove(w);
                    removeCustomEvent(w);
                    return;
                }
                w = (Wave)_enemyWaves.get(x++);
            } catch (Exception ex) { }
        } while (x <= _enemyWaves.size());
    }

    public void onBulletHit(BulletHitEvent e) {
        _oppEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
    }

    public void onHitRobot(HitRobotEvent event) {
        _ramCounter++;
    }

    public void onCustomEvent(CustomEvent e) {
        removeCustomEvent(e.getCondition());
    }
    
    public static void logHit(Wave w, Point2D.Double targetLocation, double rollingDepth) {
        for (int x = GF_ONE; x >= 0; x--) {
            w.waveGuessFactors[x] = ((w.waveGuessFactors[x] * rollingDepth)
                + ((1 + w.weight) / (Math.pow(x - getFactorIndex(w, targetLocation), 2) + 1)))
                / (rollingDepth + 1 + w.weight);
        }
    }

    private static int getFactorIndex(Wave w, Point2D.Double botLocation) {
        return (int)limit(0,
            ((((Utils.normalRelativeAngle(
            w.absoluteBearing(botLocation)
            - w.directAngle) * w.orientation)
            / Math.asin(8.0/w.bulletSpeed))
            * (GF_ZERO)) + (GF_ZERO)), GF_ONE);
    }

    private double checkDanger(int direction) {
        Wave surfWave = Komarious._surfWave;
        Point2D.Double predictedPosition = _myLocation;
        double predictedHeading = getHeadingRadians();
        double predictedVelocity = getVelocity();
        double maxTurning, moveAngle, moveDir, lastPredictedDistance;

        // - actual distance traveled so far needs +bullet_velocity,
        //    because it's detected one after being fired
        // - another +bullet_velocity b/c bullet advances before collisions
        // ...So start the counter at 2.
        // - another +bullet_velocity to approximate a bot's half width
        // ...So start the counter at 3.
        int counter = 3;

        do {
            moveDir = 1;

            if (Math.cos(moveAngle =
                wallSmoothing(predictedPosition, surfWave.absoluteBearing(
                        predictedPosition) + (direction * A_LITTLE_LESS_THAN_HALF_PI), direction)
                        - predictedHeading) < 0) {
                moveAngle += Math.PI;
                moveDir = -1;
            }

            // rozu comment:
            // 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 + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
//                8);

            // rozu comment:
            // calculate the new predicted position
            predictedPosition = project(predictedPosition, 
                predictedHeading = Utils.normalRelativeAngle(predictedHeading +
                    limit(-(maxTurning = Rules.getTurnRateRadians(Math.abs(predictedVelocity))),
                        Utils.normalRelativeAngle(moveAngle), maxTurning)),
                    (predictedVelocity = limit(-8,
                    predictedVelocity + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
                    8)));


        } while (
                (lastPredictedDistance = surfWave.distanceToPoint(predictedPosition)) >=
                surfWave.distance + ((++counter) * surfWave.bulletSpeed));

        int index;
        return (surfWave.waveGuessFactors[index = getFactorIndex(surfWave, predictedPosition)]
            + .01 / (Math.abs(index - GF_ZERO) + 1))
            / Math.pow(lastPredictedDistance, 4);
    }

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

    // CREDIT: ganked from CassiusClay, by PEZ
    // http://robowiki.net?CassiusClay
    private 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);
    }

    private static int sign(double d) {
        if (d < 0) { return -1; }
        return 1;
    }

    // CREDIT: Iterative WallSmoothing by PEZ
    // http://robowiki.net?WallSmoothing
    private static double wallSmoothing(Point2D.Double botLocation, double angle, int orientation) {
        while (!_fieldRect.contains(project(botLocation, angle, WALL_STICK))) {
            angle += orientation*0.05;
        }
        return angle;
    }
    
    private static boolean gunWallDistance(double wallDistance) {
        return _fieldRect.contains(project(_myLocation, _enemyAbsoluteBearing + (_lastGunOrientation*wallDistance), _lastDistance));
    }
    
    static class Wave extends Condition {
        Point2D.Double sourceLocation;
        double[] waveGuessFactors;
        double bulletSpeed, directAngle, distance;
        int orientation, weight;

        public double distanceToPoint(Point2D.Double p) {
            return sourceLocation.distance(p);
        }

        public double absoluteBearing(Point2D.Double target) {
            return Math.atan2(target.x - sourceLocation.x, target.y - sourceLocation.y);
        }

        public boolean test() {
            if (distanceToPoint(Komarious._enemyWaves.contains(this)?
                    Komarious._myLocation:Komarious._enemyLocation)
                    <= (distance+=bulletSpeed) + (2 * bulletSpeed)) {
//            logHit((Wave)_enemyWaves.removeFirst(),
//                Komarious._myLocation, 500);
                if (!Komarious._enemyWaves.remove(this)) {
                    Komarious.logHit(this, Komarious._enemyLocation, 600);
                }

                return true;
            }
            return false;
        }
    }
}