Difference between revisions of "PluggableRobot/Source"
Jump to navigation
Jump to search
RobertWalker (talk | contribs) (Migrated from old wiki, consolidated onto one page) |
RobertWalker (talk | contribs) m (Added summary) |
||
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]]. | ||
+ | |||
== Component.java == | == Component.java == | ||
<pre><nowiki> | <pre><nowiki> |
Revision as of 18:07, 12 December 2007
Below is the source for PluggableRobot. Pillage to your heart's content. PluggableRobot is released under the RoboWiki Limited Public Code License.
Contents
Component.java
/* * PluggableRobot, by Robert J. Walker * Home page: http://robowiki.net/cgi-bin/robowiki?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/cgi-bin/robowiki?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/cgi-bin/robowiki?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/cgi-bin/robowiki?RWLPCL. */ package rjw.pluggablerobot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.util.ArrayList; import robocode.*; /** * A pluggable listener and strategy architecture for a robot. * http://robowiki.net/cgi-bin/robowiki?PluggableRobot * @author Robert J. Walker */ public abstract class PluggableRobot extends AdvancedRobot { private static boolean _battleInitialized = false; private static Hud _hud; private ListenerDelegate _listenerDelegate; private ArrayList<Component> _components; private ArrayList<Hud.Painter> _painters; private Point2D.Double _center; /** * Sets up the ListenerDelegate and the Component and Painter lists. */ protected PluggableRobot() { _listenerDelegate = new ListenerDelegate(); _components = new ArrayList<Component>(); _painters = new ArrayList<Hud.Painter>(); } /** * Set up the robot, then continuously collect events and invoke components. */ @Override public final void run() { // Initialize battle (at start of first round only) if(!_battleInitialized) { _hud = new Hud(this); initializeBattle(); _battleInitialized = true; } // Register custom event test() hook for event manager addCustomEvent(new Condition("eventManager") { public boolean test() { PluggableRobot.this.handleEvents(); return false; } }); // Round is starting initializeRound(); // Main loop while(true) { for(Component component : _components) { component.go(); } execute(); } } /** * This method will be called at the beginning of a battle. Robots can override this method to * initialize their properties. This is a good place to initialize static properties or set your * tank color. */ protected void initializeBattle() { // Default implementation does nothing } /** * This method will be called at the beginning of each round. Robots can override this method to * initialize their properties. This is a good place to set up non-static properties and * register listeners, painters and components. */ protected void initializeRound() { // Default implementation does nothing } /** * Called before events get processed each tick. The default implementation does nothing. */ public void onBeforeEventsProcessed() { // Do nothing } /** * Returns a Point2D.Double object representing the center of the battlefield. */ public Point2D.Double getCenter() { if(_center == null) { _center = new Point2D.Double(getBattleFieldWidth() / 2, getBattleFieldHeight() / 2); } return _center; } /** * Registers the given EventListener, which will cause it to receive notifications of the events * indicated by the listener interfaces it implements. */ protected void registerListener(EventListener listener) { _listenerDelegate.register(listener); } /** * Reigsters the given Component, which will give it the opportunity to act each turn. */ protected void registerComponent(Component component) { _components.add(component); } /** * Reigsters the given Painter, which will give it the opportunity to draw on the HUD each turn. */ protected void registerPainter(Hud.Painter painter) { _painters.add(painter); } /** * Hand out notifications to the Painters. */ @Override public final void onPaint(Graphics2D g) { _hud.setContext(g); // Inject the graphics context into the Hud for(Hud.Painter painter : _painters) { painter.paint(_hud, getTime()); } _hud.setContext(null); // Clear the injected graphics context } /** * Process all the events in the queue. */ private void handleEvents() { onBeforeEventsProcessed(); _listenerDelegate.processEvents(getAllEvents()); clearAllEvents(); } // Since we have our own event manager, we want to prevent overrides of the Robocode event // methods, so we'll make them final. @Override public final void onCustomEvent(CustomEvent event) { } @Override public final void onDeath(DeathEvent event) { } @Override public final void onSkippedTurn(SkippedTurnEvent 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 onHitByBullet(HitByBulletEvent event) { } @Override public final void onHitRobot(HitRobotEvent event) { } @Override public final void onHitWall(HitWallEvent event) { } @Override public final void onRobotDeath(RobotDeathEvent event) { } @Override public final void onScannedRobot(ScannedRobotEvent event) { } @Override public final void onWin(WinEvent event) { } }
Hud.java
/* * PluggableRobot, by Robert J. Walker * Home page: http://robowiki.net/cgi-bin/robowiki?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/cgi-bin/robowiki?RWLPCL. */ package rjw.pluggablerobot; import java.awt.Graphics2D; import java.awt.Paint; import robocode.Robot; /** * Facilitates drawing graphics on the arena by automatically converting between values used in * calculations to values used for drawing. The graphics context (Graphics2D) is injected into the * Hud just before it is handed to Painters, then removed afterward. * @author Robert J. Walker */ public class Hud { /** * Objects which implement this interface can be passed to PluggableRobot.registerPainter(), and * will get their paint() methods called when it's time to draw the HUD. */ public interface Painter { /** * This method will be called by PluggableRobot when it's time for this object to draw. */ public void paint(Hud hud, long tick); } private Graphics2D _g; public double _w; public double _h; /** * Creates a new Hud object. */ public Hud(Robot bot) { _w = bot.getBattleFieldWidth(); _h = bot.getBattleFieldHeight(); } /** * Draws a point at the given coordinates. */ public void drawPoint(double x, double y) { int x0 = i(x); int y0 = i(y); _g.drawLine(x0, y0, x0, y0); } /** * Draws a line between the two indicated points. */ public void drawLine(double x1, double y1, double x2, double y2) { _g.drawLine(i(x1), i(y1), i(x2), i(y2)); } /** * Draws a circle with the given center point 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); } public void drawArc(double x, double y, double r, double start, double extend) { int d = i(r * 2); _g.drawArc(i(x - r), i(y - r), d, d, i(Math2.radToDeg(start)), i(Math2.radToDeg(extend))); } /** * Draws a rectangle with the given position and dimensions. */ public void drawRect(double x, double y, double w, double h) { _g.drawRect(i(x), i(y), i(w), i(h)); } /** * Draws a filled rectangle with the given position and dimensions. */ public void drawFilledRect(double x, double y, double w, double h) { _g.fillRect(i(x), i(y), i(w), i(h)); } /** * Draws a left-aligned String at the given position. */ public void drawString(String s, double x, double y) { _g.drawString(s, i(x), i(y)); } /** * Draws a right-aligned String at the given position. */ public void drawStringRight(String s, double x, double y) { _g.drawString(s, i(x) - _g.getFontMetrics().stringWidth(s), i(y)); } /** * Sets the Hud's current drawing paint. */ public void setPaint(Paint c) { _g.setPaint(c); } /** * Returns the width of the battlefield. This is useful in situations where you don't have * access to the robot object. */ public double getWidth() { return _w; } /** * Returns the height of the battlefield. This is useful in situations where you don't have * access to the robot object. */ public double getHeight() { return _h; } /** * Allows PluggableRobot to inject the graphics context into the Hud. */ public void setContext(Graphics2D g) { _g = g; } /** * Rounds the given double value to the nearest int. */ private int i(double val) { return (int) Math.round(val); } }
ListenerDelegate.java
/* * PluggableRobot, by Robert J. Walker * Home page: http://robowiki.net/cgi-bin/robowiki?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/cgi-bin/robowiki?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. So a class with a declaration like this: * public class MyClass implements ScannedRobotListener, EventListener.Death * ...will get notified of the ScannedRobotEvent *before* the DeathEvent! * @author Robert J. Walker */ public class ListenerDelegate { private ArrayList<EventListener> _listeners = new ArrayList<EventListener>(); private static HashMap<Class<? extends EventListener>, ListenerInvoker> _invokers; // Build the invoker map, allowing us to look up invokers by listener class static { _invokers = new HashMap<Class<? extends EventListener>, ListenerInvoker>(); _invokers.put(EventListener.Death.class, new ListenerInvoker<EventListener.Death, DeathEvent>() { protected Class<DeathEvent> eventClass() { return DeathEvent.class; } protected void invokeListener(EventListener.Death listener, DeathEvent event) { listener.notifyDeath(event); } } ); _invokers.put(EventListener.Win.class, new ListenerInvoker<EventListener.Win, WinEvent>() { protected Class<WinEvent> eventClass() { return WinEvent.class; } protected void invokeListener(EventListener.Win listener, WinEvent event) { listener.notifyWin(event); } } ); _invokers.put(EventListener.SkippedTurn.class, new ListenerInvoker<EventListener.SkippedTurn, SkippedTurnEvent>() { protected Class<SkippedTurnEvent> eventClass() { return SkippedTurnEvent.class; } protected void invokeListener(EventListener.SkippedTurn listener, SkippedTurnEvent event) { listener.notifySkippedTurn(event); } } ); _invokers.put(EventListener.ScannedRobot.class, new ListenerInvoker<EventListener.ScannedRobot, ScannedRobotEvent>() { protected Class<ScannedRobotEvent> eventClass() { return ScannedRobotEvent.class; } protected void invokeListener(EventListener.ScannedRobot listener, ScannedRobotEvent event) { listener.notifyScannedRobot(event); } } ); _invokers.put(EventListener.HitByBullet.class, new ListenerInvoker<EventListener.HitByBullet, HitByBulletEvent>() { protected Class<HitByBulletEvent> eventClass() { return HitByBulletEvent.class; } protected void invokeListener(EventListener.HitByBullet listener, HitByBulletEvent event) { listener.notifyHitByBullet(event); } } ); _invokers.put(EventListener.BulletHit.class, new ListenerInvoker<EventListener.BulletHit, BulletHitEvent>() { protected Class<BulletHitEvent> eventClass() { return BulletHitEvent.class; } protected void invokeListener(EventListener.BulletHit listener, BulletHitEvent event) { listener.notifyBulletHit(event); } } ); _invokers.put(EventListener.BulletHitBullet.class, new ListenerInvoker<EventListener.BulletHitBullet, BulletHitBulletEvent>() { protected Class<BulletHitBulletEvent> eventClass() { return BulletHitBulletEvent.class; } protected void invokeListener(EventListener.BulletHitBullet listener, BulletHitBulletEvent event) { listener.notifyBulletHitBullet(event); } } ); _invokers.put(EventListener.BulletMissed.class, new ListenerInvoker<EventListener.BulletMissed, BulletMissedEvent>() { protected Class<BulletMissedEvent> eventClass() { return BulletMissedEvent.class; } protected void invokeListener(EventListener.BulletMissed listener, BulletMissedEvent event) { listener.notifyBulletMissed(event); } } ); _invokers.put(EventListener.HitRobot.class, new ListenerInvoker<EventListener.HitRobot, HitRobotEvent>() { protected Class<HitRobotEvent> eventClass() { return HitRobotEvent.class; } protected void invokeListener(EventListener.HitRobot listener, HitRobotEvent event) { listener.notifyHitRobot(event); } } ); _invokers.put(EventListener.HitWall.class, new ListenerInvoker<EventListener.HitWall, HitWallEvent>() { protected Class<HitWallEvent> eventClass() { return HitWallEvent.class; } protected void invokeListener(EventListener.HitWall listener, HitWallEvent event) { listener.notifyHitWall(event); } } ); _invokers.put(EventListener.RobotDeath.class, new ListenerInvoker<EventListener.RobotDeath, RobotDeathEvent>() { protected Class<RobotDeathEvent> eventClass() { return RobotDeathEvent.class; } protected void invokeListener(EventListener.RobotDeath listener, RobotDeathEvent event) { listener.notifyRobotDeath(event); } } ); } /** * Register a new EventListener. */ public void register(EventListener listener) { _listeners.add(listener); } /** * Hand out event notifications to the EventListeners. */ public void processEvents(Vector<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 that descend from EventListener for(Class iface : interfaces) { if(!EventListener.class.isAssignableFrom(iface)) continue; // Get the invoker and the corresponding event class for this interface ListenerInvoker invoker = _invokers.get(iface); Class<? extends Event> eventClass = invoker.eventClass(); // Iterate the events and hand the ones of the proper type to the invoker for(Event event : events) { if(!eventClass.isAssignableFrom(event.getClass())) continue; invoker.invokeListener(listener, event); } } } } /** * An object that knows about a Robocode Event class and how to invoke its corresponding * EventListener. * @author Robert J. Walker */ private static abstract class ListenerInvoker<K extends EventListener, V extends Event> { /** * Returns the Robocode Event class handled by this ListenerInvoker. */ protected abstract Class<V> eventClass(); /** * Invokes the given EventListener, passing in a Robocode Event object. */ protected abstract void invokeListener(K listener, V event); } }
Math2.java
/* * PluggableRobot, by Robert J. Walker * Home page: http://robowiki.net/cgi-bin/robowiki?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/cgi-bin/robowiki?RWLPCL. */ package rjw.pluggablerobot; import java.awt.geom.Point2D; import java.util.Random; import robocode.util.Utils; /** * Math utility class. * @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; } /** * Returns a random double value between 0.0 (inclusive) and 1.0 (exclusive). */ public static double randomDouble() { return random.nextDouble(); } /** * 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 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 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/cgi-bin/robowiki?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/cgi-bin/robowiki?RWLPCL. */ package rjw.pluggablerobot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.util.ArrayList; import robocode.*; /** * A pluggable listener and strategy architecture for a robot. * http://robowiki.net/cgi-bin/robowiki?PluggableRobot * @author Robert J. Walker */ public abstract class PluggableRobot extends AdvancedRobot { private static boolean _battleInitialized = false; private static Hud _hud; private ListenerDelegate _listenerDelegate; private ArrayList<Component> _components; private ArrayList<Hud.Painter> _painters; private Point2D.Double _center; /** * Sets up the ListenerDelegate and the Component and Painter lists. */ protected PluggableRobot() { _listenerDelegate = new ListenerDelegate(); _components = new ArrayList<Component>(); _painters = new ArrayList<Hud.Painter>(); } /** * Set up the robot, then continuously collect events and invoke components. */ @Override public final void run() { // Initialize battle (at start of first round only) if(!_battleInitialized) { _hud = new Hud(this); initializeBattle(); _battleInitialized = true; } // Register custom event test() hook for event manager addCustomEvent(new Condition("eventManager") { public boolean test() { PluggableRobot.this.handleEvents(); return false; } }); // Round is starting initializeRound(); // Main loop while(true) { for(Component component : _components) { component.go(); } execute(); } } /** * This method will be called at the beginning of a battle. Robots can override this method to * initialize their properties. This is a good place to initialize static properties or set your * tank color. */ protected void initializeBattle() { // Default implementation does nothing } /** * This method will be called at the beginning of each round. Robots can override this method to * initialize their properties. This is a good place to set up non-static properties and * register listeners, painters and components. */ protected void initializeRound() { // Default implementation does nothing } /** * Called before events get processed each tick. The default implementation does nothing. */ public void onBeforeEventsProcessed() { // Do nothing } /** * Returns a Point2D.Double object representing the center of the battlefield. */ public Point2D.Double getCenter() { if(_center == null) { _center = new Point2D.Double(getBattleFieldWidth() / 2, getBattleFieldHeight() / 2); } return _center; } /** * Registers the given EventListener, which will cause it to receive notifications of the events * indicated by the listener interfaces it implements. */ protected void registerListener(EventListener listener) { _listenerDelegate.register(listener); } /** * Reigsters the given Component, which will give it the opportunity to act each turn. */ protected void registerComponent(Component component) { _components.add(component); } /** * Reigsters the given Painter, which will give it the opportunity to draw on the HUD each turn. */ protected void registerPainter(Hud.Painter painter) { _painters.add(painter); } /** * Hand out notifications to the Painters. */ @Override public final void onPaint(Graphics2D g) { _hud.setContext(g); // Inject the graphics context into the Hud for(Hud.Painter painter : _painters) { painter.paint(_hud, getTime()); } _hud.setContext(null); // Clear the injected graphics context } /** * Process all the events in the queue. */ private void handleEvents() { onBeforeEventsProcessed(); _listenerDelegate.processEvents(getAllEvents()); clearAllEvents(); } // Since we have our own event manager, we want to prevent overrides of the Robocode event // methods, so we'll make them final. @Override public final void onCustomEvent(CustomEvent event) { } @Override public final void onDeath(DeathEvent event) { } @Override public final void onSkippedTurn(SkippedTurnEvent 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 onHitByBullet(HitByBulletEvent event) { } @Override public final void onHitRobot(HitRobotEvent event) { } @Override public final void onHitWall(HitWallEvent event) { } @Override public final void onRobotDeath(RobotDeathEvent event) { } @Override public final void onScannedRobot(ScannedRobotEvent event) { } @Override public final void onWin(WinEvent event) { } }