[Home]VertiLeach/Code

Robo Home | VertiLeach | Changes | Preferences | AllPages

Source code for VertiLeach V 0.3.3, released under the RoboWiki Public Code Licence (RWPCL).

There are a couple of things that might be interesting with this code:

  1. It's from a top-10 class bot in RoboRumble@Home (and top-2 class in the MiniBots class)
  2. It uses a simple GuessFactorTargeting gun
  3. Rather few lines of code. Even if it has some improvements to ask for in terms of clarity, it isn't as obfuscated as some minibot code out there.

This source code is also included in the bot jar file: http://www.robocoderepository.com/Controller.jsp?submitAction=downloadClass&id=1744

package pez.mini;
import robocode.*;
import robocode.util.Utils;
import java.awt.geom.*;
import java.awt.Color;
import java.util.*;
import java.util.zip.*;
import java.io.*;

// VertiLeach, by PEZ. Stays close to you and follows your vertical movements
//
// This code is released under the RoboWiki Public Code Licence (RWPCL), datailed on:
// http://robowiki.net/?RWPCL
//
// Code home page: http://robowiki.net/?VertiLeach/Code
//
// $Id: VertiLeach.java,v 1.9 2003/12/19 00:43:56 peter Exp $

public class VertiLeach extends AdvancedRobot {
    private static final double MAX_VELOCITY = 8;
    private static final double DEFAULT_DISTANCE = 300;
    private static final double DEFAULT_BULLET_POWER = 2.8;
    private static final double WALL_MARGIN = 50;
    private static final int DIRECTION_SEGMENTS = 3;
    private static final int VERTICAL_SEGMENTS = 9;
    private static final int AIM_FACTORS = 23;
    private static Point2D robotLocation = new Point2D.Double();
    private static Point2D lastRobotLocation = new Point2D.Double();
    private static Point2D lastEnemyLocation = new Point2D.Double();
    private static Point2D enemyLocation = new Point2D.Double();
    private static double robotEnergy;
    private static String enemyName = "";
    private static double enemyDistance;
    private static double enemyEnergy;
    private static double enemyAbsoluteBearing;
    private static double enemyY;
    private static double enemyYNormal;
    private static double lastEnemyYNormal;
    private static double maxEnemyBearing;
    private static double deltaBearing;
    private static int bearingDirection;
    private static double yOffset;
    private static double midY;
    private static int[][][] aimFactors;
    private static double bulletPower = DEFAULT_BULLET_POWER;
    private static int mostVisited;
    private int[] currentAimFactors;

    public void run() {
        setColors(Color.red.darker().darker(), Color.green.darker().darker(), Color.gray.darker().darker());
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);
	midY = getBattleFieldHeight() / 2;

        do {
            turnRadarRightRadians(Double.POSITIVE_INFINITY); 
        } while (true);
    }

    public void onScannedRobot(ScannedRobotEvent e) {
        if (enemyName == "") {
            enemyName = e.getName();
            restoreFactors();
        }
        lastRobotLocation.setLocation(robotLocation);
        robotLocation.setLocation(getX(), getY());
        enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
        enemyDistance = e.getDistance();
        lastEnemyLocation.setLocation(enemyLocation);
        toLocation(enemyAbsoluteBearing, enemyDistance, robotLocation, enemyLocation);

	lastEnemyYNormal = enemyYNormal;
	enemyYNormal = enemyY = enemyLocation.getY();
	if (enemyY > midY) {
	    enemyYNormal = midY - (enemyY - midY);
	}

	double eDelta = enemyEnergy - e.getEnergy();
	if (eDelta >= 0.1 && eDelta <= 3.0) {
	    double maxOffset = 175 + (DEFAULT_DISTANCE - Math.min(DEFAULT_DISTANCE, enemyDistance));
	    yOffset = 2 * maxOffset * Math.random() - maxOffset;
	    if (aimVerticalSegment() < 1) {
		yOffset *= 0.5 * Math.abs(yOffset) * sign(midY - enemyY);
	    }
	}
        enemyEnergy = e.getEnergy();
        robotEnergy = getEnergy();

	if (getTime() > 8) {
	    move();

	    deltaBearing = Utils.normalRelativeAngle(absoluteBearing(lastRobotLocation, enemyLocation) -
		    absoluteBearing(lastRobotLocation, lastEnemyLocation));
	    if (deltaBearing < 0) {
		bearingDirection = -1;
	    }
	    else if (deltaBearing > 0) {
		bearingDirection = 1;
	    }
	    bulletPower = (enemyDistance < 2 * DEFAULT_DISTANCE / 3) ? 3.0 : DEFAULT_BULLET_POWER;
	    bulletPower = Math.min(robotEnergy / 5, Math.min(enemyEnergy / 4, bulletPower));
	    maxEnemyBearing = Math.abs(Math.asin(MAX_VELOCITY / (bulletVelocity(bulletPower))));

	    currentAimFactors = aimFactors[aimDirectionSegment()][aimVerticalSegment()];

	    setTurnGunRightRadians(Utils.normalRelativeAngle(
			enemyAbsoluteBearing + maxEnemyBearing * bearingDirection * mostVisitedFactor() - getGunHeadingRadians()));

	    if (setFireBullet(bulletPower) != null && bulletPower == DEFAULT_BULLET_POWER) {
		addCustomEvent(new Wave());
	    }
	}

        setTurnRadarRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getRadarHeadingRadians()) * 2);
    }

    public void onWin(WinEvent e) {
        saveFactors();
    }

    public void onDeath(DeathEvent e) {
        saveFactors();
    }

    private void move() {
	Point2D robotDestination = null;
	double direction = 1;
	for (int i = 0; i < 2; i++) {
	    double X = enemyLocation.getX() + direction * side() * DEFAULT_DISTANCE;
	    robotDestination = new Point2D.Double(X, enemyLocation.getY() + yOffset);
	    translateInsideField(robotDestination, WALL_MARGIN);
	    if (Math.abs(X - robotDestination.getX()) < 100) {
		break;
	    }
	    else {
		direction *= -1;
	    }
	}
        goTo(robotDestination);
    }

    void goTo(Point2D destination) {
        double angle = Utils.normalRelativeAngle(absoluteBearing(robotLocation, destination) - getHeadingRadians());
	double turnAngle = Math.atan(Math.tan(angle));
        int direction = angle == turnAngle ? 1 : - 1;
        setTurnRightRadians(turnAngle);
        setAhead(robotLocation.distance(destination) * direction);
    }

    int side() {
	return sign(getX() - enemyLocation.getX());
    }

    double mostVisitedFactor() {
        mostVisited = (AIM_FACTORS - 1) / 2;
        for (int i = 0; i < AIM_FACTORS; i++) {
            if (currentAimFactors[i] > currentAimFactors[mostVisited]) {
                mostVisited = i;
            }
        }
	return ((mostVisited + 0.5) / AIM_FACTORS) * 2 - 1;
    }

    void registerFactorVisit(int[] factors, int index) {
	index = Math.min(Math.max(0, index), AIM_FACTORS - 1);
	if (factors[mostVisited] > 100) {
	    for (int i = 0; i < AIM_FACTORS; i++) {
		if (factors[i] > 1) {
		    factors[i] -= 2;
		}
	    }
	}
	factors[index] += 2;
	if (index < AIM_FACTORS - 1) {
	    factors[index + 1]++;
	}
	if (index > 0) {
	    factors[index - 1]++;
	}
    }

    private int aimVerticalSegment() {
	int segment = Math.min((int)(enemyYNormal / (midY / VERTICAL_SEGMENTS)), VERTICAL_SEGMENTS - 1);
	return segment;
    }

    private int aimDirectionSegment() {
        double yDelta = enemyYNormal - lastEnemyYNormal;
        if (yDelta < 0) {
            return 0;
        }
        else if (yDelta > 0) {
            return 2;
        }
        return 1;
    }

    private void translateInsideField(Point2D point, double margin) {
        point.setLocation(Math.max(margin, Math.min(getBattleFieldWidth() - margin, point.getX())),
                          Math.max(margin, Math.min(getBattleFieldHeight() - margin, point.getY())));
    }

    private double bulletVelocity(double power) {
        return 20 - 3 * power;
    }

    private int sign(double v) {
        return v > 0 ? 1 : -1;
    }

    private void toLocation(double angle, double length, Point2D sourceLocation, Point2D targetLocation) {
        targetLocation.setLocation(sourceLocation.getX() + Math.sin(angle) * length,
            sourceLocation.getY() + Math.cos(angle) * length);
    }

    private double absoluteBearing(Point2D source, Point2D target) {
        return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
    }

    void restoreFactors() {
        try {
            ZipInputStream zipin = new ZipInputStream(new
                FileInputStream(getDataFile(enemyName)));
            zipin.getNextEntry();
            ObjectInputStream in = new ObjectInputStream(zipin);
            aimFactors = (int[][][])in.readObject();
            in.close();
        }
        catch (Exception e) {
            aimFactors = new int[DIRECTION_SEGMENTS][VERTICAL_SEGMENTS][AIM_FACTORS];
        }
    }

    void saveFactors() {
        try {
            ZipOutputStream zipout = new ZipOutputStream(new RobocodeFileOutputStream(getDataFile(enemyName)));
            zipout.putNextEntry(new ZipEntry("aimFactors"));
            ObjectOutputStream out = new ObjectOutputStream(zipout);
            out.writeObject(aimFactors);
            out.flush();
            zipout.closeEntry();
            out.close();
        }
        catch (IOException e) {
        }
    }

    class Wave extends Condition {
        private long wTime;
        private double wBulletVelocity;
        private double wBearingDelta;
        private Point2D wRobotLocation = new Point2D.Double();
        private Point2D wEnemyLocation = new Point2D.Double();
	private int wBearingDirection;
        private double wMaxBearing;
        int[] wAimFactors;

        public Wave() {
            this.wTime = getTime();
            this.wBulletVelocity = bulletVelocity(bulletPower);
            this.wBearingDelta = deltaBearing;
            this.wRobotLocation.setLocation(robotLocation);
            this.wEnemyLocation.setLocation(enemyLocation);
            this.wMaxBearing = maxEnemyBearing;
            this.wAimFactors = currentAimFactors;
	    this.wBearingDirection = bearingDirection;
        }

        public boolean test() {
            if (wBulletVelocity * (getTime() - wTime) > wRobotLocation.distance(enemyLocation) - 10) {
                double bearingDiff = Utils.normalRelativeAngle(absoluteBearing(wRobotLocation, enemyLocation) -
                    absoluteBearing(wRobotLocation, wEnemyLocation));
		registerFactorVisit(wAimFactors, (int)Math.round(((((wBearingDirection * bearingDiff) /
		    wMaxBearing) + 1) / 2) * wAimFactors.length));
                removeCustomEvent(this);
            }
            return false;
        }
    }
}

Comments, suggestions and questions are welcome


Robo Home | VertiLeach | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited December 19, 2003 13:48 EST by PEZ (diff)
Search: