Difference between revisions of "User:AaronR/HatLeagueRobot"
Jump to navigation
Jump to search
(Publishing some source code for the Hat League) |
(Version 0.2 is up) |
||
Line 2: | Line 2: | ||
== Version history == | == 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.'' | * '''0.1''' ''The "This should save some time" release.'' | ||
** '''Release: 2008-01-14''' | ** '''Release: 2008-01-14''' | ||
Line 8: | Line 15: | ||
== What's next == | == What's next == | ||
* Debugging, probably. | * Debugging, probably. | ||
+ | * Make it sort out duplicate enemy-scanned and enemy-fired-wave events registered by both robots. | ||
* Better enemy wave detection. | * Better enemy wave detection. | ||
Line 24: | Line 32: | ||
import java.awt.geom.Point2D; | import java.awt.geom.Point2D; | ||
import java.io.IOException; | import java.io.IOException; | ||
− | import java.util. | + | import java.util.*; |
− | |||
import robocode.*; | import robocode.*; | ||
Line 79: | Line 86: | ||
public void hlOnPartnerBulletOriginReceived(long time, | public void hlOnPartnerBulletOriginReceived(long time, | ||
− | Point2D.Double location, double heading, double | + | Point2D.Double location, double heading, double power) { |
} | } | ||
Line 87: | Line 94: | ||
*/ | */ | ||
public void hlOnEnemyWaveOriginReceived(long time, Point2D.Double location, | public void hlOnEnemyWaveOriginReceived(long time, Point2D.Double location, | ||
− | double | + | double power) { |
} | } | ||
Line 133: | Line 140: | ||
return null; | return null; | ||
} | } | ||
− | + | ||
double realBulletPower = min(getEnergy(), min(max(bulletPower, | double realBulletPower = min(getEnergy(), min(max(bulletPower, | ||
Rules.MIN_BULLET_POWER), Rules.MAX_BULLET_POWER)); | Rules.MIN_BULLET_POWER), Rules.MAX_BULLET_POWER)); | ||
Line 144: | Line 151: | ||
return setFireBullet(realBulletPower); | return setFireBullet(realBulletPower); | ||
} | } | ||
+ | |||
+ | private String radarTarget = null; | ||
+ | private String bulletTarget = null; | ||
+ | public final String hlGetRadarTarget() { | ||
+ | return radarTarget; | ||
+ | } | ||
+ | |||
public final void hlSetRadarTarget(String target) { | public final void hlSetRadarTarget(String target) { | ||
broadcastHatLeagueMessage(new HatLeagueMessage("MR", target)); | broadcastHatLeagueMessage(new HatLeagueMessage("MR", target)); | ||
+ | radarTarget = target; | ||
+ | } | ||
+ | |||
+ | public final String hlGetBulletTarget() { | ||
+ | return bulletTarget; | ||
} | } | ||
public final void hlSetBulletTarget(String target) { | public final void hlSetBulletTarget(String target) { | ||
broadcastHatLeagueMessage(new HatLeagueMessage("MB", target)); | broadcastHatLeagueMessage(new HatLeagueMessage("MB", target)); | ||
+ | bulletTarget = target; | ||
} | } | ||
Line 180: | Line 200: | ||
} | } | ||
− | private HatLeagueMessage | + | 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 new HatLeagueMessage( | + | return decodedMessages.toArray(new HatLeagueMessage[decodedMessages.size()]); |
} | } | ||
Line 237: | Line 266: | ||
public final void onScannedRobot(ScannedRobotEvent e) { | public final void onScannedRobot(ScannedRobotEvent e) { | ||
String name = e.getName(); | String name = e.getName(); | ||
− | long time = getTime(); | + | 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 | @Override | ||
public final void onMessageReceived(MessageEvent e) { | public final void onMessageReceived(MessageEvent e) { | ||
− | + | HatLeagueMessage[] messages = decodeHatLeagueMessages(e.getMessage().toString()); | |
+ | for (HatLeagueMessage message : messages) { | ||
+ | passHatLeagueMessage(message); | ||
+ | } | ||
} | } | ||
Line 291: | Line 327: | ||
Double.parseDouble(messageValues[5]), messageValues[6]); | Double.parseDouble(messageValues[5]), messageValues[6]); | ||
} else if (messageType.equals("BO")) { | } else if (messageType.equals("BO")) { | ||
− | hlOnPartnerBulletOriginReceived(Long.parseLong(messageValues[0]), | + | hlOnPartnerBulletOriginReceived( |
+ | Long.parseLong(messageValues[0]), | ||
new Point2D.Double(Double.parseDouble(messageValues[1]), | new Point2D.Double(Double.parseDouble(messageValues[1]), | ||
Double.parseDouble(messageValues[2])), | Double.parseDouble(messageValues[2])), | ||
Double.parseDouble(messageValues[3]), | Double.parseDouble(messageValues[3]), | ||
− | Double.parseDouble(messageValues[4])); | + | getBulletPowerFromSpeed(Double.parseDouble(messageValues[4]))); |
} else if (messageType.equals("EW")) { | } else if (messageType.equals("EW")) { | ||
− | hlOnEnemyWaveOriginReceived(Long.parseLong(messageValues[0]), | + | hlOnEnemyWaveOriginReceived( |
+ | Long.parseLong(messageValues[0]), | ||
new Point2D.Double(Double.parseDouble(messageValues[1]), | new Point2D.Double(Double.parseDouble(messageValues[1]), | ||
Double.parseDouble(messageValues[2])), | Double.parseDouble(messageValues[2])), | ||
− | Double.parseDouble(messageValues[3])); | + | getBulletPowerFromSpeed(Double.parseDouble(messageValues[3]))); |
} else if (messageType.equals("MR")) { | } else if (messageType.equals("MR")) { | ||
hlOnPartnerRadarTargetReceived(messageValues[0]); | hlOnPartnerRadarTargetReceived(messageValues[0]); | ||
Line 325: | Line 363: | ||
+ messageType); | + messageType); | ||
} | } | ||
+ | } | ||
+ | |||
+ | private static double getBulletPowerFromSpeed(double bulletSpeed) { | ||
+ | return (20.0 - bulletSpeed) / 3; | ||
} | } | ||
Revision as of 02:39, 16 January 2008
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); } }