[Home]WSGunChallenge/KomariMover

Robo Home | WSGunChallenge | Changes | Preferences | AllPages

package wiki.kmove;

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

/**
 * KomariMover
 *
 * Movement of Komarious 1.37, by Voidious, 
 * for use in WaveSurferGunChallenge,
 *   http://robowiki.net?WSGunChallenge
 *
 * CREDITS:
 *   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 KomariMover {
    private AdvancedRobot _robot;
    
    private static double _surfStats[][][][] = new double[2][3][5][47];
    private static Point2D.Double _myLocation;
    private static Point2D.Double _enemyLocation;
    private static LinkedList _enemyWaves;
    private static double _oppEnergy;

    private static EnemyWave _surfWave;
    private static double _lastDistance;
    private static double _surfDistance;

    private static int _lastLastOrientation;
    private static double _lastLatVel;
    private static double _lastLastAbsBearingRadians;
    private static double _lastAbsBearingRadians;
    private static double _lastPredictedDistance;

    private static double _goAngle;

    private static java.awt.geom.Rectangle2D.Double _fieldRect
        = 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.35;
    static final double WALL_STICK = 140;
    static final int SURF_MIDDLE_BIN = 23;

	public KomariMover(AdvancedRobot robot) {
		_robot = robot;
		_enemyWaves = new LinkedList();
	}

    public void onScannedRobot(ScannedRobotEvent e) {
        double enemyAbsoluteBearing = _robot.getHeadingRadians() + e.getBearingRadians();

        double bulletPower;
        if ((bulletPower = _oppEnergy - e.getEnergy()) <= 3
            && bulletPower > 0) {
            EnemyWave ew;
            _robot.addCustomEvent(ew = new EnemyWave());
            ew.bulletVelocity = bulletVelocity(bulletPower);
            ew.directAngle = _lastLastAbsBearingRadians;
            ew.sourceLocation = _enemyLocation;
            ew.orientation = _lastLastOrientation;
            ew.waveGuessFactors = _surfStats[_fieldRect.contains(project(_enemyLocation, _lastLastAbsBearingRadians + (_lastLastOrientation*maxEscapeAngle(ew.bulletVelocity)/2), _lastDistance))?0:1][(int)(Math.min(_lastDistance/250, 2))][(int)((Math.abs(_lastLatVel))/2)];
            _enemyWaves.add(ew);
        }

        _oppEnergy = e.getEnergy();

        _enemyLocation =
            project((_myLocation = new Point2D.Double(_robot.getX(), _robot.getY())),
            enemyAbsoluteBearing, _lastDistance = e.getDistance());

        // WaveSurfing /////////////////////////////////////////////////////////
        int direction = (_lastLastOrientation = sign(_lastLatVel));
        if (!_enemyWaves.isEmpty()) {
            double angleFromWaveSource = absoluteBearing(
                (_surfWave = (EnemyWave)(_enemyWaves.getFirst())).sourceLocation,
                _myLocation);

            _goAngle = angleFromWaveSource +
                directedGoAngle(direction = (sign(checkDanger(-1) - checkDanger(1))));
        }

        moveWithBackAsFront(wallSmoothing(_myLocation, _goAngle, direction));

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

    public void onHitByBullet(HitByBulletEvent e) {
        if (!_enemyWaves.isEmpty() && (_surfDistance < 100)) {

            // The waves are removed quickly enough that it's possible
            // I could be hit by a bullet on a wave that's been removed...
            // logging that could really screw up the stats.
            logHit(_surfWave, _myLocation, 2);
        }
    }

    public void logHit(Wave w, Point2D.Double targetLocation, int rollingDepth) {
        for (int x = 46; 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(
            absoluteBearing(w.sourceLocation, botLocation)
            - w.directAngle) * w.orientation)
            / maxEscapeAngle(w.bulletVelocity))
            * (SURF_MIDDLE_BIN)) + (SURF_MIDDLE_BIN)), 46);
    }

    private double checkDanger(int direction) {
        int index = getFactorIndex(_surfWave, predictPosition(direction));

        return (_surfWave.waveGuessFactors[index]
            + .01 / (Math.abs(index - SURF_MIDDLE_BIN) + 1))
            * Math.pow(_lastDistance/_lastPredictedDistance, 4);
    }

    private double directedGoAngle(int direction) {
        return (direction * A_LITTLE_LESS_THAN_HALF_PI);
    }

    // CREDIT: mini sized predictor from Apollon, by rozu
    // http://robowiki.net?Apollon
    private Point2D.Double predictPosition(int direction) {
        Point2D.Double predictedPosition = (Point2D.Double)_myLocation.clone();
        double predictedVelocity = _robot.getVelocity();
        double predictedHeading = _robot.getHeadingRadians();
        double maxTurning, moveAngle, moveDir;

        // - 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.
        int counter = 2;

        do {
            moveAngle =
                wallSmoothing(predictedPosition, absoluteBearing(_surfWave.sourceLocation,
                predictedPosition) + (direction * (A_LITTLE_LESS_THAN_HALF_PI)), 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 + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
//                8);

            // calculate the new predicted position
            predictedPosition = project(predictedPosition, predictedHeading,
                (predictedVelocity = limit(-8,
                predictedVelocity + (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir),
                8)));

            if ((_lastPredictedDistance =
                predictedPosition.distance(_surfWave.sourceLocation)) - 15 <
                _surfWave.distance + ((++counter) * _surfWave.bulletVelocity)) {
                break;
            }
    	} while(true);

        return predictedPosition;
    }

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

    private static double bulletVelocity(double power) {
        return (20.0 - (3.0*power));
    }

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

    // 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 double absoluteBearing(Point2D.Double source, Point2D.Double target) {
        return Math.atan2(target.x - source.x, target.y - source.y);
    }

    static int sign(double d) {
        return (d >= 0) ? 1 : -1;
    }

    // CREDIT: code by Iiley,
    // http://robowiki.net?BackAsFront
    void moveWithBackAsFront(double bearing) {
        double angle = Utils.normalRelativeAngle(bearing - _robot.getHeadingRadians());
        double turnAngle;
        _robot.setTurnRightRadians(turnAngle = Math.atan(Math.tan(angle)));
        _robot.setAhead((angle == turnAngle) ? 100 : -100);
    }

    // CREDIT: Iterative WallSmoothing by PEZ
    // http://robowiki.net?WallSmoothing
    public double wallSmoothing(Point2D.Double botLocation, double angle, int orientation) {
        while (!_fieldRect.contains(project(botLocation, angle, WALL_STICK))) {
            angle += orientation*0.05;
        }
        return angle;
    }

    abstract class Wave extends Condition {
        Point2D.Double sourceLocation;
        double[] waveGuessFactors;
        double bulletVelocity, directAngle, distance;
        int orientation, weight;
    }
    
    class EnemyWave extends Wave {
        public boolean test(){
            if ((KomariMover._myLocation).distance(sourceLocation)
                <= (distance+=bulletVelocity) + (2*bulletVelocity)) {
                _enemyWaves.removeFirst();
                _robot.removeCustomEvent(this);
            }
            return false;
        }
    }
}

Robo Home | WSGunChallenge | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited July 22, 2006 0:06 EST by Voidious (diff)
Search: