XanderFramework
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.
Below is a list of pluggable components I've built so far. I have more planned.
Contents
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
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 agaist published robots who are likely mostly using guess factor guns).
AvoidanceDrive
This drive relies 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.
Example Xander Robot
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 causes 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 is 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.
The Xander framework solves this problem through a static resource manager. The static resource manager acts in a manner similar to a singleton, except there can be one instance for every robot. These instances are stored in a static Map. Whenever a variable or object needs to be kept or retrieved from round to round, the static resource manager for the robot is looked up by robot name, 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(robotName); srm.register(myResource);
Looking Up a Resource
StaticResourceManager srm = StaticResourceManager.getInstance(robotName); 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(robotName); srm.store("myKey",myProperty);
Looking Up a Property
StaticResourceManager srm = StaticResourceManager.getInstance(robotName); MyProperty myProperty = srm.getProperty("myKey");
Class Sharing Management
One of the problems with using a "framework" in Robocode is in how to manage versions of the framework. Usually this isn't too much of a problem, as Robocode convention is to put all of your classes into your own package. While in a general sense, this would be considered bad, as it forces you to refactor the framework into your own package structure, for Robocode it has the advantage of ensuring no problems between different users.
However, what happens when a user has multiple robots of his own making? This will probably work fine, if they are all packaged at the same time, or at least packaged with the same framework version. But it is easy to conceive a situation that might cause problems. For example, consider the following sequence of events:
- User creates robot 1 with framework.
- User packages robot 1.
- User updates framework.
- User creates robot 2 with updated framework.
- User packages robot 2.
Now what happens when you try to run robot 1 and robot 2 at the same time? While I do not know exactly how Robocode loads and handles classes, there is a fair chance it will cause problems, as both robots are using different versions of the same framework classes in the same framework packages.
I will be investigating this problem and developing a solution for it in the near future.