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, * https://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: * https://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 // https://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 // https://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, // https://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 // https://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; } } }