... or how I applied the Strategy (315) pattern to Robocode.
download source code for this article (3Kb)
Taking the event based methods of AdvancedRobot into a Strategy interface
public interface Strategy {
public void preRun();
public void run();
public void onBulletHit(BulletHitEvent event);
public void onBulletHitBullet(BulletHitBulletEvent event);
public void onBulletMissed(BulletMissedEvent event);
public void onCustomEvent(CustomEvent e);
public void onDeath(DeathEvent event);
public void onHitByBullet(HitByBulletEvent event);
public void onHitRobot(HitRobotEvent event);
public void onHitWall(HitWallEvent event);
public void onRobotDeath(RobotDeathEvent event);
public void onScannedRobot(ScannedRobotEvent event);
public void onWin(WinEvent event);
}
|
Then providing a simple adapter with a default implementation of each event method
public class StrategyAdapter implements Strategy {
public void preRun() {}
public void run() {}
public void onBulletHit(BulletHitEvent event) {}
public void onBulletHitBullet(BulletHitBulletEvent event) {}
public void onBulletMissed(BulletMissedEvent event) {}
public void onCustomEvent(CustomEvent e) {}
public void onDeath(DeathEvent event) {}
public void onHitByBullet(HitByBulletEvent event) {}
public void onHitRobot(HitRobotEvent event) {}
public void onHitWall(HitWallEvent event) {}
public void onRobotDeath(RobotDeathEvent event) {}
public void onScannedRobot(ScannedRobotEvent event) {}
public void onWin(WinEvent event) {}
}
|
All this means that I could take an existing robot, and turn him into a strategic advisor. In the example below the sample WallRobot has been adapted into a strategist, simply by passing in a reference to the real robot that is in the arena, the WallStrategy can advise what action to take on any of the events it cares to override.
So this means coding a strategy is just like coding a real robot, but you call i.turnLeft() instead of the implied super.turnLeft().
public class WallStrategy extends StrategyAdapter {
private Robot i;
private boolean peek;
private double moveAmount;
public WallStrategy(Robot robot) {
this.i = robot;
}
public void preRun() {
moveAmount = Math.max( i.getBattleFieldWidth(), i.getBattleFieldHeight());
peek = false;
i.turnLeft(i.getHeading() % 90);
i.ahead(moveAmount);
peek = true;
i.turnGunRight(90);
i.turnRight(90);
}
public void run() {
peek = true;
i.ahead(moveAmount);
peek = false;
i.turnRight(90);
}
public void onHitRobot(HitRobotEvent e) {
if (e.getBearing() > -90 && e.getBearing() < 90) {
i.back(100);
} else {
i.ahead(100);
}
}
public void onScannedRobot(ScannedRobotEvent e) {
i.fire(2);
if (peek) {
i.scan();
}
}
}
|
All of this comes together in my robot LNE (Lenny), which uses the strategy above, but more importantly, when Lenny realises that the strategy is not working, he automatically changes strategy. (I think Lenny could grow into something beautiful...)
public class LNE extends AdvancedRobot implements Strategy {
protected int current = 0;
protected Strategy[] strategy = new Strategy[] {
new TagAlongStrategy(this),
new TrackFireStrategy(this),
};
public void run() {
while (true) {
int currentAtStartOfOuterWhileLoop = current;
strategy[current].preRun();
while (current == currentAtStartOfOuterWhileLoop) {
strategy[current].run();
}
}
}
public void onBulletHit(BulletHitEvent e) {
strategy[current].onBulletHit(e);
}
...
public void onHitByBullet(HitByBulletEvent e) {
strategy[current].onHitByBullet(e);
changeStrategy();
}
public void onWin(WinEvent e) {
turnGunRight(36000);
}
private void changeStrategy() {
clearAllEvents();
current++;
if (current == strategy.length) {
current = 0;
}
}
}
|
Just a shame it didn't win :-)
|