Difference between revisions of "PluggableRobot/Source"

From Robowiki
Jump to navigation Jump to search
(Version 2.0 alpha)
Line 1,100: Line 1,100:
 
     */
 
     */
 
     public static double degToRad(double degrees) {
 
     public static double degToRad(double degrees) {
     return degrees * Math.PI / 180;
+
     return Math.toRadians(degrees);
 
     }
 
     }
  
Line 1,107: Line 1,107:
 
     */
 
     */
 
     public static double radToDeg(double radians) {
 
     public static double radToDeg(double radians) {
     return radians * 180 / Math.PI;
+
     return Math.toDegrees(radians);
 
     }
 
     }
 
}
 
}

Revision as of 10:23, 28 October 2009

Below is the source for PluggableRobot. Pillage to your heart's content. PluggableRobot is released under the RoboWiki Limited Public Code License.

Canvas.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import java.awt.*;
import java.awt.geom.Point2D;

import robocode.AdvancedRobot;

/**
 * A convenience wrapper around Graphics2D which provides the following
 * features:
 * 
 * - Accepts doubles for many arguments which are ints in Graphics2D
 * - getCenter(), getHeight() and getWidth()
 * - drawCircle(), fillCircle()
 * 
 * @author Robert J. Walker
 */
public class Canvas {
	private Graphics2D _g;
	private double _width;
	private double _height;
	private Point2D.Double _center;

	Canvas(AdvancedRobot bot) {
		_g = bot.getGraphics();
		_width = bot.getBattleFieldWidth();
		_height = bot.getBattleFieldHeight();
		_center = new Point2D.Double(_width / 2, _height / 2);
	}

	/**
	 * @see java awt.Graphics2D.clearRect(int, int, int, int)
	 */
	public void clearRect(double x, double y, double width, double height) {
		_g.clearRect(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.clip(java.awt.Shape)
	 */
	public void clip(Shape shape) {
		_g.clip(shape);
	}

	/**
	 * @see java awt.Graphics2D.clipRect(int, int, int, int)
	 */
	public void clipRect(double x, double y, double width, double height) {
		_g.clipRect(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.copyArea(int, int, int, int, int, int)
	 */
	public void copyArea(double x, double y, double width, double height,
			double dx, double dy) {
		_g.copyArea(i(x), i(y), i(width), i(height), i(dx), i(dy));
	}

	/**
	 * @see java awt.Graphics2D.draw(java.awt.Shape)
	 */
	public void draw(Shape shape) {
		_g.draw(shape);
	}

	/**
	 * @see java awt.Graphics2D.drawArc(int, int, int, int, int, int)
	 */
	public void drawArc(double x, double y, double width, double height,
			double startAngle, double arcAngle) {
		_g.drawArc(i(x), i(y), i(width), i(height), i(startAngle), i(arcAngle));
	}

	/**
	 * Convenience method for drawing a circle, given the center coordinates and
	 * radius.
	 */
	public void drawCircle(double x, double y, double r) {
		int d = i(r * 2);
		_g.drawOval(i(x - r), i(y - r), d, d);
	}

	/**
	 * @see java awt.Graphics2D.drawLine(int, int, int, int)
	 */
	public void drawLine(double x1, double y1, double x2, double y2) {
		_g.drawLine(i(x1), i(y1), i(x2), i(y2));
	}

	/**
	 * @see java awt.Graphics2D.drawOval(int, int, int, int)
	 */
	public void drawOval(double x, double y, double width, double height) {
		_g.drawOval(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.drawRect(int, int, int, int)
	 */
	public void drawRect(double x, double y, double width, double height) { 
		_g.drawRect(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.drawRoundRect(int, int, int, int, int, int)
	 */
	public void drawRoundRect(double x, double y, double width, double height,
			double arcWidth, double arcHeight) { 
		_g.drawRoundRect(i(x), i(y), i(width), i(height), i(arcWidth),
				i(arcHeight));
	}

	/**
	 * @see java awt.Graphics2D.drawString(String, float, float)
	 */
	public void drawString(String str, double x, double y) {
		_g.drawString(str, (float) x, (float) y);
	}

	/**
	 * @see java awt.Graphics2D.fill(java.awt.Shape)
	 */
	public void fill(Shape shape) {
		_g.fill(shape);
	}

	/**
	 * @see java awt.Graphics2D.fillArc(int, int, int, int, int, int)
	 */
	public void fillArc(double x, double y, double width, double height,
			double startAngle, double arcAngle) {
		_g.fillArc(i(x), i(y), i(width), i(height), i(startAngle), i(arcAngle));
	}

	/**
	 * Convenience method for filling a circle, given the center coordinates and
	 * radius.
	 */
	public void fillCircle(double x, double y, double r) {
		int d = i(r * 2);
		_g.fillOval(i(x - r), i(y - r), d, d);
	}

	/**
	 * @see java awt.Graphics2D.fillOval(int, int, int, int)
	 */
	public void fillOval(double x, double y, double width, double height) {
		_g.fillOval(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.fillRect(int, int, int, int)
	 */
	public void fillRect(double x, double y, double width, double height) { 
		_g.fillRect(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.fillRoundRect(int, int, int, int, int, int)
	 */
	public void fillRoundRect(double x, double y, double width, double height,
			double arcWidth, double arcHeight) { 
		_g.fillRoundRect(i(x), i(y), i(width), i(height), i(arcWidth),
				i(arcHeight));
	}

	/**
	 * @see java awt.Graphics2D.getBackground()
	 */
	public Color getBackground() {
		return _g.getBackground();
	}

	/**
	 * Returns a Point2D located at the center of the canvas.
	 */
	public Point2D.Double getCenter() {
		return _center;
	}

	/**
	 * @see java awt.Graphics2D.getClip()
	 */
	public Shape getClip() {
		return _g.getClip();
	}

	/**
	 * @see java awt.Graphics2D.getClipBounds()
	 */
	public Rectangle getClipBounds() {
		return _g.getClipBounds();
	}

	/**
	 * @see java awt.Graphics2D.getClipBounds(java.awt.Rectangle)
	 */
	public Rectangle getClipBounds(Rectangle r) {
		return _g.getClipBounds(r);
	}

	/**
	 * @see java awt.Graphics2D.getColor()
	 */
	public Color getColor() {
		return _g.getColor();
	}

	/**
	 * @see java awt.Graphics2D.getDeviceConfiguration()
	 */
	public GraphicsConfiguration getDeviceConfiguration() {
		return _g.getDeviceConfiguration();
	}

	/**
	 * Returns a reference to the actual Graphics2D object wrapped by the
	 * Canvas.
	 */
	public Graphics2D getGraphics() {
		return _g;
	}

	/**
	 * Returns the canvas's height.
	 */
	public double getHeight() {
		return _height;
	}

	/**
	 * @see java awt.Graphics2D.getPaint()
	 */
	public Paint getPaint() {
		return _g.getPaint();
	}

	/**
	 * @see java awt.Graphics2D.getStroke()
	 */
	public Stroke getStroke() {
		return _g.getStroke();
	}

	/**
	 * Returns the canvas's width.
	 */
	public double getWidth() {
		return _width;
	}

	/**
	 * @see java awt.Graphics2D.hit(java.awt.Rectangle, java.awt.Shape, boolean)
	 */
	public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
		return _g.hit(rect, s, onStroke);
	}

	/**
	 * @see java awt.Graphics2D.hitClip(int, int, int, int)
	 */
	public boolean hitClip(double x, double y, double width, double height) {
		return _g.hitClip(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.setBackground(java.awt.Color)
	 */
	public void setBackground(Color color) {
		_g.setBackground(color);
	}

	/**
	 * @see java awt.Graphics2D.setClip(int, int, int, int)
	 */
	public void setClip(double x, double y, double width, double height) {
		_g.setClip(i(x), i(y), i(width), i(height));
	}

	/**
	 * @see java awt.Graphics2D.setClip(java.awt.Shape)
	 */
	public void setClip(Shape clip) {
		_g.setClip(clip);
	}

	/**
	 * @see java awt.Graphics2D.setColor(java.awt.Color)
	 */
	public void setColor(Color c) {
		_g.setColor(c);
	}

	/**
	 * @see java awt.Graphics2D.setPaint(java.awt.Paint)
	 */
	public void setPaint(Paint paint) {
		_g.setPaint(paint);
	}

	/**
	 * @see java awt.Graphics2D.setStroke(java.awt.Stroke)
	 */
	public void setStroke(Stroke stroke) {
		_g.setStroke(stroke);
	}

	/**
	 * Rounds a double to the nearest integer and returns it as an int.
	 */
	private static int i(double d) {
		return (int) Math.round(d);
	}
}

Component.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

/**
 * Components encapsulate the main combat behavior of the robot into pluggable
 * modules. Any code that actually sets an action for the robot (moving,
 * turning, rotating the turret or radar, etc.) should be done in the go()
 * method. Components should NEVER call any blocking methods; PluggableRobot
 * will call execute() automatically when all components have had a chance to
 * act.
 * @author Robert J. Walker
 */
public interface Component {
	/**
	 * Asks this Component to execute any actions that it wants to take this
	 * turn. PluggableRobot will call this method once per turn for each
	 * registered Component. Don't put any blocking calls in here;
	 * PluggableRobot will call execute() for you automatically after calling
	 * go() on each Component.
	 */
	public abstract void go();
}

EventListener.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import robocode.*;

/**
 * Event listener interfaces. Objects that wish to be notified of events must
 * extend one or more of these subinterfaces and register themselves with
 * PluggableRobot at the start of the round via the registerListener() method.
 * @author Robert J. Walker
 */
public interface EventListener {
	public interface BattleEnded extends EventListener {
		/**
		 * Called by the ListenerDelegate when the battle ends.
		 */
		public void notifyBattleEnded(BattleEndedEvent event);
	}

	public interface BulletHitBullet extends EventListener {
		/**
		 * Called by the ListenerDelegate when a bullet fired by your robot has
		 * hit another bullet.
		 */
		public void notifyBulletHitBullet(BulletHitBulletEvent event);
	}

	public interface BulletHit extends EventListener {
		/**
		 * Called by the ListenerDelegate when a bullet fired by your robot has
		 * hit another robot.
		 */
		public void notifyBulletHit(BulletHitEvent event);
	}

	public interface BulletMissed extends EventListener {
		/**
		 * Called by the ListenerDelegate when a bullet fired by your robot has
		 * hit a wall.
		 */
		public void notifyBulletMissed(BulletMissedEvent event);
	}

	public interface Death extends EventListener {
		/**
		 * Called by the ListenerDelegate when your robot has been destroyed.
		 */
		public void notifyDeath(DeathEvent event);
	}

	public interface HitByBullet extends EventListener {
		/**
		 * Called by the ListenerDelegate when your robot has been hit by an
		 * enemy bullet.
		 */
		public void notifyHitByBullet(HitByBulletEvent event);
	}

	public interface HitRobot extends EventListener {
		/**
		 * Called by the ListenerDelegate when your robot has collided with
		 * another robot.
		 */
		public void notifyHitRobot(HitRobotEvent event);
	}

	public interface HitWall extends EventListener {
		/**
		 * Called by the ListenerDelegate when your robot has collided with a
		 * wall.
		 */
		public void notifyHitWall(HitWallEvent event);
	}

	public interface RobotDeath extends EventListener {
		/**
		 * Called by the ListenerDelegate when an enemy robot has been
		 * destroyed.
		 */
		public void notifyRobotDeath(RobotDeathEvent event);
	}

	public interface ScannedRobot extends EventListener {
		/**
		 * Called by the ListenerDelegate when your radar has swept over an
		 * enemy robot.
		 */
		public void notifyScannedRobot(ScannedRobotEvent event);
	}

	public interface SkippedTurn extends EventListener {
		/**
		 * Called by the ListenerDelegate when your robot has skipped a turn.
		 */
		public void notifySkippedTurn(SkippedTurnEvent event);
	}

	public interface Status extends EventListener {
		/**
		 * Called by the ListenerDelegate every turn with a robot status update.
		 */
		public void notifyStatus(StatusEvent event);
	}

	public interface Win extends EventListener {
		/**
		 * Called by the ListenerDelegate when all robots besides yours have
		 * been destroyed.
		 */
		public void notifyWin(WinEvent event);
	}


	// Internal event listeners

	/**
	 * An EventListener used only by the Hud to inform it of KeyPressedEvents
	 * so that it handle requests to toggle layers.
	 */
	interface _KeyPressed extends EventListener {
		/**
		 * Notifies the Hud that a key was pressed.
		 */
		public void notifyKeyPressed(KeyPressedEvent event);
	}

	/**
	 * An EventListener used only by the ListenerDelegate to inform it of
	 * PaintEvents. The ListenerDelegate registers with itself as being
	 * interested in this event. This is the connection point for
	 * PluggableRobot's debug graphics harness.
	 */
	interface _Paint extends EventListener {
		/**
		 * Notifies the ListenerDelegate that a PaintEvent has been received.
		 */
		public void notifyPaint(PaintEvent event);
	}
}

Hud.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import java.util.ArrayList;

import rjw.pluggablerobot.EventListener._KeyPressed;
import robocode.AdvancedRobot;
import robocode.KeyPressedEvent;

/**
 * The Hud is responsible for drawing debug graphics. The Hud has multiple
 * layers, each of which can be enabled or disabled independently through a key
 * binding. Any object which is interested in contributing to the Hud must
 * register itself with PluggableRobot, declaring which layer it wishes to paint
 * on. Layers are painted in the order in which they were declared, and
 * individual painters are invoked in the order that they were registered for
 * that layer.
 * 
 * Concept based on previous work by Nfwu and David Alves:
 * http://robowiki.net/w/index.php?title=User:Nfwu/Painter
 *
 * @author Robert J. Walker
 */
public class Hud implements _KeyPressed {
	private Canvas _canvas;
	private ArrayList<Layer> _layers = new ArrayList<Layer>();

	public Hud(AdvancedRobot bot) {
		_canvas = new Canvas(bot);
	}

	/**
	 * Constructs a new Layer with the given name and bound to the indicated
	 * key. The enabled argument determines whether or not the layer should
	 * be on or off by default.
	 */
	public void createLayer(int key, String name, boolean enabled) {
		Layer layer = new Layer(key, name, enabled);
		_layers.add(layer);
	}

	/**
	 * Registers a painter with the layer bound to the given key.
	 */
	public void registerPainter(int key, Painter painter) {
		for (Layer layer : _layers) {
			if (layer.getKey() == key) {
				layer.addPainter(painter);
				return;
			}
		}

		throw new IllegalArgumentException("No layer bound to that key!");
	}

	/**
	 * Notifes the Hud that a key was pressed, and toggles the corresponding
	 * layer, if it exists.
	 */
	@Override
	public void notifyKeyPressed(KeyPressedEvent event) {
		Layer layer = getLayer(event.getSourceEvent().getKeyCode());
		 
		if (layer != null) {
			layer.toggle();
		}
	}

	/**
	 * Called by PluggableRobot when it's time to paint the Hud.
	 */
	void paint(long tick) {
		for (Layer layer : _layers) {
			layer.paint(_canvas, tick);
		}
	}

	/**
	 * Returns the layer bound to the given key.
	 */
	private Layer getLayer(int key) {
		for (Layer layer : _layers) {
			if (layer.getKey() == key) {
				return layer;
			}
		}

		return null;
	}


	/**
	 * Objects which implement the Painter interface and are registered
	 * PluggableRobot will get their paint() methods called when it is time for
	 * them to paint. Note that any one object that implements Painter may only
	 * paint on one layer of the Hud.
	 */
	public interface Painter {
		/**
		 * Notifies the Painter that it's time to paint, and provides it with a
		 * Canvas object to paint with.
		 */
		public void paint(Canvas canvas, long tick);
	}

	/**
	 * A single Hud layer, responsible for painting itself by calling the
	 * Painters that which to paint on it. 
	 */
	private static class Layer {
		private int _key;
		private String _name;
		private boolean _enabled;
		private ArrayList<Painter> _painters = new ArrayList<Painter>();

		/**
		 * Constructs a new Layer with the given name and bound to the indicated
		 * key. The enabled argument determines whether or not the layer should
		 * be on or off by default.
		 */
		Layer(int key, String name, boolean enabled) {
			_key = key;
			_name = name;
			_enabled = enabled;
		}

		/**
		 * Returns the key to which this Layer is bound.
		 */
		int getKey() {
			return _key;
		}

		/**
		 * Returns this Layer's name.
		 */
		String getName() {
			return _name;
		}

		/**
		 * Adds a new Painter to this Layer.
		 */
		void addPainter(Painter painter) {
			_painters.add(painter);
		}

		/**
		 * Returns whether or not this Layer is enabled.
		 */
		boolean isEnabled() {
			return _enabled;
		}

		/**
		 * Toggles this Layer between being enabled and disabled.
		 */
		void toggle() {
			_enabled = !_enabled;
		}

		/**
		 * Notifies the Painter that it's time to paint, and provides it with a
		 * Canvas object to paint with.
		 */
		void paint(Canvas canvas, long tick) {
			if (!_enabled) {
				return;
			}

			for (Painter painter : _painters) {
				painter.paint(canvas, tick);
			}
		}
	}
}

ListenerDelegate.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import java.util.*;

import robocode.*;

/**
 * Class that manages all the event listeners for a PluggableRobot and delegates
 * events to the appropriate EventListeners. Unlike the default Robocode
 * behavior, events are doled out first by listener (first registered, first
 * notified), then by the order of the listener interfaces declared on the
 * listener implementation. This allows each component to get notified of events
 * in any order it likes, regardless of event priorities or the order used by
 * other components.
 * 
 * PaintEvents are handled differently; they are captured and held until all
 * EventListeners have been notified and Components have acted, so that as much
 * information as possible is available for debug painting.
 * 
 * @author Robert J. Walker
 */
public class ListenerDelegate implements EventListener._Paint {
	private static HashMap<Class, Invoker> _invokers;

	// Build the invoker map, so we can look up invokers by listener class
	static {
		_invokers = new HashMap<Class, Invoker>();

		// interal paint event listener
		_invokers.put(_Paint.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof PaintEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((_Paint) listener).notifyPaint((PaintEvent) event);
			}
		});

		// interal key pressed event listener
		_invokers.put(_KeyPressed.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof KeyPressedEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((_KeyPressed) listener).notifyKeyPressed(
						(KeyPressedEvent) event
				);
			}
		});

		// battle ended
		_invokers.put(EventListener.BattleEnded.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof BattleEndedEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.BattleEnded) listener).notifyBattleEnded(
						(BattleEndedEvent) event
				);
			}
		});

		// bullet hit
		_invokers.put(EventListener.BulletHit.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof BulletHitEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.BulletHit) listener).notifyBulletHit(
						(BulletHitEvent) event
				);
			}
		});

		// bullet hit bullet
		_invokers.put(EventListener.BulletHitBullet.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof BulletHitBulletEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.BulletHitBullet) listener)
					.notifyBulletHitBullet((BulletHitBulletEvent) event);
			}
		});

		// bullet missed
		_invokers.put(EventListener.BulletMissed.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof BulletMissedEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.BulletMissed) listener).notifyBulletMissed(
						(BulletMissedEvent) event
				);
			}
		});

		// death
		_invokers.put(EventListener.Death.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof DeathEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.Death) listener).notifyDeath(
						(DeathEvent) event
				);
			}
		});

		// hit by bullet
		_invokers.put(EventListener.HitByBullet.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof HitByBulletEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.HitByBullet) listener).notifyHitByBullet(
						(HitByBulletEvent) event
				);
			}
		});

		// hit robot
		_invokers.put(EventListener.HitRobot.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof HitRobotEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.HitRobot) listener).notifyHitRobot(
						(HitRobotEvent) event
				);
			}
		});

		// hit wall
		_invokers.put(EventListener.HitWall.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof HitWallEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.HitWall) listener).notifyHitWall(
						(HitWallEvent) event
				);
			}
		});

		// robot death
		_invokers.put(EventListener.RobotDeath.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof RobotDeathEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.RobotDeath) listener).notifyRobotDeath(
						(RobotDeathEvent) event
				);
			}
		});

		// scanned robot
		_invokers.put(EventListener.ScannedRobot.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof ScannedRobotEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.ScannedRobot) listener).notifyScannedRobot(
						(ScannedRobotEvent) event
				);
			}
		});

		// skipped turn
		_invokers.put(EventListener.SkippedTurn.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof SkippedTurnEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.SkippedTurn) listener).notifySkippedTurn(
						(SkippedTurnEvent) event
				);
			}
		});

		// status
		_invokers.put(EventListener.Status.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof StatusEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.Status) listener).notifyStatus(
						(StatusEvent) event
				);
			}
		});

		// win
		_invokers.put(EventListener.Win.class, new Invoker() {
			protected boolean consumesEvent(Event event) {
				return event instanceof WinEvent;
			}

			protected void invoke(EventListener listener, Event event) {
				((EventListener.Win) listener).notifyWin((WinEvent) event);
			}
		});
	}


	private ArrayList<EventListener> _listeners =
			new ArrayList<EventListener>();
	private PaintEvent _lastPaintEvent = null;

	public ListenerDelegate() {
		register(this);	// register itself for the PaintEvent
	}

	/**
	 * Register a new EventListener.
	 */
	public void register(EventListener listener) {
		_listeners.add(listener);
	}

	/**
	 * Hand out event notifications to the EventListeners. 
	 */
	public void processEvents(List<Event> events) {
		// Notify listeners in the order they were registered
		for (EventListener listener : _listeners) {
			Class[] interfaces = listener.getClass().getInterfaces();

			// Iterate the interfaces on each listener
			for (Class iface : interfaces) {
				// Skip if interface does not descend from EventListener
				if (!EventListener.class.isAssignableFrom(iface)) {
					continue;
				}

				// Get the invoker for this interface
				Invoker invoker = _invokers.get(iface);

				// Find the events this invoker consumes
				for (Event event : events) {
					// Skip if listener isn't interested in this kind of event
					if (!invoker.consumesEvent(event)) {
						continue;
					}

					// Notify the listener
					invoker.invoke(listener, event);
				}
			}
		}
	}

	/**
	 * Called by a special internal EventListener to handle PaintEvents.
	 */
	@Override
	public void notifyPaint(PaintEvent event) {
		_lastPaintEvent = event;
	}

	/**
	 * Returns the most recently received PaintEvent, or null if no PaintEvent
	 * has been received.
	 */
	public PaintEvent getLastPaintEvent() {
		return _lastPaintEvent;
	}


	/**
	 * An object that knows about a Robocode Event class and how to invoke its
	 * corresponding EventListener.
	 */
	private static abstract class Invoker {
		protected abstract boolean consumesEvent(Event event);

		/**
		 * Invokes the given EventListener, passing in a Robocode Event object.
		 */
		protected abstract void invoke(EventListener listener, Event event);
	}
}

Math2.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import java.awt.geom.Point2D;
import java.util.Random;

import robocode.util.Utils;

/**
 * Math utility class. Note that trig functions are reversed in order to provide
 * correct results for the Robocode coordinate system.
 * 
 * @author Robert J. Walker
 */
public final class Math2 {
	public static final double PI_OVER_2 = Math.PI / 2;

	private static final Random random = new Random();

	private Math2() {
	}

	/**
	 * Returns a random int between the min and max arguments, inclusive.
	 */
    public static int randomInteger(int min, int max) {
    	return random.nextInt(max - min + 1) + min;
    }

    /**
     * If value is less than min, returns min. If value is greater than max,
     * returns max. Otherwise, returns value.
     */
    public static double limit(double min, double value, double max) {
        return Math.max(min, Math.min(value, max));
    }

    /**
     * Adds the X and Y components of the given Point2D.Double objects and
     * returns a new Point2D.Double object with the result.
     */
    public static Point2D.Double add(Point2D.Double point1,
    		Point2D.Double point2) {
		return new Point2D.Double(point1.x + point2.x, point1.y + point2.y);
	}

    /**
     * Subtracts the X and Y components of the second given Point2D.Double
     * object from those of the first and returns a new Point2D.Double object
     * with the result.
     */
	public static Point2D.Double subtract(Point2D.Double point1, Point2D.Double point2) {
		return new Point2D.Double(point1.x - point2.x, point1.y - point2.y);
	}

	/**
	 * Returns the absolute bearing in radians from the given origin point to
	 * the given target point.
	 */
	public static double getAbsoluteTargetBearing(Point2D.Double origin,
			Point2D.Double target) {
		return Utils.normalAbsoluteAngle(Math.atan2(target.x - origin.x,
				target.y - origin.y));
	}

	/**
	 * Returns a Point2D.Double object indicating the relative position of an
	 * object at the given angle (in radians) and distance from the origin.
	 */
	public static Point2D.Double getRelativePosition(double angle,
			double distance) {
		double dx = distance * Math.sin(angle);
		double dy = distance * Math.cos(angle);
		return new Point2D.Double(dx, dy);
	}

	/**
	 * Returns a Point2D.Double object indicating the position of an object at
	 * the given angle (in radians) and distance from the given origin point.
	 */
	public static Point2D.Double getAbsolutePosition(Point2D.Double origin,
			double angle, double distance) {
		double x = origin.x + distance * Math.sin(angle);
		double y = origin.y + distance * Math.cos(angle);
		return new Point2D.Double(x, y);
	}

    /**
     * Converts degrees to radians.
     */
    public static double degToRad(double degrees) {
    	return Math.toRadians(degrees);
    }

    /**
     * Converts radians to degrees.
     */
    public static double radToDeg(double radians) {
    	return Math.toDegrees(radians);
    }
}

PluggableRobot.java

/*
 * PluggableRobot, by Robert J. Walker
 * Home page: http://robowiki.net/w/index.php?title=PluggableRobot
 * This software is made available under the RoboWiki Limited Public Code
 * License (RWLPCL). The full text of the license may be found at:
 * http://robowiki.net/w/index.php?title=RWLPCL
 */
package rjw.pluggablerobot;

import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import rjw.pluggablerobot.Hud.Painter;
import robocode.*;

/**
 * An abstract robot class that provides a pluggable architecture, sophisticated
 * event management and advanced debug graphics handling.
 * 
 * @author Robert Walker
 */
public abstract class PluggableRobot extends AdvancedRobot {
	private static boolean _battleInitialized = false;
	private static Point2D.Double _center;

	private ListenerDelegate _listenerDelegate;
	private ArrayList<Component> _components = new ArrayList<Component>();
	private Hud _hud;

	public PluggableRobot() {
		_listenerDelegate = new ListenerDelegate();
	}

	/**
	 * PluggableRobot's main execution loop. Rather that implementing run() in
	 * your own robot, you will register listeners, components and painters in
	 * the initRound() method, and those objects will handle your robot's combat
	 * logic. This allows you to more easily separate concerns into separate
	 * objects, and replace parts to change robot behavior.  
	 */
	@Override
	public final void run() {
		// Initialize battle (at start of first round only)
		if (!_battleInitialized) {
			_center = new Point2D.Double(getWidth() / 2, getHeight() / 2);
			initBattle();
			_battleInitialized = true;
		}

		// Mechanism for capturing all events and handing them to the
		// ListenerDelegate. 
		addCustomEvent(new Condition("eventManager") {
			public boolean test() {
				PluggableRobot.this.handleEvents();
				return false;
			}
		});

		// Initialize the Hud
		_hud = new Hud(this);
		registerListener(_hud);

		// Allow robot to initialize its parts
		initRound();

		// Main loop
		while (true) {
			// Let each component act
			for (Component component : _components) {
				component.go();
			}

			// Handle painting
			PaintEvent pe = _listenerDelegate.getLastPaintEvent();
	
			if (pe != null && pe.getTime() == getTime()) {
				_hud.paint(getTime());
			}

			execute();
		}
	}

	/**
	 * Returns a Point2D object located at the exact center of the battlefield.
	 */
	public Point2D.Double getCenter() {
		return _center;
	}

	/**
	 * Called by PluggableRobot at the start of a battle. Your implementation of
	 * this method should perform whatever operations you wish to handle at the
	 * start of a battle, such as setting tank colors and initializing memory
	 * objects that will persist between rounds.
	 */
	protected abstract void initBattle();

	/**
	 * Called by PluggableRobot at the start of each round. Your implementation
	 * of this method should create Hud layers, register your robot's
	 * EventListeners, Components and Painters, and perform any other operations
	 * you wish to handle at the start of a round.
	 */
	protected abstract void initRound();

	/**
	 * Registers an EventListener. Registration lasts until the end of the round
	 * and enables the listener to receive event notifications.
	 */
	protected final void registerListener(EventListener listener) {
		_listenerDelegate.register(listener);
	}

	/**
	 * Registers a Component. Registration lasts until the end of the round and
	 * enables the Component to be notified when it is time to act.
	 */
	protected void registerComponent(Component component) {
		_components.add(component);
	}

	/**
	 * Constructs a new Hud layer with the given name and bound to the indicated
	 * key. The enabled argument determines whether or not the layer should
	 * be on or off by default.
	 */
	protected void createLayer(int key, String name, boolean enabled) {
		_hud.createLayer(key, name, enabled);
	}

	/**
	 * Registers a Painter and binds it to the layer corresponding to the given
	 * key. Registration lasts until the end of the round and enables the
	 * Painter to draw on a Hud layer when it's time to paint.
	 */
	protected void registerPainter(int key, Painter painter) {
		_hud.registerPainter(key, painter);
	}

	/**
	 * Called by a custom event registered by PluggableRobot. This method hands
	 * the event list to the ListenerDelegate, which will in turn notify the
	 * EventListeners in the desired order.
	 */
	private void handleEvents() {
		_listenerDelegate.processEvents(getAllEvents());
		clearAllEvents();
	}

	/*
	 * Since we've got our own event handling mechanism, we don't want to use
	 * the existing one, so make these methods final.
	 */
	@Override
	public final void onBattleEnded(BattleEndedEvent event) {
	}

	@Override
	public final void onBulletHit(BulletHitEvent event) {
	}

	@Override
	public final void onBulletHitBullet(BulletHitBulletEvent event) {
	}

	@Override
	public final void onBulletMissed(BulletMissedEvent event) {
	}

	@Override
	public final void onCustomEvent(CustomEvent event) {
	}

	@Override
	public final void onDeath(DeathEvent event) {
	}

	@Override
	public final void onHitByBullet(HitByBulletEvent event) {
	}

	@Override
	public final void onHitRobot(HitRobotEvent event) {
	}

	@Override
	public final void onHitWall(HitWallEvent event) {
	}

	@Override
	public final void onKeyPressed(KeyEvent e) {
	}

	@Override
	public final void onKeyReleased(KeyEvent e) {
	}

	@Override
	public final void onKeyTyped(KeyEvent e) {
	}

	@Override
	public final void onMouseClicked(MouseEvent e) {
	}

	@Override
	public final void onMouseDragged(MouseEvent e) {
	}

	@Override
	public final void onMouseEntered(MouseEvent e) {
	}

	@Override
	public final void onMouseExited(MouseEvent e) {
	}

	@Override
	public final void onMouseMoved(MouseEvent e) {
	}

	@Override
	public final void onMousePressed(MouseEvent e) {
	}

	@Override
	public final void onMouseReleased(MouseEvent e) {
	}

	@Override
	public final void onMouseWheelMoved(MouseWheelEvent e) {
	}

	@Override
	public final void onPaint(Graphics2D g) {
	}

	@Override
	public final void onRobotDeath(RobotDeathEvent event) {
	}

	@Override
	public final void onScannedRobot(ScannedRobotEvent event) {
	}

	@Override
	public final void onSkippedTurn(SkippedTurnEvent event) {
	}

	@Override
	public final void onStatus(StatusEvent e) {
	}

	@Override
	public final void onWin(WinEvent event) {
	}
}