Difference between revisions of "XanderFramework"

From Robowiki
Jump to navigation Jump to search
m (→‎Handling Static Data: Updated code examples)
(→‎Xander Drives: Added some details on StatDrive)
Line 34: Line 34:
 
=== OrbitalDrive ===
 
=== OrbitalDrive ===
 
This drive approaches the target using linear targeting and then, when it range, orbits around the target, changing direction periodically.  It also uses "inverse gravity bullet avoidance"; this is currently somewhat experimental, and at present assumes the opponent is using either head-on or linear targeting (probably not a good idea against published robots who are likely mostly using guess factor guns).
 
This drive approaches the target using linear targeting and then, when it range, orbits around the target, changing direction periodically.  It also uses "inverse gravity bullet avoidance"; this is currently somewhat experimental, and at present assumes the opponent is using either head-on or linear targeting (probably not a good idea against published robots who are likely mostly using guess factor guns).
 +
 +
=== StatDrive ===
 +
This drive is a Wave Surfing drive of my own design.  Similar to the StatGun, it supports using a segmenter, and a compound drive segmenter is available to combine the effects of multiple drive segmenters.
 +
 +
==== DriveSegmenter ====
 +
DriveSegmenter is the interface that must be implemented to perform segmentation for the StatDrive.  Available drive segmenters include:
 +
* NullDriveSegmenter - provides the behavior of there being no segmenter at all by providing only a single segment
 +
* DistanceDriveSegmenter - segments based on distance from opponent
 +
* RelativeDirectionDriveSegmenter - segments based on drive direction relative to opponent
 +
* CompoundDriveSegmenter - combines the effects of two or more other drive segmenters
  
 
=== BasicSurferDrive ===
 
=== BasicSurferDrive ===

Revision as of 19:18, 3 June 2011

The Xander framework is an AdvancedRobot framework that makes it easy to build new bots by combining different guns, radars, and drives. It is based on an abstract class named AbstractXanderBot that extends AdvancedRobot. For a gun, drive, or radar to be used in the Xander framework, it has to implement the corresponding interface Gun, Drive, or Radar.

Xander Components

Xander Radars

BasicRadar

Basic radar scan the field, and locks onto the first target it finds. Target can switch if another robot happens to be closer and drives through the scan beam.

Xander Guns

LinearGun

This gun is for targeting opponents that drive in a straight line. It fires on the opponent based on the opponents current speed and heading. It does not attempt to determine whether the target is actually moving in a straight line or not.

CircularGun

This gun is for targeting opponents that drive in circles. If the opponent does not appear to be going in circles, this gun will not fire on the target.

StatGun

This gun is a "guess factor" gun that relies on statistics to determine how to aim. If will not fire until there are sufficient stats to make a good guess. It relies on a Segmenter to determine how to categorize the statistics it collects (Segmenter is a separate interface).

Segmenter

StatGun uses a segmenter to categorize bullet statistics. The available segmenters include:

  • BulletTravelTimeSegmenter - segments statistics by approximate bullet travel time.
  • OpponentVelocitySegmenter - segments statistics by velocity of opponent at time bullet was fired
  • RelativeDirectionSegmenter - segments statistics by the direction opponent was headed at time bullet was fired
  • CompoundSegmenter - this segmenter combines the effects of two or more other segmenters.

CompoundGun

Combines two or more other guns. Compound gun delegates gunning to other guns based on a GunSelector. By default, a CompoundGun uses the FirstAvailGunSelector.

GunSelector

Compound guns can be set up with different gun selection modules. At present, I have two available gun selection modules:

  • FirstAvailGunSelector - original behavior before gun selectors, this one just hands off to the first gun it finds that claims it can fire at the target.
  • HitRatioGunSelector - this gun selector looks at gun hit ratios and bullets fired for each gun to determine which gun to fire with.

Xander Drives

In an upcoming update of the framework, drives will be more componentized like guns, where a CompoundDrive will be able to combine the effects of multiple simpler drives using a DriveSelector.

OrbitalDrive

This drive approaches the target using linear targeting and then, when it range, orbits around the target, changing direction periodically. It also uses "inverse gravity bullet avoidance"; this is currently somewhat experimental, and at present assumes the opponent is using either head-on or linear targeting (probably not a good idea against published robots who are likely mostly using guess factor guns).

StatDrive

This drive is a Wave Surfing drive of my own design. Similar to the StatGun, it supports using a segmenter, and a compound drive segmenter is available to combine the effects of multiple drive segmenters.

DriveSegmenter

DriveSegmenter is the interface that must be implemented to perform segmentation for the StatDrive. Available drive segmenters include:

  • NullDriveSegmenter - provides the behavior of there being no segmenter at all by providing only a single segment
  • DistanceDriveSegmenter - segments based on distance from opponent
  • RelativeDirectionDriveSegmenter - segments based on drive direction relative to opponent
  • CompoundDriveSegmenter - combines the effects of two or more other drive segmenters

BasicSurferDrive

This drive is an adaptation of the Wave Surfing drive used in the BasicGFSurfer robot.

RamboDrive

This drive attempts to ram the enemy. It can be configured so that it does not approach the enemy in a straight line, but the effectiveness may still be limited against the better bots out there.

AvoidanceDrive (No longer available)

This obsolete drive relied exclusively on "inverse gravity bullet avoidance" to control movement. Not really meant for real combat, this drive was created to test various aspects of bullet avoidance. It was removed when I switched from tracking "phantom" bullets to simply tracking bullet waves (unlike the former "phantom" bullets, a wave does not predict how the bullet was aimed).

Xander Utilities

Logging

The Xander framework provides basic logging services loosely based on the Log4J logging model. Though not nearly as configurable as Log4J logging, it is much more advanced than just using System.out.print statements.

Xander logs have the following levels, in order of significance:

  • DEBUG - Debugging messages.
  • INFO - Informational messages. This is the default logging level.
  • WARNING - Warning messages.
  • STAT - Statistics messages. These are meant to be messages that are useful during development, but also of interest for a finished robot.
  • ERROR - Error messages.

Log levels are defined by the nested Enum named Level in the Log class (e.g. the level INFO would be accessed via Log.Level.INFO)

Logging consists of two classes: Logger and Log. Each class can have it's own Log. A Log is created by the Logger.

A Log can currently be created in a manner similar to as follows:

public class ExampleClass {

	private static final Log log = Logger.getLog(ExampleClass.class);

}

Once created, it can be used by calling the appropriate methods of the Log class as in the following example:

	log.info("This is an informational message.");

Similar to Log4J, log messages will only be printed if the level of the message is at or above the level set in the Log. By default, all Logs are at level INFO. This can be changed globally on startup through the Logger class, or on a Log by Log basis.

The main Xander robot class, AbstractXanderBot, provides a protected method getDefaultLogLevel() for setting the global logging level on startup. Robots using the Xander framework can override this method to change the default logging level for all Logs.

Xander Code Examples

Radars, Gun, and Drives

Radars, Guns, and Drives all follow a similar pattern. Each must implement an interface. The methods defined in the interfaces provide a controller as an argument. Controllers are actually wrappers for the main robot class that limit exposure of main class methods to only those methods they should be allowed to call. For example, a Gun is allowed to call methods to turn the gun, but is not allowed to call methods that would turn the radar or robot body.

As an example, below is the Gun interface. However, note that it will likely change before the Xander framework is considered complete:

/**
 * Interface to be implemented by XanderBot gun classes.
 * 
 * @author Scott Arnold
 */
public interface Gun {

	public String getName();
	
	public void initialize(GunController gunController);
	
	public boolean canFireAt(String robotName, GunController gunController);
		
	public void fireAt(String robotName, GunController gunController);
	
}

At present, Controllers are recreated at the beginning of each round. It is primarily for this reason that the initialize(...) method is provided.

Main Robot Class

As an example of how the Xander framework is used, below is the source for the main robot class of XanderCat 2.0.

/**
 * 1-on-1 robot built on the Xander framework.  Also suitable for melee.
 * 
 * @author Scott Arnold
 */
public class XanderCat extends AbstractXanderBot {	
	
	@Override
	protected Level getLogLevel() {
		return Log.Level.INFO;
	}

	@Override
	protected Radar createRadar() {
		return new BasicRadar(this, 45, 5);
	}

	@Override
	protected Drive createDrive() {
		return new OrbitalDrive(this, 200, 40, 0.5f);
	}

	@Override
	protected Gun createGun() {
		FiringProfile firingProfile = new FiringProfile(
				1.0d, 500d, 3.0d, 150d, 0.5d, 450d, 700d);
		Segmenter segmenter = new CompoundSegmenter(
				new BulletTravelTimeSegmenter(getName(), 12, getBattlefieldBounds()),
				new RelativeDirectionSegmenter(10));
		return new CompoundGun(
				new StatGun(this, firingProfile, 1000, 10, 30, segmenter, true),
				new CircularGun(firingProfile), 
				new LinearGun(firingProfile));
	}

}

Handling Static Data

In order to remember information from one battle to the next, it is necessary to save that information in some static variable or object. However, this would seem to cause problems if you want to pit a robot against itself, or if you want to run two different robots off of the same base framework classes. This would seem to be a problem because suddenly you have two or more distinct robots sharing certain static resources, when each robot should have it's own unique set. However, Robocode uses a separate class loader for every robot, putting each robot in it's own isolated sandbox. This means static variables are not shared among robots.

The Xander framework attempts to adhere to good object oriented practices; declaring variables or objects as static to preserve them between rounds is considered contrary to this. Therefore, the Xander framework provides a static resource manager. The static resource manager is a singleton that stores objects in a static Map. Whenever a variable or object needs to be kept or retrieved from round to round, the static resource manager instance is obtained, and then the variable or object is stored or retrieved from it.

The static resource manager supports storing two different kinds of objects, which are referred to as resources and properties:

  • A resource is something like a radar, drive, or gun, that there will only be a single instance of. Resources must implement the StaticResource interface. Resources are retrieved by their class.
  • A property is meant to be a simple value such as an Integer or a String. Properties are stored and retrieved by key values. A key value must be a String.

Resources

Resources must implement the StaticResource interface. The StaticResource interface serves two purposes. First, the Xander framework detects any radar, drive, or gun that implements StaticResource, and will automatically register that component with the static resource manager. This means simply by implementing the interface, you are assured that your entire component will be treated in a static manner. Second, it requires the object to implement an initializeForNewRound(...) method, in order for that object to do any between round cleanup. Cleanup could potentially be done elsewhere, but requiring a seperate method in the interface serves as a good reminder to anyone implementing StaticResource that they need to think about any state that should be cleaned up between rounds.

Generally speaking, you shouldn't need to register or lookup resources, as only radars, drives, and guns are meant to be resources, and they are automatically handled by the Xander framework.

Registering a Resource

StaticResourceManager srm = StaticResourceManager.getInstance();
srm.register(myResource);

Looking Up a Resource

StaticResourceManager srm = StaticResourceManager.getInstance();
MyResource myResource = srm.getResource(MyResource.class);

Properties

Properties are more free-form. They are intended to be simple values, like Strings, but the static resource manager does not place any hard restrictions on what is stored. Properties must be stored and retrieved by a String key value. The key value can be any unique String the programmer chooses.

Storing a Property

StaticResourceManager srm = StaticResourceManager.getInstance();
srm.store("myKey",myProperty);

Looking Up a Property

StaticResourceManager srm = StaticResourceManager.getInstance();
MyProperty myProperty = srm.getProperty("myKey");