Difference between revisions of "PluggableRobot/Source"
Jump to navigation
Jump to search
RobertWalker (talk | contribs) m (→ListenerDelegate.java: Fixed mistake in documentation) |
RednaxelaBot (talk | contribs) m (Using <syntaxhighlight>.) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
Below is the source for [[PluggableRobot]]. Pillage to your heart's content. PluggableRobot is released under the [[RWLPCL|RoboWiki Limited Public Code License]]. | Below is the source for [[PluggableRobot]]. Pillage to your heart's content. PluggableRobot is released under the [[RWLPCL|RoboWiki Limited Public Code License]]. | ||
+ | == Canvas.java == | ||
+ | <syntaxhighlight><nowiki> | ||
+ | /* | ||
+ | * 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); | ||
+ | } | ||
+ | } | ||
+ | </nowiki></syntaxhighlight> | ||
== Component.java == | == Component.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
/** | /** | ||
− | * Components encapsulate the main combat behavior of the robot into pluggable modules. Any code | + | * 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 | * @author Robert J. Walker | ||
*/ | */ | ||
public interface Component { | public interface Component { | ||
/** | /** | ||
− | * Asks this Component to execute any actions that it wants to take this turn. PluggableRobot | + | * 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(); | public abstract void go(); | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
== EventListener.java == | == EventListener.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
Line 42: | Line 372: | ||
/** | /** | ||
− | * Event listener interfaces. Objects that wish to be notified of events must extend one of these | + | * 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 | * @author Robert J. Walker | ||
*/ | */ | ||
public interface EventListener { | 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 { | public interface BulletHitBullet extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when a bullet fired by your robot has |
+ | * hit another bullet. | ||
*/ | */ | ||
public void notifyBulletHitBullet(BulletHitBulletEvent event); | public void notifyBulletHitBullet(BulletHitBulletEvent event); | ||
Line 56: | Line 395: | ||
public interface BulletHit extends EventListener { | public interface BulletHit extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when a bullet fired by your robot has |
+ | * hit another robot. | ||
*/ | */ | ||
public void notifyBulletHit(BulletHitEvent event); | public void notifyBulletHit(BulletHitEvent event); | ||
Line 63: | Line 403: | ||
public interface BulletMissed extends EventListener { | public interface BulletMissed extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when a bullet fired by your robot has |
+ | * hit a wall. | ||
*/ | */ | ||
public void notifyBulletMissed(BulletMissedEvent event); | public void notifyBulletMissed(BulletMissedEvent event); | ||
Line 70: | Line 411: | ||
public interface Death extends EventListener { | public interface Death extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your robot has been destroyed. |
*/ | */ | ||
public void notifyDeath(DeathEvent event); | public void notifyDeath(DeathEvent event); | ||
Line 77: | Line 418: | ||
public interface HitByBullet extends EventListener { | public interface HitByBullet extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your robot has been hit by an |
+ | * enemy bullet. | ||
*/ | */ | ||
public void notifyHitByBullet(HitByBulletEvent event); | public void notifyHitByBullet(HitByBulletEvent event); | ||
Line 84: | Line 426: | ||
public interface HitRobot extends EventListener { | public interface HitRobot extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your robot has collided with |
+ | * another robot. | ||
*/ | */ | ||
public void notifyHitRobot(HitRobotEvent event); | public void notifyHitRobot(HitRobotEvent event); | ||
Line 91: | Line 434: | ||
public interface HitWall extends EventListener { | public interface HitWall extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your robot has collided with a |
+ | * wall. | ||
*/ | */ | ||
public void notifyHitWall(HitWallEvent event); | public void notifyHitWall(HitWallEvent event); | ||
Line 98: | Line 442: | ||
public interface RobotDeath extends EventListener { | public interface RobotDeath extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when an enemy robot has been |
+ | * destroyed. | ||
*/ | */ | ||
public void notifyRobotDeath(RobotDeathEvent event); | public void notifyRobotDeath(RobotDeathEvent event); | ||
Line 105: | Line 450: | ||
public interface ScannedRobot extends EventListener { | public interface ScannedRobot extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your radar has swept over an |
+ | * enemy robot. | ||
*/ | */ | ||
public void notifyScannedRobot(ScannedRobotEvent event); | public void notifyScannedRobot(ScannedRobotEvent event); | ||
Line 112: | Line 458: | ||
public interface SkippedTurn extends EventListener { | public interface SkippedTurn extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when your robot has skipped a turn. |
*/ | */ | ||
public void notifySkippedTurn(SkippedTurnEvent event); | 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 { | public interface Win extends EventListener { | ||
/** | /** | ||
− | * Called by | + | * Called by the ListenerDelegate when all robots besides yours have |
+ | * been destroyed. | ||
*/ | */ | ||
public void notifyWin(WinEvent event); | 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); | ||
} | } | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
== Hud.java == | == Hud.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
+ | import java.util.ArrayList; | ||
− | import | + | import rjw.pluggablerobot.EventListener._KeyPressed; |
− | import | + | import robocode.AdvancedRobot; |
− | + | import robocode.KeyPressedEvent; | |
− | import robocode. | ||
/** | /** | ||
− | * | + | * 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 | * @author Robert J. Walker | ||
*/ | */ | ||
− | public class Hud { | + | 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 | + | 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 | + | 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. | ||
*/ | */ | ||
− | public void | + | @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 | + | 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); | |
− | + | } | |
− | + | } | |
− | |||
} | } | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
== ListenerDelegate.java == | == ListenerDelegate.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
Line 284: | Line 709: | ||
/** | /** | ||
− | * Class that manages all the event listeners for a PluggableRobot and delegates events to the | + | * 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 | * @author Robert J. Walker | ||
*/ | */ | ||
− | public class ListenerDelegate { | + | public class ListenerDelegate implements EventListener._Paint { |
− | + | private static HashMap<Class, Invoker> _invokers; | |
− | private static HashMap<Class | ||
− | // Build the invoker map, | + | // Build the invoker map, so we can look up invokers by listener class |
static { | static { | ||
− | _invokers = new HashMap<Class | + | _invokers = new HashMap<Class, Invoker>(); |
− | _invokers.put( | + | |
− | + | // 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) { | |
− | _invokers.put(EventListener. | + | ((_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; |
− | _invokers.put(EventListener. | + | } |
− | + | ||
− | + | protected void invoke(EventListener listener, Event event) { | |
− | + | ((_KeyPressed) listener).notifyKeyPressed( | |
− | + | (KeyPressedEvent) event | |
− | + | ); | |
− | + | } | |
− | + | }); | |
− | _invokers.put(EventListener. | + | |
− | + | // 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) { | |
− | _invokers.put(EventListener.HitByBullet.class, | + | ((EventListener.BattleEnded) listener).notifyBattleEnded( |
− | + | (BattleEndedEvent) event | |
− | + | ); | |
− | + | } | |
− | + | }); | |
− | + | ||
− | + | // bullet hit | |
− | + | _invokers.put(EventListener.BulletHit.class, new Invoker() { | |
− | _invokers.put(EventListener. | + | protected boolean consumesEvent(Event event) { |
− | + | return event instanceof BulletHitEvent; | |
− | + | } | |
− | + | ||
− | + | protected void invoke(EventListener listener, Event event) { | |
− | + | ((EventListener.BulletHit) listener).notifyBulletHit( | |
− | + | (BulletHitEvent) event | |
− | + | ); | |
− | _invokers.put(EventListener. | + | } |
− | + | }); | |
− | + | ||
− | + | // bullet hit bullet | |
− | + | _invokers.put(EventListener.BulletHitBullet.class, new Invoker() { | |
− | + | protected boolean consumesEvent(Event event) { | |
− | + | return event instanceof BulletHitBulletEvent; | |
− | + | } | |
− | _invokers.put(EventListener. | + | |
− | + | protected void invoke(EventListener listener, Event event) { | |
− | + | ((EventListener.BulletHitBullet) listener) | |
− | + | .notifyBulletHitBullet((BulletHitBulletEvent) event); | |
− | + | } | |
− | + | }); | |
− | + | ||
− | + | // bullet missed | |
− | _invokers.put(EventListener. | + | _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 | |
− | _invokers.put(EventListener. | + | ); |
− | + | } | |
− | + | }); | |
− | + | ||
− | + | // death | |
− | + | _invokers.put(EventListener.Death.class, new Invoker() { | |
− | + | protected boolean consumesEvent(Event event) { | |
− | + | return event instanceof DeathEvent; | |
− | _invokers.put(EventListener. | + | } |
− | + | ||
− | + | 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 | ||
} | } | ||
Line 399: | Line 940: | ||
* Hand out event notifications to the EventListeners. | * Hand out event notifications to the EventListeners. | ||
*/ | */ | ||
− | public void processEvents( | + | public void processEvents(List<Event> events) { |
// Notify listeners in the order they were registered | // Notify listeners in the order they were registered | ||
− | for(EventListener listener : _listeners) { | + | for (EventListener listener : _listeners) { |
Class[] interfaces = listener.getClass().getInterfaces(); | Class[] interfaces = listener.getClass().getInterfaces(); | ||
− | // Iterate the interfaces on each listener | + | // Iterate the interfaces on each listener |
− | for(Class iface : interfaces) { | + | for (Class iface : interfaces) { |
− | if(!EventListener.class.isAssignableFrom(iface)) continue; | + | // 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); | |
− | |||
− | invoker. | ||
} | } | ||
} | } | ||
Line 421: | Line 969: | ||
} | } | ||
− | |||
/** | /** | ||
− | * An object that knows about a Robocode Event class and how to invoke its | + | * Called by a special internal EventListener to handle PaintEvents. |
− | * EventListener. | + | */ |
− | + | @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 | + | private static abstract class Invoker { |
− | + | protected abstract boolean consumesEvent(Event event); | |
− | |||
− | |||
− | protected abstract | ||
/** | /** | ||
* Invokes the given EventListener, passing in a Robocode Event object. | * Invokes the given EventListener, passing in a Robocode Event object. | ||
*/ | */ | ||
− | protected abstract void | + | protected abstract void invoke(EventListener listener, Event event); |
} | } | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
== Math2.java == | == Math2.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
Line 457: | Line 1,018: | ||
/** | /** | ||
− | * Math utility class. | + | * Math utility class. Note that trig functions are reversed in order to provide |
+ | * correct results for the Robocode coordinate system. | ||
+ | * | ||
* @author Robert J. Walker | * @author Robert J. Walker | ||
*/ | */ | ||
Line 476: | Line 1,039: | ||
/** | /** | ||
− | + | * If value is less than min, returns min. If value is greater than max, | |
− | + | * returns max. Otherwise, returns value. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | * If value is less than min, returns min. If value is greater than max, returns max. Otherwise, | ||
− | |||
*/ | */ | ||
public static double limit(double min, double value, double max) { | public static double limit(double min, double value, double max) { | ||
Line 491: | Line 1,047: | ||
/** | /** | ||
− | * Adds the X and Y components of the given Point2D.Double objects and returns a new | + | * 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) { | + | public static Point2D.Double add(Point2D.Double point1, |
+ | Point2D.Double point2) { | ||
return new Point2D.Double(point1.x + point2.x, point1.y + point2.y); | 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 | + | * 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) { | public static Point2D.Double subtract(Point2D.Double point1, Point2D.Double point2) { | ||
Line 507: | Line 1,065: | ||
/** | /** | ||
− | * Returns the absolute bearing in radians from the given origin point to the given target | + | * 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) { | + | public static double getAbsoluteTargetBearing(Point2D.Double origin, |
− | return Utils.normalAbsoluteAngle(Math.atan2(target.x - origin.x, target.y - origin.y)); | + | 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 | + | * 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) { | + | public static Point2D.Double getRelativePosition(double angle, |
+ | double distance) { | ||
double dx = distance * Math.sin(angle); | double dx = distance * Math.sin(angle); | ||
double dy = distance * Math.cos(angle); | double dy = distance * Math.cos(angle); | ||
Line 525: | Line 1,086: | ||
/** | /** | ||
− | * Returns a Point2D.Double object indicating the position of an object at the given angle and | + | * 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) { | + | public static Point2D.Double getAbsolutePosition(Point2D.Double origin, |
+ | double angle, double distance) { | ||
double x = origin.x + distance * Math.sin(angle); | double x = origin.x + distance * Math.sin(angle); | ||
double y = origin.y + distance * Math.cos(angle); | double y = origin.y + distance * Math.cos(angle); | ||
Line 538: | Line 1,100: | ||
*/ | */ | ||
public static double degToRad(double degrees) { | public static double degToRad(double degrees) { | ||
− | return | + | return Math.toRadians(degrees); |
} | } | ||
Line 545: | Line 1,107: | ||
*/ | */ | ||
public static double radToDeg(double radians) { | public static double radToDeg(double radians) { | ||
− | return | + | return Math.toDegrees(radians); |
} | } | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
== PluggableRobot.java == | == PluggableRobot.java == | ||
− | < | + | <syntaxhighlight><nowiki> |
/* | /* | ||
* PluggableRobot, by Robert J. Walker | * PluggableRobot, by Robert J. Walker | ||
− | * Home page: http://robowiki.net/ | + | * 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 | + | * 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; | package rjw.pluggablerobot; | ||
import java.awt.Graphics2D; | 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.awt.geom.Point2D; | ||
import java.util.ArrayList; | import java.util.ArrayList; | ||
+ | import rjw.pluggablerobot.Hud.Painter; | ||
import robocode.*; | import robocode.*; | ||
/** | /** | ||
− | * | + | * An abstract robot class that provides a pluggable architecture, sophisticated |
− | * | + | * event management and advanced debug graphics handling. |
− | * @author Robert | + | * |
+ | * @author Robert Walker | ||
*/ | */ | ||
public abstract class PluggableRobot extends AdvancedRobot { | public abstract class PluggableRobot extends AdvancedRobot { | ||
private static boolean _battleInitialized = false; | private static boolean _battleInitialized = false; | ||
− | private static | + | private static Point2D.Double _center; |
private ListenerDelegate _listenerDelegate; | private ListenerDelegate _listenerDelegate; | ||
− | private ArrayList<Component> _components | + | private ArrayList<Component> _components = new ArrayList<Component>(); |
− | + | private Hud _hud; | |
− | private | ||
− | + | public PluggableRobot() { | |
− | |||
− | |||
− | |||
_listenerDelegate = new ListenerDelegate(); | _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 | @Override | ||
public final void run() { | public final void run() { | ||
// Initialize battle (at start of first round only) | // Initialize battle (at start of first round only) | ||
− | if(!_battleInitialized) { | + | if (!_battleInitialized) { |
− | + | _center = new Point2D.Double(getWidth() / 2, getHeight() / 2); | |
− | + | initBattle(); | |
_battleInitialized = true; | _battleInitialized = true; | ||
} | } | ||
− | // | + | // Mechanism for capturing all events and handing them to the |
+ | // ListenerDelegate. | ||
addCustomEvent(new Condition("eventManager") { | addCustomEvent(new Condition("eventManager") { | ||
public boolean test() { | public boolean test() { | ||
Line 609: | Line 1,176: | ||
}); | }); | ||
− | // | + | // Initialize the Hud |
− | + | _hud = new Hud(this); | |
+ | registerListener(_hud); | ||
+ | |||
+ | // Allow robot to initialize its parts | ||
+ | initRound(); | ||
// Main loop | // Main loop | ||
− | while(true) { | + | while (true) { |
− | for(Component component : _components) { | + | // Let each component act |
+ | for (Component component : _components) { | ||
component.go(); | component.go(); | ||
+ | } | ||
+ | |||
+ | // Handle painting | ||
+ | PaintEvent pe = _listenerDelegate.getLastPaintEvent(); | ||
+ | |||
+ | if (pe != null && pe.getTime() == getTime()) { | ||
+ | _hud.paint(getTime()); | ||
} | } | ||
Line 623: | Line 1,202: | ||
/** | /** | ||
− | * | + | * 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 void | + | protected abstract void initBattle(); |
− | |||
− | |||
/** | /** | ||
− | * Called | + | * 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) { | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | protected void registerListener(EventListener listener) { | ||
_listenerDelegate.register(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) { | protected void registerComponent(Component component) { | ||
Line 675: | Line 1,241: | ||
/** | /** | ||
− | * | + | * 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 | + | 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); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | _hud. | ||
} | } | ||
/** | /** | ||
− | * | + | * 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() { | private void handleEvents() { | ||
− | |||
_listenerDelegate.processEvents(getAllEvents()); | _listenerDelegate.processEvents(getAllEvents()); | ||
clearAllEvents(); | 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 | @Override | ||
Line 716: | Line 1,297: | ||
@Override | @Override | ||
− | public final void | + | 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 | @Override | ||
− | public final void | + | public final void onMouseExited(MouseEvent e) { |
} | } | ||
@Override | @Override | ||
− | public final void | + | public final void onMouseMoved(MouseEvent e) { |
} | } | ||
@Override | @Override | ||
− | public final void | + | public final void onMousePressed(MouseEvent e) { |
} | } | ||
@Override | @Override | ||
− | public final void | + | public final void onMouseReleased(MouseEvent e) { |
} | } | ||
@Override | @Override | ||
− | public final void | + | public final void onMouseWheelMoved(MouseWheelEvent e) { |
} | } | ||
@Override | @Override | ||
− | public final void | + | public final void onPaint(Graphics2D g) { |
} | } | ||
Line 749: | Line 1,362: | ||
@Override | @Override | ||
public final void onScannedRobot(ScannedRobotEvent event) { | public final void onScannedRobot(ScannedRobotEvent event) { | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public final void onSkippedTurn(SkippedTurnEvent event) { | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public final void onStatus(StatusEvent e) { | ||
} | } | ||
Line 755: | Line 1,376: | ||
} | } | ||
} | } | ||
− | </nowiki></ | + | </nowiki></syntaxhighlight> |
[[Category:Source Code|PluggableRobot/Source]] | [[Category:Source Code|PluggableRobot/Source]] |
Latest revision as of 09:30, 1 July 2010
Below is the source for PluggableRobot. Pillage to your heart's content. PluggableRobot is released under the RoboWiki Limited Public Code License.
Contents
Canvas.java
<nowiki>
/*
* 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);
}
}
</nowiki>
Component.java
<nowiki>
/*
* 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();
}
</nowiki>
EventListener.java
<nowiki>
/*
* 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);
}
}
</nowiki>
Hud.java
<nowiki>
/*
* 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);
}
}
}
}
</nowiki>
ListenerDelegate.java
<nowiki>
/*
* 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);
}
}
</nowiki>
Math2.java
<nowiki>
/*
* 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);
}
}
</nowiki>
PluggableRobot.java
<nowiki>
/*
* 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) {
}
}
</nowiki>