[Home]VirtualBullets/VirtualBulletsSampleBot

Robo Home | VirtualBullets | Changes | Preferences | AllPages

In addition to illustrating virtual bullets, this sample bot showcases some good techniques for writing a robot.

Note that the robot defaults to "Really Bad Gun", which fires away from the enemy, but will soon learn to use a better one. =)

--David Alves

package davidalves.sample;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.Vector;

import robocode.AdvancedRobot;
import robocode.Bullet;
import robocode.DeathEvent;
import robocode.ScannedRobotEvent;
import robocode.WinEvent;

public class VirtualGunsSampleBot extends AdvancedRobot {
	static final double BULLET_POWER = 3.0;
	
	RobotState me, target; // these aren't static, so they will be reset every round
	
	static Vector guns; // this Vector (and all its contents) will persist from round to round
	
	static Vector virtualBullets = new Vector();
	
	Rectangle2D.Double battlefield;
	
	public void run(){
		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);
		setAdjustRadarForRobotTurn(true);
		
		battlefield = new Rectangle2D.Double(0,0,getBattleFieldWidth(), getBattleFieldHeight());
		
		if(guns == null){
			guns = new Vector();
			
			Gun defaultGun = new ReallyBadGun();
			defaultGun.hits = 1;
			guns.add(defaultGun);
			guns.add(new HeadOnGun());
			guns.add(new RandomGun());
		}
		
		virtualBullets.clear();
		
		while(true){			
			//Update our position, heading, and velocity
			me = new RobotState();
			me.name = getName();
			me.velocity = getVelocity();
			me.heading = getHeadingRadians();
			me.x = getX();
			me.y = getY();
			
			//Turn our radar
			if(target == null)
				setTurnRadarRightRadians(Math.toRadians(45));
			else {
				double radarTurnAngle = normalizeRelativeAngle(me.absoluteAngleTo(target) - getRadarHeadingRadians());
				if(radarTurnAngle > 0)
					radarTurnAngle += .4;
				else
					radarTurnAngle -= .4;
				setTurnRadarRightRadians(radarTurnAngle);
			}
			
			//Move each virtual bullet forward one tick. After moving, check if they are off the field or near the target.
			Iterator i = virtualBullets.iterator();
			while(i.hasNext()){
				VirtualBullet virtualBullet = (VirtualBullet) i.next();
				virtualBullet.setLocation(virtualBullet.project(virtualBullet.heading, virtualBullet.velocity));
				
				if(virtualBullet.distance(target) < 25){ //check if the virtual bullet "hit" the enemy
					
					virtualBullet.gunUsed.hits += 1;
					i.remove();
				} else if(!battlefield.contains(virtualBullet)){ //check if the virtual bullet is off the field
					i.remove();
				}
			}
			
			if(target != null) gun();
			
			execute();
		}
	}
	
	public void gun(){
		
		//Select the best gun
		long bestScore = -1;
		Gun bestGun = null;
		Iterator i = guns.iterator();
		while(i.hasNext()){
			Gun gun = (Gun) i.next();
			if(gun.hits > bestScore){
				bestScore = gun.hits;
				bestGun = gun;
			}
		}
		
		
		//Aim with the best gun
		setTurnGunRightRadians(normalizeRelativeAngle(bestGun.getFiringAngle(me, target, BULLET_POWER) - getGunHeadingRadians()));
		
		
		// create new virtual bullets only if we actually fired this turn
		Bullet b = setFireBullet(BULLET_POWER);
		if(b != null){
			i = guns.iterator();
			while(i.hasNext()){
				Gun gun = (Gun) i.next();
				VirtualBullet newVirtualBullet = new VirtualBullet();
				newVirtualBullet.setLocation(me);
				newVirtualBullet.heading = gun.getFiringAngle(me, target, BULLET_POWER);
				newVirtualBullet.velocity = 20.0 - 3.0 * BULLET_POWER;
				newVirtualBullet.gunUsed = gun;
				virtualBullets.add(newVirtualBullet);
			}
		}
	}
	
	public void onScannedRobot(ScannedRobotEvent e){
		// Create a new RobotState object to represent the enemy's current state
		target = new RobotState();
		target.setLocation(me.project(e.getBearingRadians() + getHeadingRadians(), e.getDistance()));
		target.heading = e.getHeadingRadians();
		target.velocity = e.getVelocity();
		target.name = e.getName();
	}
	
	public void onDeath(DeathEvent e){
		endRound();
	}
	
	public void onWin(WinEvent e){
		endRound();
	}
	
	public void endRound(){
		Iterator i = guns.iterator();
		out.println();
		out.println("Virtual bullet hit table");
		out.println("------------------------");
		while(i.hasNext()){
			Gun gun = (Gun) i.next();
			out.println(gun.getName() + ": " + gun.hits);
		}
		out.println();
		
		//So we don't keep firing
		target = null;
	}
	
	//Draw the virtual bullets on screen - this will only work with a recent version of robocode and "Paint" turned on in the robot's console
	public void onPaint(Graphics2D g){
		
		Iterator i = virtualBullets.iterator();
		while(i.hasNext()){
			VirtualBullet virtualBullet = (VirtualBullet) i.next();
			g.setColor(virtualBullet.gunUsed.getColor());
			g.fillOval((int)virtualBullet.x - 3, (int)virtualBullet.y -3, 6, 6);
		}
		
		int numberOfGuns = guns.size();
		for(int j = 0; j < numberOfGuns; j++){
			Gun gun = (Gun) guns.elementAt(j);
			g.setColor(Color.WHITE);
			g.drawString(gun.getName() + ": " + gun.hits, 20, 5 + j*15);
			g.setColor(gun.getColor());
			g.fillOval(5, 5 + j * 15, 10, 10);
		}
	}
	
	//This method ensures that angles are between -180 and 180 degrees, for turning radar / gun 
	public static double normalizeRelativeAngle(double angle){
		while(angle <= -Math.PI){
			angle += 2.0 * Math.PI;
		}
		while(angle > Math.PI){
			angle -= 2.0 * Math.PI;
		}
		return angle;
	}
}

class ReallyBadGun extends Gun{
	public String getName(){return "Really bad gun - fires away from the enemy";}
	public Color getColor(){return Color.RED;}
	
	public double getFiringAngle(RobotState shooter, RobotState target, double bulletPower) {
		return target.absoluteAngleTo(shooter);
	}
}
class RandomGun extends Gun {
	public String getName(){return "Simple linear gun";}
	public Color getColor(){return Color.BLUE;}
	
	public double getFiringAngle(RobotState shooter, RobotState target, double bulletPower) {
		//double maxAngle = Math.asin(8.0 / (20.0 - 3.0 * bulletPower));
		double directAngle = shooter.absoluteAngleTo(target);
		return directAngle - target.velocity * Math.sin(directAngle - target.heading) / (20.0 - 3.0 * bulletPower);
	}
}

class HeadOnGun extends Gun {
	public String getName(){return "Head on";}
	public Color getColor(){return Color.GREEN;}
	
	public double getFiringAngle(RobotState shooter, RobotState target, double bulletPower) {
		return shooter.absoluteAngleTo(target);
	}
}

abstract class Gun {
	public long hits;
	public abstract Color getColor();
	public abstract String getName();
	public abstract double getFiringAngle(RobotState shooter, RobotState target, double bulletPower);
}

class FieldPoint extends Point2D.Double{
	public FieldPoint(){}
	public FieldPoint(double x, double y){
		super(x,y);
	}
	
	public double absoluteAngleTo(FieldPoint p){
		double angle = Math.atan2(p.x - x, p.y - y);
		while(angle < 0.0){
		 	angle += 2.0 * Math.PI;
		}
		while(angle >= 2.0 * Math.PI){
		 	angle -= 2.0 * Math.PI;
		}
		return angle;
	}
	
	public FieldPoint project(double angle, double distance) {
		return new FieldPoint(x + Math.sin(angle) * distance, y + Math.cos(angle) * distance);
	}
} 

class MotionState extends FieldPoint{
	public double velocity, heading;
}

class VirtualBullet extends MotionState{
	public Gun gunUsed;
}

class RobotState extends MotionState{
	public String name;
}

Robo Home | VirtualBullets | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited September 20, 2006 19:37 EST by David Alves (diff)
Search: