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 AbstractXanderRobot that extends AdvancedRobot. All basic framework classes are in the package xander.core. Some more advanced guess factor and wave surfing classes are in package xander.gfws (I haven't decided whether or not I consider this part of the framework or not). When looking at any of my xandercat robots, anything outside of the xander.core and xander.gfws packages are components built for the Xander framework, not part of the Xander framework itself.
Contents
Xander Framework 2.0
The Xander framework is a framework on which Robocode robots can be built. This section details the core framework. Recently, the framework has undergone a fairly major update, and is thus now deemed as version 2.0.
Major changes from the original framework include:
- The drive and gun tree concept with compound drives and guns serving as tree nodes has been replaced by a component chain. The component chain inspired by the Chain of Responsibility design pattern. For each tick, the chain is followed until the gun, drive, and radar have all been selected. One improvement this provides is it cleans up the handling of pair situations where -- for example -- a drive and gun should be used together or not at all.
- There is no longer a strong distinction between static and non-static components. All components are now static. This helps simplify things.
- Event listeners are now kept through all rounds of a battle. This is a somewhat fundamental change that means you now add a listener once (usually in a constructor), rather than repeatedly at the beginning of each round.
- A new WaveHistory class now takes the responsibility of handling all waves for both self and opponent. This cleans up wave handling a bit and also provides some new functionality such as calculating Bullet Shadows.
Base Robot Class
Xander robots extend the AbstractXanderRobot class. The AbstractXanderRobot class contains basic robot setup code and a basic processing loop.
The basic setup code performs as follows:
- set all setXxxForRobotXxx values to true.
- if first round:
- initialize Resources
- call configure(Configuration)
- apply various configuration choices
- issue round begin event
The basic processing loop performs as follows:
- issue turn begin event
- Process component chain to select radar, drive, and gun
- Query radar for target
- If no target is locked...
- Tell drive to drive without a target
- Otherwise...
- Tell drive to drive with given target
- If target is disabled and configuration allows...
- Fire on target with Head-On gun
- Otherwise...
- Fire on target with gun
- issue turn end event
- call execute()
A Xander robot must implement the following methods:
- addComponents() - for setting up component chain
In addition, a Xander robot may optionally override the following methods:
- void configure(Configuration) - allows basic configuration of the robot to be changed
- void style(RobotStyle) - allows setting styling options like robot colors
Components / Component Chain
In a Xander robot, guns, drives, and radars are referred to as components. Components are managed within a component chain. The component chain follows the general concepts of the Chain of Responsibility design pattern. It consists of a series of ComponentScenario objects that are responsible for selecting a radar, drive, and gun to use for each turn. The chain is followed from the beginning to the end, each ComponentScenario passing off to the next, until a radar, drive, and gun have all been selected for the current turn.
The component chain is set up at the beginning of the first round by a call to the abstract method
addComponents()
. Each link in the chain has a Scenario that is responsible for deciding whether or not the components for that link should be used or not. This design makes it easy to handle situations where more than one component should be used in combination. All specialized scenarios should be set up by calls to
addComponents(...)
, while main or default components to be used when no specialized scenario applies should be set up by a call to
addDefaultComponents(...)
.
Example:
Assume you have a mainRadar, mainDrive and mainGun for a robot. Assume you also have a ramDrive and ramGun that should be used together against robots who use a ramming strategy. For this, a Scenario ramScenario could be written to test if a robot is using a ramming strategy. The component chain would then be set up as follows:
addComponents(ramScenario, ramDrive, ramGun); addDefaultComponents(mainRadar, mainDrive, mainGun);
Resources
Classes within a Xander robot have easy access to a number of resources which can be accessed through the Resources class.
DriveStats
Statistics related to the drives.
GunStats
Statistics related to the guns, including hit ratios and virtual gun hit ratios.
RobotEvents
Manages all major event listeners, including all of the repackaged AdvancedRobot events.
RobotProxy
Access to all robot getXxx methods outside of the main robot class itself should be accessed via the RobotProxy object.
SnapshotHistory
Robot history is available through the SnapshotHistory. Robot history for a particular point in time is stored in a snapshot via the Snapshot class, an immutable snapshot of a robot at a particular point in time.
Each turn, a Xander robot takes a new snapshot for itself and stores it in the history. Each scan, a new snapshot for the scanned robot is generated and stored in the history. Robot history allows the robot to look back at snapshots of itself and it's opponents back a set number of turns and scans.
WaveHistory
History of all bullet waves (for both self and opponent).
Controllers
Radars, guns, and drives control the robot through controller classes. The controllers for radar, gun, and drive provide access to the radar, gun, and drive related setXxx methods of the robot. This approach helps to prevent components from doing operations that are outside their area of responsibility, and also prevents components from calling the basic Robot control methods, or any methods that would immediately cause a turn to end. Controllers may also have other helper methods.
Radar
Radars must implement the Radar interface. In a Xander robot, the radar is responsible for operating the radar and selecting a Snapshot of a robot for the drive and gun to focus on.
Drive
Drives must implement the Drive interface.
Gun
Guns must implement the Gun interface. However, rather than creating a gun class and directly implementing the gun interface, the recommended approach for a Xander robot is to use the XanderGun class. The XanderGun class relies on two other interfaces: Targeter and PowerSelector. A PowerSelector decides on what fire power to use, while the Targeter determines how to aim the gun based on what the bullet velocity will be. When using a XanderGun, you create classes that implement Targeter and PowerSelector, and the XanderGun takes care of the rest, including:
- Preparing information for self and target. This takes the latest snapshots and predicts them one turn into the future (necessary for a more accurate aim due to how the Robocode processing loop operates), and passes those to the Targeter for aiming.
- Calling the setXxx methods to aim the gun and fire bullets.
- Auto-adjusting the fire power for low energy situations (so long as the PowerSelector allows it). Fire power is modified based on the current state of the target and self. Power selection steps are as follows:
- Get preferred fire power from the PowerSelector.
- If power is more than is required to kill the target, reduce firepower to what is needed for a kill.
- If target is not disabled and firing bullet would disable self, reduce firepower such that it will not disable self.
Gun Array
In cases were the robot should choose between a variety of guns based on some criteria, a gun array can be set up using the GunArray class. A gun array must have a gun selector, defined by the GunSelector interface. A gun array can be set up to fire virtual bullets.
Historical Note: The GunArray operates much like the CompoundGun of the Xander 1.0 framework. However, it is no longer intended for the construction of trees (though technically it is still possible to create one). For the Xander 2.0 framework, most component selections are expected to be handled by the component chain.
Events
Robocode issues a number of different events to the robot. A Xander framework robot repackages these events and also provides other custom events, and makes those available to other components. Xander robots can make use of the following events:
Listener Class | Events Issued By (Register With) | Purpose |
---|---|---|
BulletHitListener | RobotEvents | Repackages Robocode events onHitByBullet, onBulletHit, onBulletMissed, and onBulletHitBullet |
CollisionListener | RobotEvents | Repackages Robocode events onHitRobot and onHitWall |
MyWaveListener | WaveHistory | Handles events related to the robot's own bullet waves. Actual bullets only. |
MyVirtualWaveListener | WaveHistory | Handles events related to the robot's own virtual bullet waves. Virtual gun bullets only. |
OpponentWaveListener | WaveHistory | Handles events related to the opponents bullet waves. |
Painter | RobotEvents | Repackages Robocode event onPaint, allowing other components to paint on the battlefield |
RoundBeginListener | RobotEvents | Handler for a single event that fires at the beginning of every round. Can be used to initialize variables when a round begins. |
RoundListener | RobotEvents | Repackages Robocode events onRoundEnd and onBattleEnd |
ScannedRobotListener | RobotEvents | Repackages Robocode event onScannedRobot |
SurvivalListener | RobotEvents | Repackages Robocode events onWin, onDeath, and onRobotDeath |
TurnListener | RobotEvents | Handler for events that fire on every iteration of the AbstractXanderRobot main processing loop. One event that fires at the beginning of a turn, and one event that fires at the end of a turn immediately before execute() is called. |
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.
Mathematics
RCMath
This class provides generic math helper functions. Most are targeted at solving Robocode problems, but are still usable in a more generic sense.
RCPhysics
This class provides constants and methods that are only useful within the context of Robocode physics. Some of what it provides is obsolete due to various additions to the Robocode API over the last few years -- specifically the Robocode Rules and Utils classes -- but I leave it there for posterity.
CPU Utilization
The Xander framework also provides a few ways to track processing time.
RunTimeLogger
Provides an easy-to-use means for testing average and peak run times for any particular block of code. Results are logged to the console at the end of each round.
CPU Utilization Painting
CPU utilization can be graphed on screen by setting the drawCPUUtilization flag to true in the Configuration.
Guess Factors and Wave Surfing
At the fringe of the framework are a variety of classes for handling guess factors and wave surfing. These classes are in package xander.gfws.
Segmentation
Wave surfing drives and guess factor guns can use segmenters. Basic segmenters must implement the Segmenter interface. There is also an AbstractSegmenter class for simple segmenters that operate on values within a fixed range.
Current available segmenters include:
- AttackerBearingSegmenter - segments on the attacker's bearing from the back-as-front heading of the defender
- BulletTravelTimeSegmenter - segments on bullet travel time
- DefenderAccelerationSegmenter - segments on defender acceleration
- DefenderSpeedSegmenter - segments on defender speed
- NullSegmenter - provides no segmentation (a segmenter with only a single segment).
- WallSmoothingSegmenter - segments on whether it appears defender will have to wall smooth clockwise, counter-clockwise, both directions (in corner), or not at all.
Components Built on Xander
The components listed in this section are not considered part of the Xander framework core; they are example components built for the Xander framework that I use in my various robots. Exceptions to this are the HeadOnTargeter, as it is used by a Xander framework core class and must therefore be considered part of the framework.
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 and Targeters
AntiMirrorGun
Specifically for targeting opponents who use a mirroring drive strategy. Must be used in combination with the AntiMirrorDrive.
BSProtectedGun
A wrapper for other guns that provides support for counteracting bullet shielding.
CircularTargeter
This targeter 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. One advantage this gun has over some other circular targeting implementations is that it works for circular paths that are centered anywhere, and is not limited to circular paths centered at the location bullet wave was fired from. This gun pulverizes SpinBot.
HeadOnTargeter
Head-on shots.
LinearTargeter
This targeter 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, but does compensate for battlefield bounds (don't shoot off the grid). The linear intercept calculation is precise (not iterative nor an approximation).
Xander Drives
AntiMirrorDrive
Drive specifically for use with opponents who use a mirroring drive strategy. This drive must be used in combination with the AntiMirrorGun.
BasicSurferDrive
This drive is an adaptation of the Wave Surfing drive used in the BasicGFSurfer robot.
DirectWaveSurfingDrive
Wave surfing drive.
IdealPositionDrive
Attempts to ideally position the robot on the battlefield. This should only be used when no enemy bullets are in play.
RamEscapeDrive
For getting away from needy robots who always want a hug.
Xander Code Examples
TO BE COMPLETED
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 as static to preserve them between rounds is considered contrary to this. Therefore, the Xander framework hides this necessity by storing most objects as static behind the scenes, rather than relying on overuse of static variables.