BrokenSword/Code

From Robowiki
< BrokenSword
Revision as of 21:30, 22 April 2009 by Voidious (talk | contribs) (migrating this page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The source code for BrokenSword 1.0. Code size is 1443.


package voidious.mini;

import robocode.*;
import robocode.util.Utils;

import java.awt.Color;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;

public class BrokenSword extends AdvancedRobot {
    static final int GF_ZERO = 23;
    static final int GF_ONE = 46;

    protected static java.awt.geom.Rectangle2D.Double _fieldRect;
    protected static Point2D.Double _myLocation;
    protected static HashMap _enemyLocationsByName = new HashMap();
    protected static double _randomDirChangeAmount;
    protected long _timeSinceDirChangeCounter;
    protected static double _lastHeading;
    protected ArrayList _recentLocations;

    protected double _targetRating = Double.POSITIVE_INFINITY;
    protected static String _targetName = "";

    protected static final double WAVE_BULLET_POWER = 1.99;
    protected static final double WAVE_BULLET_VELOCITY = 14.03;
    protected static final double WAVE_MAX_ESCAPE_ANGLE = 0.62;
    protected static double _bulletPower;

    private static int _radarDirection = 1;
    
	public void run() {
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);
		setColors(new Color(141, 220, 175), Color.white, Color.white);
        _recentLocations = new ArrayList();

    	_fieldRect = new java.awt.geom.Rectangle2D.Double(30, 30, 
                getBattleFieldWidth() - 60, getBattleFieldHeight() - 60);

		do {
          	setFireBullet(_bulletPower);
          	setTurnRadarRight(45 * _radarDirection);
			move();
			execute();
		} while (true);
	}

    public void onScannedRobot(ScannedRobotEvent e) {
        String eName = e.getName();
        double eDistance = e.getDistance();
        double eEnergy = e.getEnergy();
        double eVelocity = e.getVelocity();

        
        ////////////////////////////////////////////////
        // General stuff
        ////////////////////////////////////////////////
        
        double enemyAbsoluteBearing = 
        	e.getBearingRadians() + getHeadingRadians();

        if (!_enemyLocationsByName.containsKey(eName)) {
        	_enemyLocationsByName.put(eName, new EnemyData());
        }
        EnemyData eData = (EnemyData)_enemyLocationsByName.get(eName);
        eData.alive = true;
        eData.location = project(_myLocation, enemyAbsoluteBearing, eDistance);
        double absSinRelativeHeading = Math.abs(Math.sin(e.getHeadingRadians() 
        		- enemyAbsoluteBearing));
        eData.energy = eEnergy;
        
        if (getOthers() == 1 || 
             (_targetName.equals(eName) && 
               (eDistance < 300 || (getGunHeat() < .8 && getOthers() <= 5)))) { 
        
        	_radarDirection *= -1; 
        }
        
        
        ////////////////////////////////////////////////
        // Gun wave creation
        ////////////////////////////////////////////////

        MicroWave w;
        addCustomEvent(w = new MicroWave());
        w.targetName = eName;
        w.sourceLocation = _myLocation;
        w.directAngle = enemyAbsoluteBearing;
        w.orientation = sign((eVelocity)*Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));

        w.waveGuessFactors = eData.gunStats[(int)Math.abs(eVelocity) / 3][(int)(eDistance / 300)][(int)Math.min(2, absSinRelativeHeading * 3)];

        
        ////////////////////////////////////////////////
        // Target selection and actual firing.
        ////////////////////////////////////////////////

        double thisRating = (eEnergy * square(eDistance)); 
        if (thisRating < (_targetRating * .8) || eName.equals(_targetName)) {
            _targetRating = thisRating;
            _targetName = eName;
        }

        if (_targetName.equals(eName)) {
      
            _bulletPower = 1.99;
            
            if (eDistance < 250 || getOthers() >= 6) {
            	_bulletPower = 3;
            }
            
            if (eDistance > 600 && (getOthers() <= 2)) {
            	_bulletPower = 1.4;
            }
            
            if (getEnergy() < 30 && eEnergy > getEnergy()) {
            	_bulletPower = Math.min(_bulletPower, 2 - ((33 - getEnergy()) / 15));
            }
            
            int bestGF = GF_ZERO;
            double bestGFRank = 0;
            for (int x = 0; x < GF_ONE; x++) {
            	if (w.waveGuessFactors[x] > bestGFRank) {
            		bestGFRank = w.waveGuessFactors[x];
            		bestGF = x;
            	}
            }
                    	
            setTurnGunRightRadians(Utils.normalRelativeAngle(
            		enemyAbsoluteBearing - getGunHeadingRadians() +
            		(e.getEnergy() > 0 ?
            			(w.orientation 
            			* (maxEscapeAngle(bulletVelocity(_bulletPower)) / GF_ZERO)
            			* (bestGF - GF_ZERO)) : 0)));        
        }
    }
    
    public void onRobotDeath(RobotDeathEvent e) {
    	String eName = e.getName();
    	        
        if (_targetName.equals(eName)) {
            _targetName = "";
            _targetRating = Double.POSITIVE_INFINITY;
        }
        
        ((EnemyData)_enemyLocationsByName.get(eName)).alive = false;
    }
   
    protected void move() {
        _myLocation = new Point2D.Double(getX(), getY());
        
        if (Math.random() > 0.9) {
        	_recentLocations.add(0, _myLocation);
        }
        
        double bestRisk = Double.POSITIVE_INFINITY;
        double bestAngle = 0;
        double currentHeading =
        		getHeadingRadians() + (getVelocity() < 0?Math.PI:0);
        
        Object[] enemies = 
            (_enemyLocationsByName.values().toArray());
        
       	_timeSinceDirChangeCounter++;
        if (Math.abs(Utils.normalRelativeAngle(currentHeading - _lastHeading)) 
           	> Math.PI / 4) {
        	_timeSinceDirChangeCounter = 0;
        	_randomDirChangeAmount = (Math.random() * Math.random() * 50);
        }
        
        for (double x = 0; x < 2; x += .04) {
            double testAngle = x * Math.PI;
            Point2D.Double testPoint = project(_myLocation, testAngle, 
                50 + Math.random() * 200);
            
            if (_fieldRect.contains(testPoint)) {
                double testRisk = 0;
                
                for (int y = 0; y < enemies.length; y++) {
                    EnemyData eData = ((EnemyData)enemies[y]);
                    if (eData.alive) {
                        double distSquaredToEnemy = 
                        	testPoint.distanceSq(eData.location);
                        
                        testRisk += 
                            ((limit(0.5, (eData.energy / getEnergy()), 2)
                            	/ distSquaredToEnemy))
                            * (1 + square(Math.cos(absoluteBearing(
                                _myLocation, eData.location) - testAngle)))
                            ;
                    }
                }
                                
            	if (Math.abs(Utils.normalRelativeAngle(currentHeading 
                    	- testAngle)) < (Math.PI / 4) && getOthers() <= 6) {
            		testRisk /= 2;
            		if (_timeSinceDirChangeCounter > _randomDirChangeAmount) {
                		testRisk *= 20;
                	}
                }
            	
                try {
             		for (int z = 0; z < 4 && z < _recentLocations.size(); z++) {
              			testRisk *= (1 + (((400) - (50 * z)) / (testPoint.distanceSq((Point2D.Double)_recentLocations.get(z)))));
             		}
                } catch (Exception e) { }
                                
                if (testRisk < bestRisk) {
                    bestRisk = testRisk;
                    bestAngle = x * Math.PI;
                }        
            }           
        }
               
        _lastHeading = currentHeading;

        moveWithBackAsFront(bestAngle);
    }
	
    private static double absoluteBearing(Point2D.Double source, Point2D.Double target) {
        return Math.atan2(target.x - source.x, target.y - source.y);
    }
    
    // CREDIT: code by Iiley,
    // http://robowiki.net?BackAsFront
    void moveWithBackAsFront(double bearing) {
        double angle = Utils.normalRelativeAngle(bearing - getHeadingRadians());
        double turnAngle;
        setTurnRightRadians(turnAngle = Math.atan(Math.tan(angle)));
        setAhead((angle == turnAngle) ? 100 : -100);
    }
     
    protected 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 maxEscapeAngle(double velocity) {
        return Math.asin(8.0/velocity);
    }

    protected static double bulletVelocity(double power) {
        return (20.0 - (3.0*power));
    }
    
    static int sign(double d) {
        if (d >= 0) return 1;
        return-1;
    }
    
    static double square(double d) {
        return d * d;
    }
    
    protected static double limit(double min, double value, double max) {
        return Math.max(min, Math.min(value, max));
    }

    class MicroWave extends Condition {
        String targetName;
        Point2D.Double sourceLocation;
        long[] waveGuessFactors;
        double bulletVelocity, directAngle, distance;
        int orientation;

        public boolean test(){
        	try {
                Point2D.Double enemyLocation = 
                    ((EnemyData)_enemyLocationsByName.get(targetName)).location;
                if ((enemyLocation).distance(sourceLocation)
                    <= (distance+=WAVE_BULLET_VELOCITY) + (WAVE_BULLET_VELOCITY/2)) {
                	try {
                		double guessFactor = (Utils.normalRelativeAngle(
                				absoluteBearing(sourceLocation, enemyLocation)
                				- directAngle) * orientation) / WAVE_MAX_ESCAPE_ANGLE;
                		int guessFactorIndex = (int)((guessFactor * GF_ZERO) + GF_ZERO);
                		waveGuessFactors[guessFactorIndex]++;
/*
                		for (int x = GF_ONE - 1; x >= 0; x--) {
                			waveGuessFactors[x] +=
                				(1D / (square(x - guessFactorIndex) + 1));
//                			waveGuessFactors[x] = ((waveGuessFactors[x] * 200) +
//                				(1D / (square(x - guessFactorIndex) + 1)))
//                				/ 201;
                		}
*/
                	} catch (Exception e) { }
                    removeCustomEvent(this);
                }
        	} catch (Exception e) { }
            return false;
        }
    }

    class EnemyData {
        protected long[][][][] gunStats = new long[3][5][3][GF_ONE+1];

        boolean alive;
        Point2D.Double location;
        double energy;
    }
}