[Home]OrcaM/Code

Robo Home | OrcaM | Changes | Preferences | AllPages

This is the bot source of OrcaM 0.2. To compile it you will need a copy of NRLIBJ, patched to worke with Robocode. Albert has done the paching already so no need to do it again. The patched version is included in the jar of the uploaded OrcaM. -- PEZ

There are some immediate issues with this code:

All input is entirely welcome. Please download the OrcaM jar and hack away and test different aproaches. -- PEZ
package wiki.nn;
import robocode.*;
import wiki.nn.nrlibj.*;
import java.awt.geom.*;
import java.awt.Color;
import java.util.*;

// OrcaM, A multi-smart killer. By PEZ.
//
// OrcaM is open source under GPL-ish conditions.
// You must share any improvements you do to the code.
// You must include this header with any code based on this code.
// If you pick minor parts of the code you can, if you like, close source it.
// But please give credit to the authors of this code.
//
// Home page of this bot is: http://robowiki.dyndns.org/?OrcaM
// The code should be available there and it is also the place for you to share any
// code improvements.
//
// $Id: OrcaM.java,v 1.1 2003/04/21 19:53:27 peter Exp $

public class OrcaM extends AdvancedRobot {
    private final static double MAX_BEARING_DIFF = 45;
    private final static double MAX_VELOCITY = 8;
    private final static double MAX_BEARING_DELTA = 7;
    private final static double MAX_DISTANCE_DELTA = 11;
    private final static double MAX_HEADING_DELTA = 6;
    private final static int HISTORY_DEPTH = 2;
    private final static int HISTORY_ITEMS = 2;
    private final static String NNdescr[] = {
        "layer=0 tnode=8 nname=NodeLin",
        "layer=1 tnode=18 nname=NodeSigm",
        "layer=2 tnode=10 nname=NodeSigm",
        "layer=3 tnode=1 nname=NodeSigm",
        "linktype=all fromlayer=0 tolayer=1",
        "linktype=all fromlayer=1 tolayer=2",
        "linktype=all fromlayer=2 tolayer=3"
    };
    private final static int NN_CANDIDATES = 7;
    private final static double MIN_NETS_IN_TRAINING = 2;
    private final static int MAX_TRAININGS = 1000;
    private final static int TRAINING_SESSION_LENGTH = 50;
    private final static double NN_CANDIDATE_RATE_DEPTH = 50;
    private final static double ALLOWED_HIT_RATE_DEGRADATION = -5.0;
    private final static double MIN_ACCEPTED_HITRATE = 7;
    private final static double MAX_HIT_RATE = 100;

    private static Point2D location = new Point2D.Double();
    private static Point2D oldLocation = new Point2D.Double();
    private static Point2D enemyLocation = new Point2D.Double();
    private static Point2D oldEnemyLocation = new Point2D.Double();
    private static Rectangle2D fieldRectangle;
    private static double velocity;
    private boolean haveEnemy;
    private static String enemyName;
    private double enemyDistance;
    private double enemyDistanceDelta;
    private double enemyHeading;
    private double enemyHeadingDelta;
    private double enemyEnergy;
    private double enemyVelocity;
    private double absoluteBearing;
    private double enemyBearingDelta;
    private double bulletPower;
    private long roundNum;
    private boolean roundOver;
    private int waitBeforeRam;
    private static long time;
    private static long wins;
    private static long shots;
    private static long skippedTurns;

    private double timeDelta;
    private static double maxTimeDelta;
    private static double maxDistance;
    private static double rollingBearingDelta;
    private static double rollingDistanceDelta;
    private static int nNetsInTraining;
    private static NNCandidate[] nnCandidate = new NNCandidate[NN_CANDIDATES];
    private static LinkedList nnHistoryList = new LinkedList();
    private static float[] nnHistoryArray = new float[HISTORY_DEPTH * HISTORY_ITEMS + 4];
    private double nnError;

    public void run() {
        if (fieldRectangle == null) {
            initBattle();
        }
        roundOver = false;
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);

        while (true) {
            if (Math.random() < 0.05) {
                velocity = Math.min(8, Math.random() * 24);
            }
            setMaxVelocity(Math.abs(getTurnRemaining()) > 45 ? 0.1 : velocity);
            selectNNetsForTraining();
            if (!haveEnemy) {
                setTurnRadarLeft(22.5);
            }
            haveEnemy = false;
            if (enemyEnergy > 0) {
                Bullet bullet = setFireBullet(bulletPower);
                if (bullet != null) {
                    shots++;
                    addCustomEvent(new CheckUpdateFactors(bullet));
                }
            }
            execute();
        }
    }

    public void onScannedRobot(ScannedRobotEvent e) {
        double radarTurn;
        enemyName = e.getName();
        oldLocation.setLocation(location);
        location.setLocation(getX(), getY());
        oldEnemyLocation.setLocation(enemyLocation);
        absoluteBearing = getHeading() + e.getBearing();
        enemyEnergy = e.getEnergy();
        enemyVelocity = e.getVelocity();
        enemyDistance = e.getDistance();
        toLocation(absoluteBearing, enemyDistance, location, enemyLocation);
        setBulletPower();
        timeDelta = getTime() - time;
        if (timeDelta > 0) {
            if (timeDelta > maxTimeDelta) {
                maxTimeDelta = timeDelta;
            }
            enemyBearingDelta = normalRelativeAngle(absoluteBearing(oldLocation, enemyLocation) -
                absoluteBearing(oldLocation, oldEnemyLocation)) / timeDelta;
            enemyDistanceDelta = (oldLocation.distance(oldEnemyLocation) - oldLocation.distance(enemyLocation)) / timeDelta;
            enemyHeadingDelta = (enemyHeading - e.getHeading()) / timeDelta;
            time = getTime();
            if (enemyBearingDelta <= MAX_BEARING_DELTA) {
                record();
            }
        }
        enemyHeading= e.getHeading();
        haveEnemy = true;
        radarTurn = normalRelativeAngle(getHeading() + e.getBearing() - getRadarHeading()) * 2;
        setTurnRadarRight(radarTurn);
        if (getOthers() > 0 && getGunHeat() / getGunCoolingRate() < 2) {
            aim();
        }
        else {
            setTurnGunRight(normalRelativeAngle(absoluteBearing - getGunHeading()));
        }
        considerRamming();
        move();
    }

    public void onWin(WinEvent e) {
        wins++;
        if (!roundOver) {
            printStats();
        }
        roundOver = true;
    }

    public void onDeath(DeathEvent e) {
        if (!roundOver) {
            printStats();
        }
        roundOver = true;
    }

    public void onSkippedTurn(SkippedTurnEvent e) {
        skippedTurns++;
    }

    private void initBattle() {
        roundNum = getRoundNum();
        fieldRectangle = new Rectangle2D.Double(0, 0 , getBattleFieldWidth(), getBattleFieldHeight());
        setColors(Color.black, Color.black, Color.white);
        maxDistance = (new Point2D.Double(20,20)).distance(
            new Point2D.Double(fieldRectangle.getWidth() - 20, fieldRectangle.getHeight() - 20));
        for (int i = 0; i < NN_CANDIDATES; i++) {
            nnCandidate[i] = new NNCandidate(i);
        }
    }

    private void aim() {
        double guessedDistance = location.distance(enemyLocation);
        Arrays.sort(nnCandidate);
        nnCandidate[0].predict(nnHistoryArray);
        double guessedHeading = absoluteBearing(location, enemyLocation) + nnCandidate[0].getPrediction();
        Point2D impactLocation = new Point2D.Double();
        toLocation(guessedHeading, guessedDistance, location, impactLocation);
        translateInsideField(impactLocation, 1);
        guessedHeading = absoluteBearing(location, impactLocation);
        setTurnGunRight(normalRelativeAngle(guessedHeading - getGunHeading()));
    }

    private void considerRamming() {
        if (enemyEnergy == 0 && getOthers() == 1) {
            if (waitBeforeRam == 0) {
                goTo(enemyLocation);
            }
            else {
                waitBeforeRam--;
            }
        }
        else {
            waitBeforeRam = 100;
        }
    }

    private void move() {
        if (Math.abs(getDistanceRemaining()) < Math.random() * 50) {
            Point2D dLocation = new Point2D.Double();
            double relativeAngle = -36 + 72 * Math.random();
            double distanceExtra = 3;
            double angle = absoluteBearing + 180 + relativeAngle;
            if (isCornered() || enemyDistance > 500) {
                distanceExtra = -1;
            }
            if (enemyEnergy == 0 && getOthers() == 1) {
                distanceExtra = -10;
            }
            distanceExtra *= Math.abs(relativeAngle);
            toLocation(angle, enemyDistance + distanceExtra, enemyLocation, dLocation);
            if (!fieldRectangle.contains(dLocation)) {
                angle = absoluteBearing + 180 - relativeAngle;
                toLocation(angle, enemyDistance + distanceExtra, enemyLocation, dLocation);
            }
            translateInsideField(dLocation, 35);
            goTo(dLocation);
        }
    }

    private boolean isCornered() {
        double m = 100;
        double mnX = m;
        double mnY = m;
        double mxX = fieldRectangle.getWidth() - m;
        double mxY = fieldRectangle.getHeight() - m;
        double x = location.getX();
        double y = location.getY();
        return ((x < mnX && (y < mnY || y > mxY)) || (x > mxX && (y < mnY || y > mxY)));
    }

    private void setBulletPower() {
        double power = 3;
        power = Math.min(4 * 7 * nnCandidate[0].getHitRate() / MAX_HIT_RATE, power);
        power = Math.min(enemyEnergy / 4, power);
        power = Math.min(getEnergy() / 3, power);
        bulletPower = power;
    }

    private void goTo(Point2D point) {
        double distance = location.distance(point);
        double angle = normalRelativeAngle(absoluteBearing(location, point) - getHeading());
        if (Math.abs(angle) > 90) {
            distance *= -1;
            if (angle > 0) {
                angle -= 180;
            }
            else {
                angle += 180;
            }
        }
        setTurnRight(angle);
        setAhead(distance);
    }

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

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

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

    private static double normalRelativeAngle(double angle) {
        double relativeAngle = angle % 360;
        if (relativeAngle <= -180 )
            return 180 + (relativeAngle % 180);
        else if ( relativeAngle > 180 )
            return -180 + (relativeAngle % 180);
        else
            return relativeAngle;
    }

    //Paul Evans' excellent function for keeping rolling averages
    private static double rollingAvg(double value, double newEntry, double n, double weighting ) {
        return (value * n + newEntry * weighting) / (n + weighting);
    } 

    private static float nnNormalizedValue(double value, double max) {
        return (float)((value + max) / (2 * max));
    }

    private void record() {
        rollingBearingDelta = rollingAvg(rollingBearingDelta, enemyBearingDelta, 15, 1);
        rollingDistanceDelta = rollingAvg(rollingDistanceDelta, enemyDistanceDelta, 15, 1);

        nnHistoryList.addLast(new Double(nnNormalizedValue(enemyBearingDelta, MAX_BEARING_DELTA)));
        nnHistoryList.addLast(new Double(nnNormalizedValue(enemyDistanceDelta, MAX_DISTANCE_DELTA)));
        if (nnHistoryList.size() > HISTORY_DEPTH * HISTORY_ITEMS) {
            for (int i = 0; i < HISTORY_ITEMS; i++) {
                nnHistoryList.removeFirst();
            }
            int i;
            for (i = 0; i < HISTORY_DEPTH * HISTORY_ITEMS; i++) {
                nnHistoryArray[i] = ((Double)(nnHistoryList.get(i))).floatValue();
            }
            nnHistoryArray[i++] = (float)(enemyDistance / maxDistance);
            nnHistoryArray[i++] = (float)(bulletPower / 3);
            nnHistoryArray[i++] = nnNormalizedValue(rollingBearingDelta, MAX_BEARING_DELTA);
            nnHistoryArray[i++] = nnNormalizedValue(rollingDistanceDelta, MAX_DISTANCE_DELTA);
        }
    }

    private void selectNNetsForTraining() {
        int numSelected = 0;
        int numInTraining = 0;
        for (int i = 0; i < NN_CANDIDATES; i++) {
            if (nnCandidate[i].isInTraining()) {
                numInTraining++;
                if (nnCandidate[i].getTrainingsThisSession() > TRAINING_SESSION_LENGTH) {
                    double trainingResults = nnCandidate[i].validateTraining();
                    if (trainingResults > 0) {
                        nnCandidate[i].setInTraining(true);
                        nnCandidate[i].initiateTraining();
                    }
                    else {
                        nnCandidate[i].setInTraining(false);
                    }
                }
                if (nnCandidate[i].getTrainings() > MAX_TRAININGS) {
                    nnCandidate[i].setInTraining(false);
                }
            }
        }
        if (numInTraining < MIN_NETS_IN_TRAINING) {
            int net = (int)Math.floor((Math.random() * NN_CANDIDATES));
            if (!(nnCandidate[net].getTrainings() > MAX_TRAININGS)) {
                nnCandidate[net].setInTraining(true);
                nnCandidate[net].initiateTraining();
            }
        }
    }

    private void printStats() {
        System.out.println("Wins: " + wins);
        for (int i = 0; i < NN_CANDIDATES; i++) {
            System.out.println("Net #: " + nnCandidate[i].getId() +
                ", trainings: " + nnCandidate[i].getTrainings() +
                ", hit rate: " + nnCandidate[i].getHitRate() +
                ", error: " + nnCandidate[i].getError());
        }
    }

    class CheckUpdateFactors extends Condition {
        private long time;
        private double bulletVelocity;
        private double bulletPower;
        private double bearingDelta;
        private Point2D oldRLocation = new Point2D.Double();
        private Point2D oldELocation = new Point2D.Double();
        private double oldBearing;
	private float[] history = new float[HISTORY_DEPTH * HISTORY_ITEMS + 4];
        private double[] prediction = new double[NN_CANDIDATES];

        public CheckUpdateFactors(Bullet bullet) {
            this.time = getTime();
            this.bulletVelocity = bullet.getVelocity();
            this.bulletPower = bullet.getPower();
            this.bearingDelta = enemyBearingDelta;
            this.oldRLocation.setLocation(location);
            this.oldELocation.setLocation(enemyLocation);
            this.oldBearing = absoluteBearing(oldRLocation, oldELocation);
            System.arraycopy(nnHistoryArray, 0, history, 0, nnHistoryArray.length);
            for (int i = 0; i < NN_CANDIDATES; i++) {
                this.prediction[i] = nnCandidate[i].getPrediction();
            }
        }

        public boolean test() {
            if (getOthers() == 0) {
                removeCustomEvent(this);
                return false;
            }
            double bulletDistance = bulletVelocity * (getTime() - time);
            if (bulletDistance > oldRLocation.distance(enemyLocation) - 10) {
                double impactBearing = absoluteBearing(oldRLocation, enemyLocation);
                double bearingDiff = normalRelativeAngle(impactBearing - oldBearing);
                for (int i = 0; i < NN_CANDIDATES; i++) {
                    if (Math.abs(prediction[i] - bearingDiff) < Math.toDegrees(Math.atan(20 / bulletDistance))) {
                        nnCandidate[i].updateHitRate(MAX_HIT_RATE, bulletPower);
                    }
                    else {
                        nnCandidate[i].updateHitRate(0, bulletPower);
                    }
                }
                if (nnHistoryList.size() == HISTORY_DEPTH * HISTORY_ITEMS) { 
                    float nnCorrectAnswer[] = { nnNormalizedValue(bearingDiff, MAX_BEARING_DIFF) };
                    for (int i = 0; i < NN_CANDIDATES; i++) {
                        if (nnCandidate[i].isInTraining()) {
                            nnError = nnCandidate[i].train(history, nnCorrectAnswer);
                        }
                    }
                }
                removeCustomEvent(this);
            }
            return false;
        }
    }

    class NNCandidate implements Comparable {
        private int id;
        private wiki.nn.nrlibj.NNet nnet;
        private wiki.nn.nrlibj.NNet nnetBackup;;
        private double nnError;
        private double prediction;
        private long trainings;
        private long trainingsBackup;
        private int trainingsThisSession;
        private double hitRate;
        private double hitRateBackup;
        private double bestHitRate;
        private boolean inTraining;

        public NNCandidate(int id) {
            this.id = id;
            wiki.nn.nrlibj.NrPop.setSeed();
            nnet = new wiki.nn.nrlibj.NNet(NNdescr);
        }

        public NNCandidate(int id, wiki.nn.nrlibj.NNet nnet) {
            this.id = id;
            this.nnet = nnet;
        }

        public int compareTo(Object o) {
            NNCandidate nnC = (NNCandidate) o;
            if (this.getHitRate() > nnC.getHitRate()) {
                return -1;
            }
            if (this.getHitRate() < nnC.getHitRate()) {
                return +1;
            }
            return 0;
        }

        public boolean equals(Object object) {
            if (object instanceof NNCandidate) {
                return (((NNCandidate)object).getHitRate() == this.getHitRate());
            }
            return false;
        }

        wiki.nn.nrlibj.NNet getNNet() {
            return this.nnet;
        }

        double getError() {
            return nnError;
        }

        double train(float[] input, float[] answer) {
            nnError = nnet.ebplearnNNet(input, answer);
            trainings++;
            trainingsThisSession++;
            return nnError;
        }

        long getTrainings() {
            return this.trainings;
        }

        void predict(float[] input) {
            float[] nnAnswer = new float[1];
            nnet.frwNNet(input, nnAnswer);
            prediction = (0 - MAX_BEARING_DIFF) + MAX_BEARING_DIFF * 2 * nnAnswer[0];
        }

        double getPrediction() {
            return this.prediction;
        }

        void updateHitRate(double hitValue, double weight) {
            this.hitRate = rollingAvg(this.hitRate, hitValue, Math.min(shots, NN_CANDIDATE_RATE_DEPTH), weight);
        }

        double getHitRate() {
            return this.hitRate;
        }
        
        boolean isInTraining() {
            return this.inTraining;
        }

        void setInTraining(boolean inTraining) {
            this.inTraining = inTraining;
        }

        void initiateTraining() {
            nnetBackup = nnet.cloneNNet();
            hitRateBackup = hitRate;
            trainingsBackup = trainings;
            trainingsThisSession = 0;
        }

        int getTrainingsThisSession() {
            return this.trainingsThisSession;
        }

        double validateTraining() {
            double hitRateDelta = hitRate - hitRateBackup;
            if (hitRate > bestHitRate) {
                bestHitRate = hitRate;
            }
            if (hitRateDelta < ALLOWED_HIT_RATE_DEGRADATION || bestHitRate < MIN_ACCEPTED_HITRATE) {
                nnet = nnetBackup;
                hitRate = hitRateBackup;
                trainings = trainingsBackup;
            }
            return hitRateDelta;
        }

        int getId() {
            return this.id;
        }
    }
}

Robo Home | OrcaM | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited April 21, 2003 21:11 EST by PEZ (diff)
Search: