Please do not enter modified versions of this bot in the SpinBotChallenge, although you are welcome to use my ideas in your own bot for the challenge.
/* Created on Oct 13, 2004 David Alves presents _____ __ .__ _________ .__ __________ __ / _ \ ____ _/ |_ |__| / _____/______ |__| ____ \______ \ ____ _/ |_ / /_\ \ / \ \ __\| | \_____ \ \____ \ | | / \ | | _/ / _ \ \ __\ . / | \| | \ | | | | / \| |_> >| || | \ | | \( <_> ) | | \____|__ /|___| / |__| |__|/_______ /| __/ |__||___| / |______ / \____/ |__| \/ \/ \/ |__| \/ \/ ____ ______ |_ | | | _| |_ __ | -- | |______||__||______| */ package davidalves.robot; import java.awt.Color; import java.awt.geom.*; import robocode.*; /** * @author David Alves * */ public class AntiSpinBot extends Robot{ static final boolean firingEnabled = true; static final boolean movingEnabled = true; static final int ENEMY_FIREPOWER = 3; static final int ENEMY_BULLET_SPEED = 20 - 3 * ENEMY_FIREPOWER; //Time SpinBot takes to drive a complete circle. static final int ENEMY_CIRCLE_TIME = 58; //SpinBot fires once per revolution static final int ENEMY_FIRE_FREQUENCY = ENEMY_CIRCLE_TIME; //Size of the circle that SpinBot drives in static final int ENEMY_TURN_CIRCLE_RADIUS = 46; //We stay at less than this distance to ensure that there will only be one enemy bullet in the air at a time static final int MAX_DISTANCE = (ENEMY_FIRE_FREQUENCY - 12) * ENEMY_BULLET_SPEED; //We need at least 30 ticks to dodge safely static final int MIN_DISTANCE = 30 * ENEMY_BULLET_SPEED; //If we only have 24 ticks or less to dodge each bullet, //forget about dodging and just drive far away static final int PANIC_DISTANCE = 19 * ENEMY_BULLET_SPEED; //try to maintain this distance static final int IDEAL_DISTANCE = (MAX_DISTANCE + MIN_DISTANCE) / 2; static Rectangle2D field; static double enemyEnergy; static long lastScanTime = 0; static long lastFireTime = 0; static long nextFireTime; static double fireRange; static boolean lastDirectionWasClockwise; static double enemyHeadingAtMoveStart; static double enemyHeadingDegrees; static double enemyAngleDegrees; Point2D.Double enemy, circleCenter, fire; //A "death hop" is a dodge made after spinbot dies... in case he fired right before dying and we didn't notice //This variable ensures we only do one death hop per round. static boolean okToDeathHop = true; //stats static int skippedTurns; static int damageTaken; static int ramCount; static String state = ""; public void run(){ //My colors, used on all my bots setColors(Color.darkGray, Color.darkGray, Color.cyan); enemyHeadingAtMoveStart = Double.NaN; enemyHeadingDegrees = Double.NaN; okToDeathHop = true; fireRange = 0; enemyEnergy = 100; lastFireTime = -100; nextFireTime = 30; lastScanTime = -100; enemy = null; circleCenter = null; fire = null; enemyAngleDegrees = Double.NaN; setAdjustGunForRobotTurn(true); setAdjustRadarForGunTurn(true); setAdjustRadarForRobotTurn(true); field = new Rectangle2D.Double(18,18, getBattleFieldWidth() - 36, getBattleFieldHeight() - 36); do{ //First find SpinBot state = "find SpinBot"; findSpinBot(); //Sit still until enemy fires, then we dodge. If moving is disabled we never leave this loop, //and therefore never dodge. while(movingEnabled == false || getTime() - lastFireTime > 10){ //Sit still and sweep radar back and forth across enemy. Fire if gun is cool. state = "sit still"; sitStill(); //If enemy is very close to us, retreat to bullet-dodging range. if(circleCenter != null && circleCenter.distance(getX(), getY()) < PANIC_DISTANCE){ print("SpinBot is too close... retreating"); break; } Point2D.Double me = new Point2D.Double(getX(), getY()); if( normalRelativeAngle(enemyHeadingAtMoveStart - absoluteBearing(enemy, me)) < 0 && normalRelativeAngle(enemyHeadingDegrees - absoluteBearing(enemy, me)) > 0){ long detectedTime = Math.round(getTime() - normalRelativeAngle(enemyHeadingDegrees - absoluteBearing(enemy, me)) / (360.0 / ENEMY_CIRCLE_TIME) ); if(Math.abs(detectedTime - lastFireTime) > 10){ print("SpinBot fired while I was moving!! estimated time: " + detectedTime); break; } } } enemyHeadingAtMoveStart = enemyHeadingDegrees; //Dodge goTo(getDodgePoint()); }while(true); } public void onScannedRobot(ScannedRobotEvent e) { double enemyAngleRadians = Math.toRadians(getHeading()) + e.getBearingRadians(); enemyAngleDegrees = Math.toDegrees(enemyAngleRadians); double distance = e.getDistance(); enemy = new Point2D.Double( getX() + Math.sin(enemyAngleRadians) * distance, getY() + Math.cos(enemyAngleRadians) * distance); enemyHeadingDegrees = e.getHeading(); boolean enemyHeadingIndicatesFired = Math.abs(normalRelativeAngle(absoluteBearing(enemy, new Point2D.Double(getX(), getY())) - e.getHeading())) < 10; boolean enemyEnergyIndicatesFiredAtStartOfRound = lastScanTime == -100 && e.getEnergy() <= 97.0; boolean fired = enemyEnergyIndicatesFiredAtStartOfRound || enemyHeadingIndicatesFired; if(fired){ fire = new Point2D.Double(enemy.x, enemy.y); lastFireTime = getTime(); print("Enemy fired at range " + distance); fireRange = distance; } enemyEnergy = e.getEnergy(); //The point that SpinBot is orbiting circleCenter = project(enemy, ENEMY_TURN_CIRCLE_RADIUS, e.getHeading() + 90); lastScanTime = e.getTime(); } private Point2D getDodgePoint(){ Point2D.Double enemyCircleCenter = circleCenter; double angleOffset; double dodgeDistance; double distance = enemyCircleCenter.distance(getX(), getY()); if(distance < PANIC_DISTANCE) { //out.println("< min"); state = "panic move - " + distance; angleOffset = 140; dodgeDistance = 170; } else if(distance < MIN_DISTANCE) { state = "min move - " + distance; //out.println("< min"); angleOffset = 140; dodgeDistance = 120; } else if(distance < IDEAL_DISTANCE){ state = "less than ideal move - " + distance; //out.println("< ideal"); angleOffset = 95; dodgeDistance = 65; } else if (distance < MAX_DISTANCE){ state = "greater than ideal move - " + distance; //out.println("< max"); angleOffset = 75; dodgeDistance = 65; } else { //print("> max"); state = "greater than max move - " + distance; angleOffset = 55; dodgeDistance = 100; } Point2D.Double me = new Point2D.Double(getX(), getY()); double angleToTarget = absoluteBearing(me, circleCenter); Point2D cw, ccw; int smoothCW = 0; //How much we have to wallsmooth to drive clockwise do{ cw = project(me, dodgeDistance, angleToTarget + angleOffset - smoothCW * 3); smoothCW++; }while(!field.contains(cw)); int smoothCCW = 0; //How much we have to wallsmooth to drive counter-clockwise do{ ccw = project(me, dodgeDistance, angleToTarget - angleOffset + smoothCCW * 3); smoothCCW++; }while(!field.contains(ccw)); //Keep traveling in the same direction unless it forces us to wallsmooth a lot more than reversing if(lastDirectionWasClockwise){ if(smoothCW <= smoothCCW + 3){ return cw; } else { lastDirectionWasClockwise = false; return ccw; } } else { if(smoothCCW <= smoothCW + 3){ return ccw; } else { lastDirectionWasClockwise = true; return cw; } } } public void findSpinBot(){ while(lastScanTime != getTime()) turnRadarRight(44.9); } public void sitStill(){ //if(firingEnabled && getGunHeat() == 0.0 && getTime() - lastScanTime <= 1 && enemy.distance(getX(), getY()) > MIN_DISTANCE){ if(firingEnabled && getGunHeat() == 0.0 && getTime() - lastScanTime <= 1 && circleCenter.distance(getX(), getY()) > MIN_DISTANCE){ state = "aim and fire"; gun(); return; } state = "radar"; /*if(getTime() - lastScanTime >= 3){ turnRadarRight(44.9); } else {*/ double radarTurnAngle = normalRelativeAngle(enemyAngleDegrees - getRadarHeading()); if(radarTurnAngle > 0){ radarTurnAngle = Math.min(44.9, radarTurnAngle + 15); } else { radarTurnAngle = Math.max(-44.9, radarTurnAngle - 15); } turnRadarRight(radarTurnAngle); } //} public void gun(){ double gunTurnTime = Math.ceil(Math.abs(normalRelativeAngle(getGunHeading() - getAbsoluteAngleDegrees(getX(), getY(), enemy.x, enemy.y)))/ 10.0); double distanceBulletTraveled = - 11 * gunTurnTime; double enemyPositionOnCircle = absoluteBearing(circleCenter, enemy); Point2D.Double projected; //print("gunTurnTime: " + gunTurnTime); if(enemyEnergy != 0.0){ do { distanceBulletTraveled += 11.0; enemyPositionOnCircle += 360.0 / ENEMY_FIRE_FREQUENCY; projected = project(circleCenter, ENEMY_TURN_CIRCLE_RADIUS, enemyPositionOnCircle); }while (projected.distance(getX(), getY()) > distanceBulletTraveled); } else { projected = enemy; } //print("enemy: " + enemy); //print("projected: " + projected); double gunAngle = normalRelativeAngle(getAbsoluteAngleDegrees(getX(), getY(), projected.x, projected.y) - getGunHeading()); if(gunTurnTime < 5){ turnGunRight(gunAngle); } else { turnGunRight(Math.max(Math.min(gunAngle, 50), -50)); return; } fire(3); } private void goTo(Point2D destination) { Point2D location = new Point2D.Double(getX(), getY()); double distance = location.distance(destination); double angle = normalRelativeAngle(absoluteBearing(location, destination) - getHeading()); if (Math.abs(angle) > 90) { distance *= -1; if (angle > 0) { angle -= 180; } else { angle += 180; } } turnRight(angle); ahead(distance); } private void print(String s){ out.println(getRoundNum() + ":" + getTime() + ": " + s); } private double getAbsoluteAngleDegrees(double x1, double y1, double x2, double y2){ return Math.toDegrees(Math.atan2(x2 - x1, y2 - y1)); } private double absoluteBearing(Point2D source, Point2D target) { return Math.toDegrees(Math.atan2(target.getX() - source.getX(), target.getY() - source.getY())); } private double normalRelativeAngle(double angle) { angle = Math.toRadians(angle); return Math.toDegrees(Math.atan2(Math.sin(angle), Math.cos(angle))); } private Point2D.Double project(Point2D source, double distance, double angle){ return new Point2D.Double(source.getX() + distance * Math.sin(Math.toRadians(angle)), source.getY() + distance * Math.cos(Math.toRadians(angle))); } public void onRobotDeath(RobotDeathEvent e){ enemyEnergy = 0; if(withinExpectedFireTime()) lastFireTime = e.getTime(); } public boolean withinExpectedFireTime(){ return Math.abs(getTime() - (lastFireTime + ENEMY_FIRE_FREQUENCY)) < 10; } public void onSkippedTurn(SkippedTurnEvent e){ skippedTurns ++; } public void onWin(WinEvent e){ onDeath(null); } public void onDeath(DeathEvent e){ print("skipped turns: " + skippedTurns); print("damage taken: " + damageTaken); print("times rammed: " + ramCount); } public void onHitByBullet(HitByBulletEvent e){ damageTaken += 4 * e.getPower(); if(e.getPower() > 1) damageTaken += 2 * (e.getPower() - 1); if(lastScanTime != e.getTime()){ enemyEnergy += 3 * e.getPower(); } print("hit by bullet of power " + e.getPower() + " at distance " + fireRange + " while in state: " + state); } public void onHitRobot(HitRobotEvent e){ ramCount++; enemyEnergy = e.getEnergy(); } public void onBulletHit(BulletHitEvent e){ enemyEnergy = e.getEnergy(); } }
I just want to say it's amazing, you are crazy, its the most amazing complex robot extends Robot i had seen. And the code is beautiful:). -- iiley
I'm as amazed as iiley is. How does this bot fare in the ExtendsRobotCompetition? -- PEZ