User:AaronR/HatLeagueRobot
Jump to navigation
Jump to search
Yet another base class for robots participating in the Hat League.
Contents
Version history
- 0.2 The "I didn't even expect it to work this well" release.
- Release: 2008-01-15
- Bugfix: the enemy-scanned event didn't bother to check whether the scanned robot was actually an enemy...
- The API has been changed to pass bullet power instead of bullet speed as the parameter to the partner-bullet-fired and enemy-wave-fired events.
- The internal implementation now properly handles multiple messages that are merged into a single string (separated by "\n"). It will also ignore blank messages.
- Added hlGetRadarTarget() and hlGetBulletTarget() methods that return the values previously supplied through the corresponding hlSetRadarTarget() and hlSetBulletTarget() methods.
- 0.1 The "This should save some time" release.
- Release: 2008-01-14
- A largely untested and probably buggy robot framework.
What's next
- Debugging, probably.
- Make it sort out duplicate enemy-scanned and enemy-fired-wave events registered by both robots.
- Better enemy wave detection.
Source code
Usage rules
- If a method begins with "
hl
", use it instead of the normal Robocode method. - See rule 1.
The HatLeagueRobot class
package wiki.hat; import static java.lang.Math.*; import static robocode.Rules.*; import java.awt.geom.Point2D; import java.io.IOException; import java.util.*; import robocode.*; public abstract class HatLeagueRobot extends TeamRobot { // OVERRIDE THESE IN SUBCLASSES, NOT THE NORMAL ROBOCODE METHODS! // /** * Don't: * * public void hlRun() { * // ... * * do { * // ... * execute(); * } while (true); * } * * Do: * * public void hlRun() { * // ... * } * * public void hlOnTickEnd() { * // ... * } */ public void hlRun() { } public void hlOnTickBegin() { } public void hlOnTickEnd() { } public void hlOnCustomEvent(CustomEvent e) { } public void hlOnPartnerDataReceived(long time, Point2D.Double location, double heading, double velocity, double energy) { } /** * Note that this method is called both when this robot scans an enemy and * when the partner scans one. */ public void hlOnEnemyDataReceived(long time, Point2D.Double location, double heading, double velocity, double energy, String name) { } public void hlOnPartnerBulletOriginReceived(long time, Point2D.Double location, double heading, double power) { } /** * Note that this method is called both when this robot scans an enemy that * fired a wave and when the partner scans one. */ public void hlOnEnemyWaveOriginReceived(long time, Point2D.Double location, double power) { } public void hlOnPartnerRadarTargetReceived(String target) { } public void hlOnPartnerBulletTargetReceived(String target) { } public void hlOnSuggestedRadarTargetReceived(String target) { } public void hlOnSuggestedBulletTargetReceived(String target) { } public void hlOnCustomMessageReceived(String message) { } // SPECIAL HAT LEAGUE ACTION METHODS. // private static class HatLeagueMessage { private String name; private String[] values; public HatLeagueMessage(String name, String... values) { this.name = name; this.values = values; } public String getName() { return name; } public String[] getValues() { return values; } } /** * Use this everywhere you would normally use setFire(). */ public final Bullet hlSetFireBullet(double bulletPower) { // Copied from robocode.peer.RobotPeer.java. =) if (getGunHeat() > 0 || getEnergy() == 0) { return null; } double realBulletPower = min(getEnergy(), min(max(bulletPower, Rules.MIN_BULLET_POWER), Rules.MAX_BULLET_POWER)); broadcastHatLeagueMessage(new HatLeagueMessage("BO", Long.toString(getTime()), Double.toString(getX()), Double.toString(getY()), Double.toString(getGunHeadingRadians()), Double.toString(getBulletSpeed(realBulletPower)))); return setFireBullet(realBulletPower); } private String radarTarget = null; private String bulletTarget = null; public final String hlGetRadarTarget() { return radarTarget; } public final void hlSetRadarTarget(String target) { broadcastHatLeagueMessage(new HatLeagueMessage("MR", target)); radarTarget = target; } public final String hlGetBulletTarget() { return bulletTarget; } public final void hlSetBulletTarget(String target) { broadcastHatLeagueMessage(new HatLeagueMessage("MB", target)); bulletTarget = target; } public final void hlSuggestRadarTarget(String target) { broadcastHatLeagueMessage(new HatLeagueMessage("TR", target)); } public final void hlSuggestBulletTarget(String target) { broadcastHatLeagueMessage(new HatLeagueMessage("TB", target)); } public final void hlBroadcastCustomMessage(String message) { broadcastHatLeagueMessage(new HatLeagueMessage("NA", message)); } private void broadcastHatLeagueMessage(HatLeagueMessage message) { StringBuilder buffer = new StringBuilder(); buffer.append(message.getName()); for (String value : message.getValues()) { buffer.append(","); buffer.append(value); } try { broadcastMessage(buffer.toString()); } catch (IOException ex) { throw new RuntimeException(ex); } } private HatLeagueMessage[] decodeHatLeagueMessages(String receivedBroadcast) { String[] messages = receivedBroadcast.split("\n"); List<HatLeagueMessage> decodedMessages = new ArrayList<HatLeagueMessage>(); for (String message : messages) { if (!message.equals("")) { String[] messageParts = message.split(","); // Dang it! Why did they have to wait until Java 1.6 to // introduce Arrays.copyOfRange()? String[] valueParts = new String[messageParts.length - 1]; for (int i = 1; i < messageParts.length; i++) { valueParts[i - 1] = messageParts[i]; } decodedMessages.add(new HatLeagueMessage(messageParts[0], valueParts)); } } return decodedMessages.toArray(new HatLeagueMessage[decodedMessages.size()]); } // CLASS INTERNALS START HERE. // private static class TickCondition extends Condition { public TickCondition(String name, int priority) { super(name, priority); } public boolean test() { return true; } } private static final String TICK_BEGIN_NAME = "onTickBegin"; private static final String TICK_END_NAME = "onTickStart"; private Map<String, Double> enemyEnergies = new HashMap<String, Double>(); @Override public final void run() { addCustomEvent(new TickCondition(TICK_BEGIN_NAME, 99)); addCustomEvent(new TickCondition(TICK_END_NAME, 1)); hlRun(); } @Override public final void onCustomEvent(CustomEvent e) { String name = e.getCondition().getName(); if (name.equals(TICK_BEGIN_NAME)) { broadcastHatLeagueMessage(new HatLeagueMessage("PD", Long.toString(getTime()), Double.toString(getX()), Double.toString(getY()), Double.toString(getHeadingRadians()), Double.toString(getVelocity()), Double.toString(getEnergy()))); hlOnTickBegin(); } else if (name.equals(TICK_END_NAME)) { hlOnTickEnd(); } else { hlOnCustomEvent(e); } } @Override public final void onScannedRobot(ScannedRobotEvent e) { String name = e.getName(); if (!isTeammate(name)) { long time = getTime(); double distance = e.getDistance(); Point2D.Double location = project( new Point2D.Double(getX(), getY()), e.getBearingRadians(), distance); double heading = e.getHeadingRadians(); double velocity = e.getVelocity(); double energy = e.getEnergy(); HatLeagueMessage scannedMessage = new HatLeagueMessage("ES", Long.toString(time), Double.toString(location.getX()), Double.toString(location.getY()), Double.toString(heading), Double.toString(velocity), Double.toString(energy), name); passHatLeagueMessage(scannedMessage); broadcastHatLeagueMessage(scannedMessage); if (enemyEnergies.containsKey(name)) { double lastEnergy = enemyEnergies.get(name); double difference = lastEnergy - energy; if (difference > 0.09 && difference < 3.01) { HatLeagueMessage waveMessage = new HatLeagueMessage("EW", Long.toString(time), Double.toString(location.getX()), Double.toString(location.getY()), Double.toString(getBulletSpeed(difference))); passHatLeagueMessage(waveMessage); broadcastHatLeagueMessage(waveMessage); } } enemyEnergies.put(name, energy); } } @Override public final void onMessageReceived(MessageEvent e) { HatLeagueMessage[] messages = decodeHatLeagueMessages(e.getMessage().toString()); for (HatLeagueMessage message : messages) { passHatLeagueMessage(message); } } private void passHatLeagueMessage(HatLeagueMessage message) { String messageType = message.getName(); String[] messageValues = message.getValues(); if (messageType.equals("PD")) { hlOnPartnerDataReceived(Long.parseLong(messageValues[0]), new Point2D.Double(Double.parseDouble(messageValues[1]), Double.parseDouble(messageValues[2])), Double.parseDouble(messageValues[3]), Double.parseDouble(messageValues[4]), Double.parseDouble(messageValues[5])); } else if (messageType.equals("ES")) { hlOnEnemyDataReceived(Long.parseLong(messageValues[0]), new Point2D.Double(Double.parseDouble(messageValues[1]), Double.parseDouble(messageValues[2])), Double.parseDouble(messageValues[3]), Double.parseDouble(messageValues[4]), Double.parseDouble(messageValues[5]), messageValues[6]); } else if (messageType.equals("BO")) { hlOnPartnerBulletOriginReceived( Long.parseLong(messageValues[0]), new Point2D.Double(Double.parseDouble(messageValues[1]), Double.parseDouble(messageValues[2])), Double.parseDouble(messageValues[3]), getBulletPowerFromSpeed(Double.parseDouble(messageValues[4]))); } else if (messageType.equals("EW")) { hlOnEnemyWaveOriginReceived( Long.parseLong(messageValues[0]), new Point2D.Double(Double.parseDouble(messageValues[1]), Double.parseDouble(messageValues[2])), getBulletPowerFromSpeed(Double.parseDouble(messageValues[3]))); } else if (messageType.equals("MR")) { hlOnPartnerRadarTargetReceived(messageValues[0]); } else if (messageType.equals("MB")) { hlOnPartnerBulletTargetReceived(messageValues[0]); } else if (messageType.equals("TR")) { hlOnSuggestedRadarTargetReceived(messageValues[0]); } else if (messageType.equals("TB")) { hlOnSuggestedBulletTargetReceived(messageValues[0]); } else if (messageType.equals("NA")) { // This is so that we correctly parse custom messages containing // commas. StringBuilder joinedMessage = new StringBuilder(); for (int i = 0; i < messageValues.length; i++) { joinedMessage.append(messageValues[i]); if (i < messageValues.length - 1) { joinedMessage.append(","); } } hlOnCustomMessageReceived(joinedMessage.toString()); } else { throw new RuntimeException("Unrecognized message type: " + messageType); } } private static double getBulletPowerFromSpeed(double bulletSpeed) { return (20.0 - bulletSpeed) / 3; } private static Point2D.Double project(Point2D.Double sourceLocation, double angle, double distance) { return new Point2D.Double( sourceLocation.x + Math.sin(angle) * distance, sourceLocation.y + Math.cos(angle) * distance); } }
Example robot
package wiki.hat; import java.awt.Color; import java.awt.geom.*; public class HatLeagueTestRobot extends HatLeagueRobot { @Override public void hlRun() { setColors(Color.RED, Color.ORANGE, Color.RED); setTurnRadarRightRadians(Double.POSITIVE_INFINITY); } @Override public void hlOnPartnerBulletOriginReceived(long time, Point2D.Double location, double heading, double velocity) { out.println(time + " - partner fired bullet: location " + location + ", heading " + heading + ", velocity " + velocity); } @Override public void hlOnTickEnd() { hlSetFireBullet(0.5); } }