Difference between revisions of "PluggableRobot/Source"
Jump to navigation
Jump to search
RobertWalker (talk | contribs) m (→ListenerDelegate.java: Fixed mistake in documentation) |
RobertWalker (talk | contribs) (Version 2.0 alpha) |
||
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 == | ||
+ | <pre><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></pre> | ||
== Component.java == | == Component.java == | ||
Line 5: | Line 330: | ||
/* | /* | ||
* 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(); | ||
Line 33: | Line 362: | ||
/* | /* | ||
* 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); | ||
} | } | ||
} | } | ||
Line 130: | Line 511: | ||
/* | /* | ||
* 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); | |
− | + | } | |
− | + | } | |
− | |||
} | } | ||
} | } | ||
Line 273: | Line 697: | ||
/* | /* | ||
* 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); |
} | } | ||
} | } | ||
Line 445: | Line 1,005: | ||
/* | /* | ||
* 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 554: | Line 1,116: | ||
/* | /* | ||
* 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) { | ||
} | } | ||
Revision as of 22:23, 24 May 2009
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
/* * 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 degrees * Math.PI / 180; } /** * Converts radians to degrees. */ public static double radToDeg(double radians) { return radians * 180 / Math.PI; } }
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) { } }