Difference between revisions of "XanderFramework"

From Robowiki
Jump to navigation Jump to search
(added details on Logging)
(→‎Events: updated notes for framework v3.0)
 
(61 intermediate revisions by the same user not shown)
Line 1: Line 1:
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 Framework =
  
Below is a list of pluggable components I've built so far.  I have more planned.
+
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.  As an optional add-on to the framework, some more advanced yet less mature guess factor and wave surfing classes are in package xander.gfws.  When looking at any of my Xander 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.
  
= Xander Components =
+
With a Xander robot, the main robot class need only set up a ''component chain'' populated with your guns, radars, and drives.  It can also optionally override methods to change basic configuration options and write battle statistics to disk.
== Xander Radars ==
+
 
=== BasicRadar ===
+
Guns, radars, and drives must use the corresponding framework interfaces and can make use of a variety of framework resources and events.
Basic radar scan the field, and locks onto the first target it findsTarget can switch if another robot happens to be closer and drives through the scan beam.
+
 
 +
The current framework version available for the last few years is considered v2.0.  Version 3.0 is current in development.  Version 3.0 brings several advancements over the older version through a revised component structure, including:
 +
*  Full melee support.
 +
*  Simplified components, but without losing any prior functionality.
 +
*  Reduced code size.
 +
*  Improved performance.
 +
 
 +
== Version Notes ==
 +
 
 +
Some interesting notes about each version of the framework.
 +
 
 +
=== Version 1.0 ===
 +
 
 +
Version 1.0 had the basics complete, but lacked some of the more advanced functionality like tracking wave history.
 +
 
 +
=== Version 2.0 ===
 +
 
 +
Version 2.0 added more advanced functionality like wave history and has been the main version from about 2013 up through 2017.  Version 2.0 was tailored primarily for 1 vs 1 combat, and had certain deficiencies when attempting to use it for other purposes.  It was structured to have 1 of each framework component, and only some of them were written to accommodate more than 1 opponent on the battlefield.
 +
 
 +
=== Version 3.0 ===
 +
 
 +
Version 3.0 is under development in 2017.  It's purpose is to add full melee or multi-opponent support, simplify some of the code, reduce code size, and improve performance in a few areas.  Version 3.0 takes on a new component strategy where some components that can apply to a single robot now only apply to a single robot.  This means some main framework components will now have 1 instance for each known robot, instead of a single instance that must handle all robots.  One of the challenges with this approach is that robot specific opponents are now generated as knowledge of their existence is gained, instead of existing from the very beginning of the battles.  This is a challenge because you can no longer always fully construct your robot at the very beginning, because some parts must be created or expanded as new robots are scanned.  The framework is being rewritten to support this in as convenient a fashion as possible.
 +
 
 +
The main framework components that will now have 1 for every robot (including self) include the SnapshotHistory, WaveHistory, and GunStats components.
 +
 
 +
Any listeners for robot specific components (anything that implements WaveListener, ScannedRobotListener, etc) must now implement an additional method getOpponentFocus() that tells the framework what robots to associate the component with.  Focus options include:
 +
* ALL_ROBOTS:  Component will receive events for all robots.
 +
* TARGETED_ROBOT:  Component will only receive events from the currently targeted robot (the targeted robot determined by what your radar is returning).
 +
* SPECIFIC_ROBOT_SCANNED:  Component will only receive events for one specific robot.  That robot will be the last scanned robot at time listener is added, or the next scanned robot if there is no last scanned robot.
 +
* SPECIFIC_ROBOT_TARGETED:  Component will only receive events for one specific robot.  That robot will be the last targeted robot at time listener is added, or the next targeted robot if there is no current target.
 +
 
 +
== Base Robot Class ==
 +
Xander robots extend the '''AbstractXanderRobot''' class, which in turn extends the Robocode '''AdvancedRobot''' 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:
 +
**  call configure(Configuration)
 +
**  call style(RobotStyle)
 +
**  initialize Resources
 +
**  call addComponents(ComponentChain)
 +
*  apply robot styling (done every round in case win code changes styling)
 +
*  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(ComponentChain) - 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
 +
*  boolean recordBattleStats(Map<String, String>) - allows robot to save battle statistics to file; key is statistic name, value is statistic value as String.  Statistics are saved in the '''BattleStats''' class which is written to file as a Java object using an ObjectOutputStream.
 +
 
 +
== Components / Component Chain ==
 +
In a Xander robot, guns, drives, and radars are referred to as ''components''.  Components are managed within a ''component chain'' represented by the '''ComponentChain''' class.  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 <pre>addComponents(ComponentChain)</pre>.  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 <pre>addComponents(...)</pre>, while main or default components to be used when no specialized scenario applies should be set up by a call to <pre>addDefaultComponents(...)</pre>.
 +
 
 +
'''Example:'''
 +
 
 +
Assume you have a mainRadar, mainDrive and mainGun for a robotAssume 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:
 +
<pre>
 +
protected void addComponents(ComponentChain chain) {
 +
...
 +
chain.addComponents(ramScenario, ramDrive, ramGun);
 +
chain.addDefaultComponents(mainRadar, mainDrive, mainGun);
 +
}
 +
</pre>
 +
 
 +
== 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 of your robot, such as drive time usage.
 +
 
 +
=== GunStats ===
 +
Statistics related to the guns, including hit ratios and virtual gun hit ratios.  As of v3.0, one GunStats will be created for each robot in the battle.
 +
 
 +
=== 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.  As of v3.0, One SnapshotHistory will be created for each robot in the battle.
 +
 
 +
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.  As of v3.0, one WaveHistory is created for each robot.
 +
 
 +
== 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.
 +
 
 +
<pre>
 +
public interface Radar extends Component {
 +
 
 +
/**
 +
* Prepare next sweep of radar and return snapshot of robot currently
 +
* being focused upon.
 +
*
 +
* @param radarController  radar controller
 +
*
 +
* @return    snapshot of robot to attack
 +
*/
 +
public Snapshot search(RadarController radarController);
 +
}
 +
</pre>
 +
 
 +
== Drive ==
 +
Drives must implement the '''Drive''' interface.
 +
 
 +
<pre>
 +
public interface Drive extends Component {
 +
 +
/**
 +
* Drive into optimal firing range of the given robot.
 +
*
 +
* @param opponentSnapshot  robot to drive against
 +
* @param driveController    drive controller
 +
*/
 +
public void driveTo(Snapshot opponentSnapshot, DriveController driveController);
 +
 +
/**
 +
* Drive without any specific target.
 +
*
 +
* @param driveController    drive controller
 +
*/
 +
public void drive(DriveController driveController);
 +
}
 +
</pre>
  
== Xander Guns ==
+
=== Drive Array ===
=== 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 ===
+
In cases were the robot should choose between a variety of drives based on some common criteria, a ''drive array'' can be set up using the '''DriveArray''' classA drive array must have a ''drive selector'', defined by the '''DriveSelector''' interface.
Combines two or more other guns.  Compound gun delegates gunning to other guns based on a GunSelectorBy default, a CompoundGun uses the FirstAvailGunSelector.  
 
  
==== GunSelector ====
+
== Gun ==
Compound guns can be set up with different gun selection modulesAt present, I have two available gun selection modules:
+
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:
* '''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.
+
* 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.
* '''HitRatioGunSelector''' - this gun selector looks at gun hit ratios and bullets fired for each gun to determine which gun to fire with.
+
*  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.
  
== Xander Drives ==
+
<pre>
 +
public interface Gun extends Component {
 +
 +
/**
 +
* Aim and fire upon the given opponent.  Since guns are also required
 +
* to have the getFiringVector method, this method can generally call that
 +
* method to get the aim.  The only extra work in this method is
 +
* actually turning the gun and firing.
 +
*
 +
* @param target          snapshot of opponent
 +
* @param myself          snapshot of self
 +
* @param gunController    gun controller
 +
*
 +
* @return          whether or not a bullet was fired
 +
*/
 +
public boolean fireAt(Snapshot target, Snapshot myself, GunController gunController);
  
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.
+
/**
 +
* Returns the aim information for the given opponent. 
 +
*
 +
* @param target    the opponent to target
 +
* @param myself    snapshot of self
 +
*
 +
* @return          aim information for the given opponent
 +
*/
 +
public Aim getAim(Snapshot target, Snapshot myself);
 +
 +
/**
 +
* Returns whether or not this can is currently capable of aiming and
 +
* firing on the given robot.  This method should be lightweight (most
 +
* guns should just return true).
 +
*
 +
* @param target          snapshot of robot to fire at
 +
*
 +
* @return                whether or not gun can fire on opponent
 +
*/
 +
public boolean canFireAt(Snapshot target);
 +
}
 +
</pre>
  
=== OrbitalDrive ===
+
=== Gun Array ===
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).
 
  
=== BasicSurferDrive ===
+
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.
This drive is an adaptation of the Wave Surfing drive used in the BasicGFSurfer robot.
 
  
=== RamboDrive ===
+
Historical Note:  The GunArray operates much like the CompoundGun of the Xander 1.0 frameworkHowever, 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''.
This drive attempts to ram the enemyIt 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) ===
+
== Events ==
This obsolete drive relied exclusively on "inverse gravity bullet avoidance" to control movementNot really meant for real combat, this drive was created to test various aspects of bullet avoidanceIt 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).
+
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 accessed through the RobotEvents class (v3.0):
 +
{| border="1" cellpadding="4"
 +
! Listener Class
 +
! Opponent Focusable?
 +
! Purpose
 +
|-
 +
| BulletHitListener
 +
| Yes
 +
| Repackages Robocode events onHitByBullet, onBulletHit, onBulletMissed, and onBulletHitBullet
 +
|-
 +
| CollisionListener
 +
| No
 +
| Repackages Robocode events onHitRobot and onHitWall
 +
|-
 +
| WaveListener
 +
| Yes
 +
| Handles events related to a robot's bullet waves.  The WaveHistory stores two sets of WaveListeners, one for actual bullet waves, and one for virtual bullet wavesIf listening for both, you can tell the difference between the two by the Wave method isVirtual().
 +
|-
 +
| Painter
 +
| No
 +
| Repackages Robocode event onPaint, allowing other components to paint on the battlefield
 +
|-
 +
| RoundBeginListener
 +
| No
 +
| Handler for a single event that fires at the beginning of every roundCan be used to initialize variables when a round begins.
 +
|-
 +
| RoundListener
 +
| No
 +
| Repackages Robocode events onRoundEnd and onBattleEnd
 +
|-
 +
| ScannedRobotListener
 +
| Yes
 +
| Repackages Robocode event onScannedRobot
 +
|-
 +
| SurvivalListener
 +
| Yes
 +
| Repackages Robocode events onWin, onDeath, and onRobotDeath
 +
|-
 +
| TurnListener
 +
| No
 +
| 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 =
 
= Xander Utilities =
Line 61: Line 274:
 
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)
 
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.  Unfortunately, in order to support ''robot duplication'', Logs cannot be declared static, and the robot name must be provided as an argument when the Log is created.  This means a class may have to be passed the robot name as an argument solely for the purpose of creating a Log, which is undesirable, but also unavoidable.
+
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 should be created in a manner similar to as follows:
+
A Log can currently be created in a manner similar to as follows:
 
<pre>
 
<pre>
 
public class ExampleClass {
 
public class ExampleClass {
  
private Log log;
+
private static final Log log = Logger.getLog(ExampleClass.class);
  
public ExampleClass(String robotName) {
 
this.log = Logger.getLog(robotName, ExampleClass.class);
 
}
 
 
}
 
}
 
</pre>
 
</pre>
Line 80: Line 290:
 
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.
 
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.
+
== Mathematics ==
  
= Example Xander Robot =
+
=== RCMath ===
As an example of how the Xander framework is used, below is the source for the main robot class of XanderCat 2.0.
+
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.  This package might be best described as an add-on to the framework, and is somewhat less mature than the core framework.
 +
 
 +
== Factor Array Processors ==
 +
 
 +
A ''factor array processor'', defined by the '''FactorArrayProcessor''' interface, is responsible providing factor arrays for waves.  Originally, they were also meant to handle logging wave data, but some of the latest implementations let other classes handle logging data.
 +
 
 +
The '''AbstractFactorArrayProcessor''' provides a basic implementation of this interface designed to work in both guess factor guns and and wave surfing drives.  This is an older implementation that is intended to handle logging data in addition to building factor arrays.
 +
 
 +
== Distributers ==
 +
 
 +
A ''distributer'', defined by the '''WeightDistributer''' interface, is responsible for distributing a set amount of weight into a factor array.
 +
 
 +
Current available distributers include:
 +
*  '''PointDistributer''' - dumps all weight onto a single index.
 +
*  '''TriangleDistributer''' - weight is logged into a triangular region whose base width, by default, is equivalent to the width of the robot.
 +
*  '''WaveDistributer''' - weight is logged across the entire factor range, with weight decreasing exponentially the further it gets from the hit index.
 +
 
 +
== 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
 +
*  '''LateralVelocitySegmenter''' - segments on defender lateral velocity
 +
*  '''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.
 +
*  '''WallStickSegmenter''' - segments on opponent distance to wall in the forward or reverse direction of the defenders current direction.
 +
 
 +
== Utilities ==
 +
 
 +
A '''FactorArrays''' utility class provides static methods for common factor array functions such as converting between factor angles and factor indexes.
 +
 
 +
== Drives ==
 +
 
 +
The '''DirectWaveSurfingDrive''' (named '''DWaveSurfingDrive''' in v9.3 of XanderCat) class provides the basic implementation for a ''direct'' wave surfing drive, relying on a ''direct surf selector'' to choose the factor angle and direct heading to use for each wave.  Surf selectors are defined by the '''DirectSurfSelector''' interface.
 +
 
 +
This drive is capable of surfing 2 waves at once, though only surfs a single wave by default.  It is a Go-To style wave surfing drive, but will reprocess where to go on the same wave under the following situations:
 +
*  when surf wave is updated (such as when a newly fired bullet creates new bullet shadows)
 +
*  when opponent position deviates significantly from prediction.  This is done primarily for the sake of distancing.
 +
 
 +
The xander.gfws package does not provide any surf selector implementations.  However, XanderCat 9.3+ uses a surf selector named '''GreenBeeSurfSelector''' which other surf selectors might be modeled from.  It uses a ''factor array processor'' for managing the drive data, along with a '''DirectDrivePredictor''' and '''DistancingEquation''' from the Xander core classes to aid in choosing what direction to move in.
 +
 
 +
== Guns ==
 +
 
 +
The '''GuessFactorTargeter''' class provides the basic implementation of a guess factor targeter, and utilizes a ''factor array processor'' and ''weight distributer'' for it's operation.
 +
 
 +
= 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.
 +
 
 +
=== GreenBeeSurfSelector ===
 +
This class is a ''direct surf selector'' which can be used in a '''DirectWaveSurfingDrive'''.  It utilizes a ''factor array processor'' for managing data and building a factor array to surf.  It also utilizes the '''DirectDrivePredictor''' and '''DistancingEquation''' classes from the Xander core in it's operation. 
 +
 
 +
The basic steps in it's operation are:
 +
#  Create two sets of test drive angles, one for clockwise, one for counter-clockwise.
 +
#  Determine greatest reachable factors and end positions when driving at each test angle.
 +
#  Reduce the set of test angles to consider based on the distancing provided by the '''DistancingEquation'''.
 +
#  Determine greatest reachable factor range after distancing is applied.
 +
#  If reachable factor range after distancing is applied is too constricted, expand the test range back out until it is not.
 +
#  Use the '''FactorArrayProcessor''' to get the factor array to surf.
 +
#  Choose which factor angle to move to in the surf array.
 +
#  Choose test angle that optimizes distancing while still reaching the chosen factor angle for the wave.
 +
 
 +
=== 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 =
 +
 
 +
== Example Robot Class ==
 +
 
 +
Stripped version of XanderCat main robot class (some content removed to provide simplified example):
 
<pre>
 
<pre>
/**
+
public class XanderCat extends AbstractXanderRobot {
* 1-on-1 robot built on the Xander framework.  Also suitable for melee.
+
 
*
 
* @author Scott Arnold
 
*/
 
public class XanderCat extends AbstractXanderBot {
 
 
 
@Override
 
@Override
protected Level getLogLevel() {
+
protected void style(RobotStyle robotStyle) {
return Log.Level.INFO;
+
robotStyle.setColors(Color.WHITE, Color.BLACK, Color.GREEN);
 
}
 
}
  
 
@Override
 
@Override
protected Radar createRadar() {
+
protected void configure(Configuration configuration) {
return new BasicRadar(this, 45, 5);
+
configuration.setLogComponentRunTimes(true);
 +
configuration.setLogDriveTimes(true);
 
}
 
}
  
 
@Override
 
@Override
protected Drive createDrive() {
+
protected boolean recordBattleStats(Map<String, String> oppStats) {
return new OrbitalDrive(this, 200, 40, 0.5f);
+
GunStats gunStats = Resources.getGunStats();
 +
double oHR = gunStats.getOverallOpponentHitRatio();
 +
double mHR = gunStats.getOverallHitRatio();
 +
oppStats.put("OppHitRatio", Logger.format(oHR,3));
 +
oppStats.put("MyHitRatio", Logger.format(mHR,3));
 +
return true;
 
}
 
}
  
 
@Override
 
@Override
protected Gun createGun() {
+
protected void addComponents(ComponentChain chain) {
FiringProfile firingProfile = new FiringProfile(
+
1.0d, 500d, 3.0d, 150d, 0.5d, 450d, 700d);
+
// RADAR
Segmenter segmenter = new CompoundSegmenter(
+
new BulletTravelTimeSegmenter(getName(), 12, getBattlefieldBounds()),
+
chain.addDefaultComponents(new BasicRadar(45, 5));
new RelativeDirectionSegmenter(10));
+
return new CompoundGun(
+
// DRIVES
new StatGun(this, firingProfile, 1000, 10, 30, segmenter, true),
+
new CircularGun(firingProfile),
+
Path2D.Double driveBounds = DriveBoundsFactory.getSmoothedRectangleBounds(getBattleFieldSize());
new LinearGun(firingProfile));
+
 
}
+
RamFactory.addAntiRamComponents(chain);
 +
 +
MirrorFactory.addAntiMirrorComponents(chain, 12);
  
 +
Scenario ipScenario = new NoOpponentWavesScenario();
 +
Drive ipDrive = new IdealPositionDrive();
 +
chain.addComponents(ipScenario, ipDrive);
 +
 +
...
 +
Drive mainDrive = ...
 +
...
 +
Drive tdDrive = ...
 +
DriveSelector driveSelector = new HighPerformanceDriveSelector(mainDrive, 50, 0.08);
 +
DriveArray driveArray = new DriveArray(driveSelector, mainDrive, tdDrive);
 +
 +
chain.addDefaultComponents(driveArray);
 +
 +
// GUNS 
 +
 +
// a special scenario just for our circular drivers out there!
 +
XanderGun circularGun = new XanderGun(new CircularTargeter(), mainPowerSelector);
 +
circularDriverScenario = new CircularDriveScenario(circularGun);
 +
chain.addComponents(circularDriverScenario, circularGun);
 +
 +
// main guess factor guns
 +
...
 +
Gun[] guns = new Gun[2];
 +
...
 +
VirtualHitRatioGunSelector gunSelector = new VirtualHitRatioGunSelector();
 +
gunSelector.setRollingRatioWeight(0.25);
 +
GunArray gunArray = new GunArray(gunSelector, guns);
 +
 +
chain.addDefaultComponents(gunArray);
 +
}
 
}
 
}
 
</pre>
 
</pre>
 +
 
= Handling Static Data =
 
= 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.
+
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 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 ===
 
<pre>
 
StaticResourceManager srm = StaticResourceManager.getInstance(robotName);
 
srm.register(myResource);
 
</pre>
 
=== Looking Up a Resource ===
 
<pre>
 
StaticResourceManager srm = StaticResourceManager.getInstance(robotName);
 
MyResource myResource = srm.getResource(MyResource.class);
 
</pre>
 
== 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 ===
 
<pre>
 
StaticResourceManager srm = StaticResourceManager.getInstance(robotName);
 
srm.store("myKey",myProperty);
 
</pre>
 
=== Looking Up a Property ===
 
<pre>
 
StaticResourceManager srm = StaticResourceManager.getInstance(robotName);
 
MyProperty myProperty = srm.getProperty("myKey");
 
</pre>
 
= 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.
 
  
Each robot is an own instance. Otherwise you could not have a battle between robot ''1'' and robot ''1''. If you would package a team (f.e. [[TwinDuel]] and use both robot 1 and robot 2, I can't predict what would happen. Probably only one version of the framework would be packaged. --[[User:GrubbmGait|GrubbmGait]] 17:10, 27 May 2011 (UTC)
+
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.

Latest revision as of 18:01, 16 April 2017

Xander Framework

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. As an optional add-on to the framework, some more advanced yet less mature guess factor and wave surfing classes are in package xander.gfws. When looking at any of my Xander 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.

With a Xander robot, the main robot class need only set up a component chain populated with your guns, radars, and drives. It can also optionally override methods to change basic configuration options and write battle statistics to disk.

Guns, radars, and drives must use the corresponding framework interfaces and can make use of a variety of framework resources and events.

The current framework version available for the last few years is considered v2.0. Version 3.0 is current in development. Version 3.0 brings several advancements over the older version through a revised component structure, including:

  • Full melee support.
  • Simplified components, but without losing any prior functionality.
  • Reduced code size.
  • Improved performance.

Version Notes

Some interesting notes about each version of the framework.

Version 1.0

Version 1.0 had the basics complete, but lacked some of the more advanced functionality like tracking wave history.

Version 2.0

Version 2.0 added more advanced functionality like wave history and has been the main version from about 2013 up through 2017. Version 2.0 was tailored primarily for 1 vs 1 combat, and had certain deficiencies when attempting to use it for other purposes. It was structured to have 1 of each framework component, and only some of them were written to accommodate more than 1 opponent on the battlefield.

Version 3.0

Version 3.0 is under development in 2017. It's purpose is to add full melee or multi-opponent support, simplify some of the code, reduce code size, and improve performance in a few areas. Version 3.0 takes on a new component strategy where some components that can apply to a single robot now only apply to a single robot. This means some main framework components will now have 1 instance for each known robot, instead of a single instance that must handle all robots. One of the challenges with this approach is that robot specific opponents are now generated as knowledge of their existence is gained, instead of existing from the very beginning of the battles. This is a challenge because you can no longer always fully construct your robot at the very beginning, because some parts must be created or expanded as new robots are scanned. The framework is being rewritten to support this in as convenient a fashion as possible.

The main framework components that will now have 1 for every robot (including self) include the SnapshotHistory, WaveHistory, and GunStats components.

Any listeners for robot specific components (anything that implements WaveListener, ScannedRobotListener, etc) must now implement an additional method getOpponentFocus() that tells the framework what robots to associate the component with. Focus options include:

  • ALL_ROBOTS: Component will receive events for all robots.
  • TARGETED_ROBOT: Component will only receive events from the currently targeted robot (the targeted robot determined by what your radar is returning).
  • SPECIFIC_ROBOT_SCANNED: Component will only receive events for one specific robot. That robot will be the last scanned robot at time listener is added, or the next scanned robot if there is no last scanned robot.
  • SPECIFIC_ROBOT_TARGETED: Component will only receive events for one specific robot. That robot will be the last targeted robot at time listener is added, or the next targeted robot if there is no current target.

Base Robot Class

Xander robots extend the AbstractXanderRobot class, which in turn extends the Robocode AdvancedRobot 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:
    • call configure(Configuration)
    • call style(RobotStyle)
    • initialize Resources
    • call addComponents(ComponentChain)
  • apply robot styling (done every round in case win code changes styling)
  • 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(ComponentChain) - 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
  • boolean recordBattleStats(Map<String, String>) - allows robot to save battle statistics to file; key is statistic name, value is statistic value as String. Statistics are saved in the BattleStats class which is written to file as a Java object using an ObjectOutputStream.

Components / Component Chain

In a Xander robot, guns, drives, and radars are referred to as components. Components are managed within a component chain represented by the ComponentChain class. 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(ComponentChain)

. 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:

protected void addComponents(ComponentChain chain) {
	...
	chain.addComponents(ramScenario, ramDrive, ramGun);
	chain.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 of your robot, such as drive time usage.

GunStats

Statistics related to the guns, including hit ratios and virtual gun hit ratios. As of v3.0, one GunStats will be created for each robot in the battle.

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. As of v3.0, One SnapshotHistory will be created for each robot in the battle.

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. As of v3.0, one WaveHistory is created for each robot.

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.

public interface Radar extends Component {

	/**
	 * Prepare next sweep of radar and return snapshot of robot currently
	 * being focused upon.
	 * 
	 * @param radarController  radar controller
	 * 
	 * @return     snapshot of robot to attack
	 */
	public Snapshot search(RadarController radarController);
}

Drive

Drives must implement the Drive interface.

public interface Drive extends Component {
	
	/**
	 * Drive into optimal firing range of the given robot.
	 * 
	 * @param opponentSnapshot   robot to drive against
	 * @param driveController    drive controller
	 */
	public void driveTo(Snapshot opponentSnapshot, DriveController driveController);
	
	/**
	 * Drive without any specific target.
	 * 
	 * @param driveController    drive controller
	 */
	public void drive(DriveController driveController);
}

Drive Array

In cases were the robot should choose between a variety of drives based on some common criteria, a drive array can be set up using the DriveArray class. A drive array must have a drive selector, defined by the DriveSelector 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.
public interface Gun extends Component {
	
	/**
	 * Aim and fire upon the given opponent.  Since guns are also required
	 * to have the getFiringVector method, this method can generally call that 
	 * method to get the aim.  The only extra work in this method is 
	 * actually turning the gun and firing.
	 * 
	 * @param target           snapshot of opponent
	 * @param myself           snapshot of self
	 * @param gunController    gun controller
	 * 
	 * @return          whether or not a bullet was fired
	 */
	public boolean fireAt(Snapshot target, Snapshot myself, GunController gunController);

	/**
	 * Returns the aim information for the given opponent.  
	 * 
	 * @param target    the opponent to target
	 * @param myself    snapshot of self
	 * 
	 * @return          aim information for the given opponent
	 */
	public Aim getAim(Snapshot target, Snapshot myself);
	
	/**
	 * Returns whether or not this can is currently capable of aiming and 
	 * firing on the given robot.  This method should be lightweight (most
	 * guns should just return true).
	 * 
	 * @param target           snapshot of robot to fire at
	 * 
	 * @return                 whether or not gun can fire on opponent
	 */
	public boolean canFireAt(Snapshot target);
}

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 accessed through the RobotEvents class (v3.0):

Listener Class Opponent Focusable? Purpose
BulletHitListener Yes Repackages Robocode events onHitByBullet, onBulletHit, onBulletMissed, and onBulletHitBullet
CollisionListener No Repackages Robocode events onHitRobot and onHitWall
WaveListener Yes Handles events related to a robot's bullet waves. The WaveHistory stores two sets of WaveListeners, one for actual bullet waves, and one for virtual bullet waves. If listening for both, you can tell the difference between the two by the Wave method isVirtual().
Painter No Repackages Robocode event onPaint, allowing other components to paint on the battlefield
RoundBeginListener No Handler for a single event that fires at the beginning of every round. Can be used to initialize variables when a round begins.
RoundListener No Repackages Robocode events onRoundEnd and onBattleEnd
ScannedRobotListener Yes Repackages Robocode event onScannedRobot
SurvivalListener Yes Repackages Robocode events onWin, onDeath, and onRobotDeath
TurnListener No 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. This package might be best described as an add-on to the framework, and is somewhat less mature than the core framework.

Factor Array Processors

A factor array processor, defined by the FactorArrayProcessor interface, is responsible providing factor arrays for waves. Originally, they were also meant to handle logging wave data, but some of the latest implementations let other classes handle logging data.

The AbstractFactorArrayProcessor provides a basic implementation of this interface designed to work in both guess factor guns and and wave surfing drives. This is an older implementation that is intended to handle logging data in addition to building factor arrays.

Distributers

A distributer, defined by the WeightDistributer interface, is responsible for distributing a set amount of weight into a factor array.

Current available distributers include:

  • PointDistributer - dumps all weight onto a single index.
  • TriangleDistributer - weight is logged into a triangular region whose base width, by default, is equivalent to the width of the robot.
  • WaveDistributer - weight is logged across the entire factor range, with weight decreasing exponentially the further it gets from the hit index.

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
  • LateralVelocitySegmenter - segments on defender lateral velocity
  • 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.
  • WallStickSegmenter - segments on opponent distance to wall in the forward or reverse direction of the defenders current direction.

Utilities

A FactorArrays utility class provides static methods for common factor array functions such as converting between factor angles and factor indexes.

Drives

The DirectWaveSurfingDrive (named DWaveSurfingDrive in v9.3 of XanderCat) class provides the basic implementation for a direct wave surfing drive, relying on a direct surf selector to choose the factor angle and direct heading to use for each wave. Surf selectors are defined by the DirectSurfSelector interface.

This drive is capable of surfing 2 waves at once, though only surfs a single wave by default. It is a Go-To style wave surfing drive, but will reprocess where to go on the same wave under the following situations:

  • when surf wave is updated (such as when a newly fired bullet creates new bullet shadows)
  • when opponent position deviates significantly from prediction. This is done primarily for the sake of distancing.

The xander.gfws package does not provide any surf selector implementations. However, XanderCat 9.3+ uses a surf selector named GreenBeeSurfSelector which other surf selectors might be modeled from. It uses a factor array processor for managing the drive data, along with a DirectDrivePredictor and DistancingEquation from the Xander core classes to aid in choosing what direction to move in.

Guns

The GuessFactorTargeter class provides the basic implementation of a guess factor targeter, and utilizes a factor array processor and weight distributer for it's operation.

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.

GreenBeeSurfSelector

This class is a direct surf selector which can be used in a DirectWaveSurfingDrive. It utilizes a factor array processor for managing data and building a factor array to surf. It also utilizes the DirectDrivePredictor and DistancingEquation classes from the Xander core in it's operation.

The basic steps in it's operation are:

  1. Create two sets of test drive angles, one for clockwise, one for counter-clockwise.
  2. Determine greatest reachable factors and end positions when driving at each test angle.
  3. Reduce the set of test angles to consider based on the distancing provided by the DistancingEquation.
  4. Determine greatest reachable factor range after distancing is applied.
  5. If reachable factor range after distancing is applied is too constricted, expand the test range back out until it is not.
  6. Use the FactorArrayProcessor to get the factor array to surf.
  7. Choose which factor angle to move to in the surf array.
  8. Choose test angle that optimizes distancing while still reaching the chosen factor angle for the wave.

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

Example Robot Class

Stripped version of XanderCat main robot class (some content removed to provide simplified example):

public class XanderCat extends AbstractXanderRobot {

	@Override
	protected void style(RobotStyle robotStyle) {
		robotStyle.setColors(Color.WHITE, Color.BLACK, Color.GREEN);
	}

	@Override
	protected void configure(Configuration configuration) {
		configuration.setLogComponentRunTimes(true);
		configuration.setLogDriveTimes(true);
	}

	@Override
	protected boolean recordBattleStats(Map<String, String> oppStats) {
		GunStats gunStats = Resources.getGunStats();
		double oHR = gunStats.getOverallOpponentHitRatio();
		double mHR = gunStats.getOverallHitRatio();
		oppStats.put("OppHitRatio", Logger.format(oHR,3));
		oppStats.put("MyHitRatio", Logger.format(mHR,3));
		return true;
	}

	@Override
	protected void addComponents(ComponentChain chain) {
				
		// RADAR
		
		chain.addDefaultComponents(new BasicRadar(45, 5));
		
		// DRIVES
		
		Path2D.Double driveBounds = DriveBoundsFactory.getSmoothedRectangleBounds(getBattleFieldSize());

		RamFactory.addAntiRamComponents(chain);
		
		MirrorFactory.addAntiMirrorComponents(chain, 12);

		Scenario ipScenario = new NoOpponentWavesScenario();
		Drive ipDrive = new IdealPositionDrive();
		chain.addComponents(ipScenario, ipDrive);
		
		...
		Drive mainDrive = ...
		...
		Drive tdDrive = ...		
		DriveSelector driveSelector = new HighPerformanceDriveSelector(mainDrive, 50, 0.08);
		DriveArray driveArray = new DriveArray(driveSelector, mainDrive, tdDrive);		
						
		chain.addDefaultComponents(driveArray);
		
		// GUNS  
		
		// a special scenario just for our circular drivers out there!
		XanderGun circularGun = new XanderGun(new CircularTargeter(), mainPowerSelector);
		circularDriverScenario = new CircularDriveScenario(circularGun);
		chain.addComponents(circularDriverScenario, circularGun);
		
		// main guess factor guns
		...
		Gun[] guns = new Gun[2];
		...
		VirtualHitRatioGunSelector gunSelector = new VirtualHitRatioGunSelector();
		gunSelector.setRollingRatioWeight(0.25);
		GunArray gunArray = new GunArray(gunSelector, guns);
				
		chain.addDefaultComponents(gunArray);
	}	
}

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.