User:AaronR/HatLeagueRobot

From Robowiki
< User:AaronR
Revision as of 03:15, 15 January 2008 by AaronR (talk | contribs) (Publishing some source code for the Hat League)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Yet another base class for robots participating in the Hat League.

Version history

  • 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.
  • Better enemy wave detection.

Source code

Usage rules

  1. If a method begins with "hl", use it instead of the normal Robocode method.
  2. 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.HashMap;
import java.util.Map;

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 velocity) {
	}

	/**
	 * 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 velocity) {
	}

	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);
	}

	public final void hlSetRadarTarget(String target) {
		broadcastHatLeagueMessage(new HatLeagueMessage("MR", target));
	}

	public final void hlSetBulletTarget(String target) {
		broadcastHatLeagueMessage(new HatLeagueMessage("MB", 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 decodeHatLeagueMessage(String message) {
		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];
		}

		return new HatLeagueMessage(messageParts[0], valueParts);
	}

	// 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();
		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) {
		passHatLeagueMessage(decodeHatLeagueMessage(e.getMessage().toString()));
	}

	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]),
					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])),
					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 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);
	}
}